Skip to content
This repository was archived by the owner on Jul 10, 2023. It is now read-only.

Commit e6aac66

Browse files
committed
Properly support Windows
We use HeapSize with GetProcessHeap() and flags 0, like Rust itself.
1 parent 7913872 commit e6aac66

File tree

3 files changed

+82
-21
lines changed

3 files changed

+82
-21
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ description = "Infrastructure for measuring the total runtime size of an object
66
license = "MPL-2.0"
77
repository = "https://github.com/servo/heapsize"
88

9+
[dependencies]
10+
kernel32-sys = "0.2.1"
11+
912
[features]
1013
unstable = []

src/lib.rs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
//! Data structure measurement.
66
7+
#[cfg(target_os = "windows")]
8+
extern crate kernel32;
9+
10+
#[cfg(target_os = "windows")]
11+
use kernel32::{GetProcessHeap, HeapSize, HeapValidate};
712
use std::borrow::Cow;
813
use std::cell::{Cell, RefCell};
914
use std::collections::{BTreeMap, HashMap, LinkedList, VecDeque};
@@ -18,38 +23,42 @@ use std::sync::Arc;
1823
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize};
1924
use std::rc::Rc;
2025

21-
#[cfg(not(target_os = "windows"))]
22-
extern {
23-
// Get the size of a heap block.
24-
//
25-
// Ideally Rust would expose a function like this in std::rt::heap, which would avoid the
26-
// jemalloc dependence.
27-
//
28-
// The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some
29-
// platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice
30-
// this function doesn't modify the contents of the block that `ptr` points to, so we use
31-
// `*const c_void` here.
32-
fn je_malloc_usable_size(ptr: *const c_void) -> usize;
33-
}
34-
35-
/// A wrapper for je_malloc_usable_size that handles `EMPTY` and returns `usize`.
26+
/// Get the size of a heap block.
27+
///
28+
/// Ideally Rust would expose a function like this in std::rt::heap.
3629
///
3730
/// `unsafe` because the caller must ensure that the pointer is from jemalloc.
3831
/// FIXME: This probably interacts badly with custom allocators:
3932
/// https://doc.rust-lang.org/book/custom-allocators.html
40-
#[cfg(not(target_os = "windows"))]
4133
pub unsafe fn heap_size_of(ptr: *const c_void) -> usize {
4234
if ptr == 0x01 as *const c_void {
4335
0
4436
} else {
45-
je_malloc_usable_size(ptr)
37+
heap_size_of_impl(ptr)
4638
}
4739
}
4840

49-
/// FIXME: Need to implement heap size support on Windows.
41+
#[cfg(not(target_os = "windows"))]
42+
unsafe fn heap_size_of_impl(ptr: *const c_void) -> usize {
43+
// The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some
44+
// platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice
45+
// this function doesn't modify the contents of the block that `ptr` points to, so we use
46+
// `*const c_void` here.
47+
extern "C" {
48+
fn je_malloc_usable_size(ptr: *const c_void) -> usize;
49+
}
50+
je_malloc_usable_size(ptr)
51+
}
52+
5053
#[cfg(target_os = "windows")]
51-
pub unsafe fn heap_size_of(ptr: *const c_void) -> usize {
52-
0
54+
pub unsafe fn heap_size_of_impl(mut ptr: *const c_void) -> usize {
55+
let heap = GetProcessHeap();
56+
57+
if HeapValidate(heap, 0, ptr) == 0 {
58+
ptr = *(ptr as *const *const c_void).offset(-1);
59+
}
60+
61+
HeapSize(heap, 0, ptr) as usize
5362
}
5463

5564
// The simplest trait for measuring the size of heap data structures. More complex traits that

tests/tests.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
#![cfg_attr(feature= "unstable", feature(alloc, heap_api))]
5+
#![cfg_attr(feature= "unstable", feature(alloc, heap_api, repr_simd))]
66

77
extern crate heapsize;
88

@@ -18,11 +18,15 @@ mod unstable {
1818
use heapsize::heap_size_of;
1919
use std::os::raw::c_void;
2020

21+
#[repr(C, simd)]
22+
struct OverAligned(u64, u64, u64, u64);
23+
2124
#[test]
2225
fn check_empty() {
2326
assert_eq!(::EMPTY, alloc::heap::EMPTY);
2427
}
2528

29+
#[cfg(not(target_os = "windows"))]
2630
#[test]
2731
fn test_alloc() {
2832
unsafe {
@@ -40,8 +44,53 @@ mod unstable {
4044
let x = alloc::heap::allocate(1024 * 1024, 0);
4145
assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024);
4246
alloc::heap::deallocate(x, 1024 * 1024, 0);
47+
48+
// An overaligned 1MiB request is allocated exactly.
49+
let x = alloc::heap::allocate(1024 * 1024, 32);
50+
assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024);
51+
alloc::heap::deallocate(x, 1024 * 1024, 32);
52+
}
53+
}
54+
55+
#[cfg(target_os = "windows")]
56+
#[test]
57+
fn test_alloc() {
58+
unsafe {
59+
// A 64 byte request is allocated exactly.
60+
let x = alloc::heap::allocate(64, 0);
61+
assert_eq!(heap_size_of(x as *const c_void), 64);
62+
alloc::heap::deallocate(x, 64, 0);
63+
64+
// A 255 byte request is allocated exactly.
65+
let x = alloc::heap::allocate(255, 0);
66+
assert_eq!(heap_size_of(x as *const c_void), 255);
67+
alloc::heap::deallocate(x, 255, 0);
68+
69+
// A 1MiB request is allocated exactly.
70+
let x = alloc::heap::allocate(1024 * 1024, 0);
71+
assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024);
72+
alloc::heap::deallocate(x, 1024 * 1024, 0);
73+
74+
// An overaligned 1MiB request is over-allocated.
75+
let x = alloc::heap::allocate(1024 * 1024, 32);
76+
assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024 + 32);
77+
alloc::heap::deallocate(x, 1024 * 1024, 32);
4378
}
4479
}
80+
81+
#[cfg(not(target_os = "windows"))]
82+
#[test]
83+
fn test_simd() {
84+
let x = Box::new(OverAligned(0, 0, 0, 0));
85+
assert_eq!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32);
86+
}
87+
88+
#[cfg(target_os = "windows")]
89+
#[test]
90+
fn test_simd() {
91+
let x = Box::new(OverAligned(0, 0, 0, 0));
92+
assert_eq!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32 + 32);
93+
}
4594
}
4695

4796
#[test]

0 commit comments

Comments
 (0)