Confirmed users
328
edits
(Created page with "== Goal == The goal of this project is to know the exact locations of all GC Cell pointers stored on the stack. This will enable further development of compacting and generatio...") |
No edit summary |
||
Line 5: | Line 5: | ||
== Dynamic Rooting Analysis == | == Dynamic Rooting Analysis == | ||
Since a failure to root a stack variable properly will become a use-after-free error after we have moving GC's, it is vitally important that we catch and fix all rooting violations now, rather than later. To this end, we have enabled a dynamic correctness checker that runs on every push to mozilla-inbound. It verifies that no stack pointers are left unrooted. | Since a failure to root a stack variable properly will become a use-after-free error after we have moving GC's, it is vitally important that we catch and fix all rooting violations now, rather than later. To this end, we have enabled a dynamic correctness checker that runs on every push to mozilla-inbound. It verifies that no stack pointers are left unrooted. It is displayed on tbpl under the name SM(r). | ||
== | == Static Rooting Analysis == | ||
Tbpl can run two static analysis builds, one for the full browser (linux64-br-haz) and one for just the JS shell (linux64-sh-haz). | |||
These builds are performed as follows: | |||
* run the mozharness script http://hg.mozilla.org/build/mozharness/scripts/spidermonkey_build.py, which sets up a mozilla_mock environment and runs the analysis within it, then uploads the resulting files | |||
** compile an optimized JS shell to later run the analysis | |||
** compile the browser with gcc, using a slightly modified version of the sixgill (http://svn.sixgill.org) gcc plugin, producing a set of .xdb files describing everything encountered during the compilation | |||
** analyze the .xdb files with scripts in js/src/devtools/rootAnalysis | |||
=== Running the analysis === | |||
Theoretically, you could run the analysis yourself by checking out mozharness and running | |||
python mozharness/scripts/spidermonkey_build.py -c hazards/common.py -c hazards/build_browser.py -c users/sfink/spidermonkey.py | |||
but you'll need a bunch of dependencies that aren't bundled up in any convenient way, and you'll need to edit that last config file for your environment. | |||
The far easier way to run an analysis is to push to try with the trychooser line |try: -b do -p linux64-br-haz -u none| (or, if the hazards of interest are contained entirely within js/src, use |try: -b do -p linux64-sh-haz -u none| for a much faster result). The expected turnaround time for linux64-br-haz is just under 2 hours. | |||
The output will be uploaded to a path similar to http://stage.mozilla.org/pub/mozilla.org/firefox/try-builds/sfink@mozilla.com-a5d4f2abfda378eb57e4b6bbc5bb9d5186e0d62d/try-linux64-br-haz/ . At the moment, you'll need to dig that path out of the log file. (I will be adding it to the summary information displayed on tbpl "soon".) | |||
=== Analysis output === | |||
The main output file of interest is hazards.txt. Example snippet: | |||
<pre> | |||
Function 'jsopcode.cpp:uint8 DecompileExpressionFromStack(JSContext*, int32, int32, class JS::Handle<JS::Value>, int8**)' has unrooted 'ed' of type 'ExpressionDecompiler' live across GC call 'uint8 ExpressionDecompiler::decompilePC(uint8*)' at js/src/jsopcode.cpp:1866 | |||
js/src/jsopcode.cpp:1866: Assume(74,75, !__temp_23*, true) | |||
js/src/jsopcode.cpp:1867: Assign(75,76, return := 0) | |||
js/src/jsopcode.cpp:1867: Call(76,77, ed.~ExpressionDecompiler()) | |||
GC Function: uint8 ExpressionDecompiler::decompilePC(uint8*) | |||
JSString* js::ValueToSource(JSContext*, class JS::Handle<JS::Value>) | |||
uint8 js::Invoke(JSContext*, JS::Value*, JS::Value*, uint32, JS::Value*, class JS::MutableHandle<JS::Value>) | |||
uint8 js::Invoke(JSContext*, JS::CallArgs, uint32) | |||
JSScript* JSFunction::getOrCreateScript(JSContext*) | |||
uint8 JSFunction::createScriptForLazilyInterpretedFunction(JSContext*, class JS::Handle<JSFunction*>) | |||
uint8 JSRuntime::cloneSelfHostedFunctionScript(JSContext*, class JS::Handle<js::PropertyName*>, class JS::Handle<JSFunction*>) | |||
JSScript* js::CloneScript(JSContext*, class JS::Handle<JSObject*>, class JS::Handle<JSFunction*>, const class JS::Handle<JSScript*>, uint32) | |||
JSObject* js::CloneStaticBlockObject(JSContext*, class JS::Handle<JSObject*>, class JS::Handle<js::StaticBlockObject*>) | |||
js::StaticBlockObject* js::StaticBlockObject::create(js::ExclusiveContext*) | |||
js::Shape* js::EmptyShape::getInitialShape(js::ExclusiveContext*, js::Class*, js::TaggedProto, JSObject*, JSObject*, uint32, uint32) | |||
js::Shape* js::EmptyShape::getInitialShape(js::ExclusiveContext*, js::Class*, js::TaggedProto, JSObject*, JSObject*, uint64, uint32) | |||
js::UnownedBaseShape* js::BaseShape::getUnowned(js::ExclusiveContext*, js::StackBaseShape*) | |||
js::BaseShape* js_NewGCBaseShape(js::ThreadSafeContext*) [with js::AllowGC allowGC = (js::AllowGC)1u] | |||
js::BaseShape* js::gc::NewGCThing(js::ThreadSafeContext*, uint32, uint64, uint32) [with T = js::BaseShape; js::AllowGC allowGC = (js::AllowGC)1u; size_t = long unsigned int] | |||
void js::gc::RunDebugGC(JSContext*) | |||
void js::MinorGC(JSRuntime*, uint32) | |||
GC | |||
</pre> | |||
This means that a rooting hazard was discovered at js/src/jsopcode.cpp line 1866, in the function DecompileExpressionFromStack (it is prefixed with the filename because it's a static function.) The problem is that they're an unrooted variable 'ed' that holds an ExpressionDecompiler live across a call to decompilePC. "Live" means that the variable is used after the call to decompilePC returns. decompilePC may trigger a GC according to the static call stack given starting from the line beginning with "GC Function:". The hazard itself has some barely comprehensible Assume(...) and Call(...) gibberish that describes the exact path of the variable into the function call. That stuff is rarely useful -- usually, you'll only need to look at it if it's complaining about a temporary and you want to know where the temporary came from. The type 'ExpressionDecompiler' is believed to hold pointers to GC-controlled objects of some sort. The analysis currently does not describe the exact field it is worried about. | |||
To unpack this a little, the analysis is saying the following can happen: | |||
* ExpressionDecompiler contains some pointer to a GC thing. For example, it might have a field 'obj' of type 'JSObject*'. | |||
* DecompileExpressionFromStack is called. | |||
* A pointer is stored in that field of the 'ed' variable. | |||
* decompilePC is invoked, which calls ValueToSource, which calls Invoke, which eventually calls js::MinorGC | |||
* during the resulting garbage collection, the object pointed to by ed.obj is moved to a different location. All pointers stored in the JS heap are updated automatically, as are all rooted pointers. ed.obj is not, because the GC doesn't know about it. | |||
* after decompilePC returns, something accesses ed.obj. This is now a stale pointer, and may refer to just about anything -- the wrong object, an invalid object, or whatever. Badness 10000, as TeX would say. |