Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdd support for the wrapping of heap allocation and deallocation functions #916
Comments
This comment has been minimized.
This comment has been minimized.
|
Someone was asking for this on IRC yesterday.
|
This comment has been minimized.
This comment has been minimized.
|
@nnethercote @nikomatsakis is there any reason we would need to address this before 1.0? AFAICT we can add support for this backwards compatibly. (I agree that something like this is desirable. I just want to know if it something that we need to rush to work on, or if we can wait until the 1.0-related distractions are behind us...) |
This comment has been minimized.
This comment has been minimized.
|
I'm looking at this stuff now because I have a Q1 goal about fine-grained memory profiling in Servo. Currently the only change to Rust is a single new function in So there's no particular reason this has to be pre-1.0, as far as I can tell. It would be helpful to me to have feedback on whether any particular approach is likely to be accepted, though, so I don't waste time going down blind alleys. |
This comment has been minimized.
This comment has been minimized.
|
So it looks like this functionality is already present, but it's really hard to use -- you have to recompile How hard would it be to improve the ergonomics here? Would it be possible to build two versions of I'm not sure what to do about allocating/deallocating from within @scialex added this feature, perhaps he/she could comment. |
This comment has been minimized.
This comment has been minimized.
|
@nnethercote By the way, thank you for filling in the details here. In particular, knowing that this is a goal for Servo means that we could provide an unstable API for your use. I think that will be much easier for all of us to swallow (though of course there is still the question of whether we should put this in as a global default for all clients of |
This comment has been minimized.
This comment has been minimized.
And it looks like |
This comment has been minimized.
This comment has been minimized.
Are you saying that one is forced to implement such functions in C and not Rust? (I do not see why that would be the case.) Or are you saying that one must write |
This comment has been minimized.
This comment has been minimized.
I was saying that, but I was mistaken. Thanks for clarifying. As implemented, the feature is still very difficult to use. I don't want rustc itself to use the |
This comment has been minimized.
This comment has been minimized.
|
@nnethercote I agree that the current workflow is too awkward. I want to look over your code again and review some other material related to allocators; hopefully we can put something together for you soon. (Again: Its a big help that this need not be a stable API.) |
This comment has been minimized.
This comment has been minimized.
|
@pnkfelix: I'm not sure it even needs an API within the language. I guess it depends if the |
This comment has been minimized.
This comment has been minimized.
|
I think this is very related to the desire to have executables be able |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis: Thanks for the link. I had only thought about trying to wrap jemalloc, not system malloc. I've added instrumentation to Servo to measure both, on Linux at least (jemalloc via I agree that if we have a world where there are multiple versions of liballoc, that is definitely likely to make it easier to facilitate wrapping. E.g. you might have a malloc version, a jemalloc version, and a wrapped-jemalloc version. Or something. BTW, it might be useful for you to talk to Mike Hommey (a.k.a. glandium). He's the expert on how Firefox uses jemalloc, including all the tricks necessary on all major platforms. Indeed, just a couple of weeks ago he fixed a bug that was causing some libraries used by Firefox to (unintentionally) use system malloc. |
This comment has been minimized.
This comment has been minimized.
|
On Sat, Mar 07, 2015 at 03:53:18PM -0800, Nicholas Nethercote wrote:
I'm not sure I follow. The goal is to have only one allocator, and to allow that to be chosen when linking the executable, which seems like what you want?
thanks for the suggestion! |
This comment has been minimized.
This comment has been minimized.
|
We have now provided this, via RFC 1183, which describes the capability to swap in a different low-level default allocator. Closing. |
nnethercote commentedFeb 27, 2015
(An earlier version of this proposal was posted at http://internals.rust-lang.org/t/add-support-for-the-wrapping-of-heap-allocation-and-deallocation-functions/1618/6, where it didn't get much attention.)
It can be very useful for user programs to be able to take arbitrary actions when when heap allocation and deallocation occurs.
My motivation is that I want to build a heap profiler for Servo, one that's similar to Firefox's DMD. This profiler would record a stack trace on each allocation, and use that to emit data about which parts of the code are performing many allocations (contributing to heap churn) and which ones are responsible for allocating live blocks (contributing to peak memory usage). This may sound like a generic tool that could be built into Rust itself, but it's likely to end up with Servo-specific parts, so flexible building blocks would be better than a canned solution.
There are lots of other potential uses for this, and this kind of facility is common in other systems. E.g. glibc provides one for malloc/realloc/free, and there's also the more general
LD_PRELOAD(on Linux) andDYLD_INSERT_LIBRARIES(on Mac) which allow you to hook any library functions.What this needs.
I have a basic, ugly, proof-of-concept implementation. It adds the following code to
src/liballoc/heap.rs, which defines a struct for holding the wrapper function and a function for setting the wrapper. (I've only shown the code forallocate; the other functions are handled similarly.)It also modifies allocate like so:
In the normal case this adds a single, perfectly-predictable branch to the alloc/dealloc path, which is hopefully small in relation to the cost of an alloc/dealloc.
And here is a sample program that uses it.
Like I said, it's ugly.
set_allocation_hooksshould be called as early as possible, so that it doesn't miss any allocations. It's also entirely non-thread-safe, which is probably ok if you do call it right at the start ofmain, but is still nasty. Putting the wrappers table inside anRwLockor something might be possible but it would be a shame, performance-wise, to do that for a data structure that's written once and then read zillions of times. I figure there must be a more Rust-y way of doing this. Could a#[feature("...")]annotation work? We really want these wrappers to be enabled the language runtime at start-up, rather than the program itself having to do it.It was really just a prototype to see if I could get something working. And it does.
So... I'm wondering if (a) a feature like this seems like something that might be accepted into Rust, and (b) if there are better ways of implementing it. I hope so, because my ideas for measuring Servo's memory usage are dead in the water without this.
This idea has a small amount of overlap with this old RFC -- I want a custom allocator for the entire program, basically -- but is much smaller.
Thank you for reading.