Labs/Jetpack/Memory Profiling Notes from July 2009
This wiki page is a supplement to Atul's blog post entitled Fun with SpiderMonkey.
In attempt to help the Developer Tools Lab build a memory profiling tool, I created a binary component in Jetpack. I figured Jetpack would be the best place to put it for now because (A) Jetpack has a binary component in development and (B) memory profiling is something that Jetpacks need too.
The idea of the memory profiling component is to freeze the host JS runtime (i.e., Firefox's JS runtime) and start another JS runtime for the express purpose of memory profiling. The memory profiling JS runtime has a handful of native functions that can be used to introspect the heap of the host JS runtime; objects in the host JS runtime are referred to by a numeric id. A blocking socket object (implemented using NSPR) is also exposed to the memory profiling JS runtime, allowing the memory profiling code to start a web service that a front-end can communicate with.
The reason a memory profiling JS runtime was created rather than doing everything in C++ was simply because we didn't know the problem domain well enough, and exploring it using solely C++ would've been a hassle. The theory was that once we figured out exactly what we needed, we could optimize anything that ran too slowly in C++.
At the time of this writing, there's a number of outstanding issues with the memory profiling code:
- The host application may have threads that continue to execute while the memory profiling code is running; some of these threads may be running JS code that is associated with the host JS runtime that's being profiled. This needs to be resolved, though it doesn't really appear to be causing any problems in the general case (yet). I talked with Blake Kaplan about this a few days ago and it appears that some changes may actually need to be made to SpiderMonkey in order to really do this (at the very least,
JS_TraceRuntime()appears to not be threadsafe, and it needs to be).
- Actually binning items by their class type—not their
JSClasstype, but their prototype in JS—appears to be difficult. Dion figured out a way to get to it by looking at the
prototypeproperty of an object (
JS_GetPrototypedoesn't do the trick, sadly), yet getting this property is hard to do while guaranteeing that no JS code will execute as a result of getting the property. Indeed, enumerating through properties using
JS_NewPropertyIterator()and accessing them through
JS_LookupProperty()while freezing the JS heap seems difficult to do, and is fraught with strange assertions from XPConnect code.
- One potential way to resolve this is by using the jsdIDebuggerService to trap whenever new objects are created and generate metadata about each new object that's created. However, this means enabling the profiling code and thus slowing down Firefox while the profiling code is running; there would be no way, for instance, for a user to use Firefox at full speed and then freeze Firefox to examine its heap whenever they felt the need (e.g., when Firefox seems to be using lots of memory).
- Because the host JS runtime needs to be "frozen solid" while the memory profiling code runs, the Mozilla platform appears to be "locked up" while it's running. In the case where the memory profiling code actually sets up a web service, the host application appears to be locked up until the web service is given a command to stop waiting for incoming requests. This issue isn't too bad as long as the user knows what's going to happen beforehand.
The C++ source code for the memory profiling is here:
The test suite is here:
The above test suite actually contains tests for lots of various functionality, but near the end you'll find the memory profiling tests.
The sample memory profiling web service is here: