| DESCRIPTION ------------------------------------------------------------------- | |
| gcmole is a simple static analysis tool used to find possible evaluation order | |
| dependent GC-unsafe places in the V8 codebase and "stale" pointers to the heap | |
| (ones whose addresses got invalidated by the GC). | |
| For example the following code is GC-unsafe: | |
| Handle<Object> Foo(); // Assume Foo can trigger a GC. | |
| void Bar(Object, Object); | |
| Handle<Object> baz; | |
| baz->Qux(*Foo()); // (a) | |
| Bar(*Foo(), *baz); // (b) | |
| Both in cases (a) and (b) compiler is free to evaluate call arguments (that | |
| includes receiver) in any order. That means it can dereference baz before | |
| calling to Foo and save a raw pointer to a heap object in the register or | |
| on the stack. | |
| In terms of the AST analysis that gcmole does, it warns about places in the | |
| code which result in 2 subtrees, the order of execution of which is undefined | |
| by C++, one of which causes a GC and the other dereferences a Handle to a raw | |
| Object (or its subclasses). | |
| The following code triggers a stale variable warning (assuming that the Foo | |
| function was detected as potentially allocating, as in the previous example): | |
| JSObject raw_obj = ...; | |
| Foo(); | |
| raw_obj.Print(); | |
| Since Foo can trigger a GC, it might have moved the raw_obj. The solution is | |
| simply to store it as a Handle. | |
| PREREQUISITES ----------------------------------------------------------------- | |
| (1) Install Lua 5.1 | |
| $ sudo apt-get install lua5.1 | |
| (2) Get LLVM 8.0 and Clang 8.0 sources and build them. | |
| Follow the instructions on http://clang.llvm.org/get_started.html. | |
| Make sure to pass -DCMAKE_BUILD_TYPE=Release to cmake to get Release build | |
| instead of a Debug one. | |
| (3) Build gcmole Clang plugin (libgcmole.so) | |
| In the tools/gcmole directory execute the following command: | |
| $ BUILD_ROOT=<path> LLVM_SRC_ROOT=<path> CLANG_SRC_ROOT=<path> make | |
| (*) Note that steps (2) and (3) can also be achieved by just using the included | |
| bootstrapping script in this directory: | |
| $ ./tools/gcmole/bootstrap.sh | |
| This will use "third_party/llvm+clang-build" as a build directory and checkout | |
| required sources in the "third_party" directory. | |
| USING GCMOLE ------------------------------------------------------------------ | |
| gcmole consists of driver script written in Lua and Clang plugin that does | |
| C++ AST processing. Plugin (libgcmole.so) is expected to be in the same | |
| folder as driver (gcmole.lua). | |
| To start analysis cd into the root of v8 checkout and execute the following | |
| command: | |
| CLANG_BIN=<path-to-clang-bin-folder> lua tools/gcmole/gcmole.lua [<arch>] | |
| where arch should be one of architectures supported by V8 (arm, ia32, x64). | |
| Analysis will be performed in 2 stages: | |
| - on the first stage driver will parse all files and build a global callgraph | |
| approximation to find all functions that might potentially cause GC, list | |
| of this functions will be written into gcsuspects file. | |
| - on the second stage driver will parse all files again and will locate all | |
| callsites that might be GC-unsafe based on the list of functions causing GC. | |
| Such places are marked with a "Possible problem with evaluation order." | |
| warning. Messages "Failed to resolve v8::internal::Object" are benign and | |
| can be ignored. | |
| If any errors were found driver exits with non-zero status. | |
| TESTING ----------------------------------------------------------------------- | |
| Tests are automatically run by the main lua runner. Expectations are in | |
| test-expectations.txt and need to be updated whenever the sources of the tests | |
| in gcmole-test.cc are modified (line numbers also count). | |
| PACKAGING --------------------------------------------------------------------- | |
| gcmole is deployed on V8's buildbot infrastructure to run it as part of the | |
| continuous integration. A pre-built package of gcmole together with Clang is | |
| hosted on Google Cloud Storage for this purpose. To update this package to a | |
| newer version, use the provided packaging script: | |
| $ ./tools/gcmole/package.sh | |
| This will create a new "tools/gcmole/gcmole-tools.tar.gz" package with the | |
| corresponding SHA1 sum suitable to be used for this purpose. It assumes that | |
| Clang was built in "third_party/llvm+clang-build" (e.g. by the bootstrapping | |
| script "bootstrap.sh" mentioned above). | |
| TROUBLESHOOTING --------------------------------------------------------------- | |
| gcmole is tighly coupled with the AST structure that Clang produces. Therefore | |
| when upgrading to a newer Clang version, it might start producing bogus output | |
| or completely stop outputting warnings. In such occasion, one might start the | |
| debugging process by checking weather a new AST node type is introduced which | |
| is currently not supported by gcmole. Insert the following code at the end of | |
| the FunctionAnalyzer::VisitExpr method to see the unsupported AST class(es) | |
| and the source position which generates them: | |
| if (expr) { | |
| clang::Stmt::StmtClass stmtClass = expr->getStmtClass(); | |
| d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_), | |
| d_.getCustomDiagID(clang::DiagnosticsEngine::Remark, "%0")) << stmtClass; | |
| } | |
| For instance, gcmole currently doesn't support AtomicExprClass statements | |
| introduced for atomic operations. | |
| A convenient way to observe the AST generated by Clang is to pass the following | |
| flags when invoking clang++ | |
| -Xclang -ast-dump -fsyntax-only |