| 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 |