ecc3cd256a
Prepare gcmole.cc for the next update: - Print possible GC locations when discovering stale/dead variables - Make error messages less confusing for the modern V8 engineer - Prepare gcmole to read suspects.allowlist instead of .whitelist - Use more readable variable names - Only log non-found types with --verbose - Change the currently unusued gccauses format in gcmole.py and support loading it back in gcmole.cc - Implemented first basic gc call-chain printing (disabled by default) GCmole packaging: - Add debug mode to bootstrap.sh build script - Update gcmole.py run instructions in bootstrap.sh and package.sh Bug: v8:10009 Change-Id: I369d48baa2980455d2e8f57e7a803d0384fe83f1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3480095 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Commit-Queue: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/main@{#79357}
133 lines
5.2 KiB
Plaintext
133 lines
5.2 KiB
Plaintext
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 Python
|
|
|
|
$ sudo apt-get install python
|
|
|
|
(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 Python and Clang plugin that does
|
|
C++ AST processing. Plugin (libgcmole.so) is expected to be in the same
|
|
folder as driver (gcmole.py).
|
|
|
|
To start analysis cd into the root of v8 checkout and execute the following
|
|
command:
|
|
|
|
CLANG_BIN=<path-to-clang-bin-folder> python tools/gcmole/gcmole.py [<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 python 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 tightly 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
|