[tools] Improve gcmole part II
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}
This commit is contained in:
parent
8e18ea3913
commit
ecc3cd256a
@ -2293,12 +2293,20 @@ struct borrowed_vec {
|
|||||||
|
|
||||||
// Vectors
|
// Vectors
|
||||||
|
|
||||||
#define WASM_DEFINE_VEC_BASE(name, Name, vec, ptr_or_none) \
|
#ifdef V8_GC_MOLE
|
||||||
|
#define ASSERT_VEC_BASE_SIZE(name, Name, vec, ptr_or_none)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define ASSERT_VEC_BASE_SIZE(name, Name, vec, ptr_or_none) \
|
||||||
static_assert(sizeof(wasm_##name##_vec_t) == sizeof(vec<Name>), \
|
static_assert(sizeof(wasm_##name##_vec_t) == sizeof(vec<Name>), \
|
||||||
"C/C++ incompatibility"); \
|
"C/C++ incompatibility"); \
|
||||||
static_assert( \
|
static_assert( \
|
||||||
sizeof(wasm_##name##_t ptr_or_none) == sizeof(vec<Name>::elem_type), \
|
sizeof(wasm_##name##_t ptr_or_none) == sizeof(vec<Name>::elem_type), \
|
||||||
"C/C++ incompatibility"); \
|
"C/C++ incompatibility");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define WASM_DEFINE_VEC_BASE(name, Name, vec, ptr_or_none) \
|
||||||
|
ASSERT_VEC_BASE_SIZE(name, Name, vec, ptr_or_none) \
|
||||||
extern "C++" inline auto hide_##name##_vec(vec<Name>& v) \
|
extern "C++" inline auto hide_##name##_vec(vec<Name>& v) \
|
||||||
->wasm_##name##_vec_t* { \
|
->wasm_##name##_vec_t* { \
|
||||||
return reinterpret_cast<wasm_##name##_vec_t*>(&v); \
|
return reinterpret_cast<wasm_##name##_vec_t*>(&v); \
|
||||||
|
@ -495,7 +495,9 @@ class V8_EXPORT_PRIVATE WasmCode final {
|
|||||||
// often for rather small functions.
|
// often for rather small functions.
|
||||||
// Increase the limit if needed, but first check if the size increase is
|
// Increase the limit if needed, but first check if the size increase is
|
||||||
// justified.
|
// justified.
|
||||||
|
#ifndef V8_GC_MOLE
|
||||||
STATIC_ASSERT(sizeof(WasmCode) <= 88);
|
STATIC_ASSERT(sizeof(WasmCode) <= 88);
|
||||||
|
#endif
|
||||||
|
|
||||||
WasmCode::Kind GetCodeKind(const WasmCompilationResult& result);
|
WasmCode::Kind GetCodeKind(const WasmCompilationResult& result);
|
||||||
|
|
||||||
|
@ -32,11 +32,18 @@ LLVM_BUILD_INCLUDE:=$(BUILD_ROOT)/include
|
|||||||
CLANG_SRC_INCLUDE:=$(CLANG_SRC_ROOT)/include
|
CLANG_SRC_INCLUDE:=$(CLANG_SRC_ROOT)/include
|
||||||
CLANG_BUILD_INCLUDE:=$(BUILD_ROOT)/tools/clang/include
|
CLANG_BUILD_INCLUDE:=$(BUILD_ROOT)/tools/clang/include
|
||||||
|
|
||||||
|
CXXFLAGS = -O3 -g3
|
||||||
|
all: libgcmole.so
|
||||||
|
Release: libgcmole.so
|
||||||
|
|
||||||
|
Debug: CXXFLAGS = -O1 -DDEBUG -g
|
||||||
|
Debug: libgcmole.so
|
||||||
|
|
||||||
libgcmole.so: gcmole.cc
|
libgcmole.so: gcmole.cc
|
||||||
$(CXX) -I$(LLVM_BUILD_INCLUDE) -I$(LLVM_SRC_INCLUDE) \
|
$(CXX) -I$(LLVM_BUILD_INCLUDE) -I$(LLVM_SRC_INCLUDE) \
|
||||||
-I$(CLANG_BUILD_INCLUDE) -I$(CLANG_SRC_INCLUDE) -I. -D_DEBUG \
|
-I$(CLANG_BUILD_INCLUDE) -I$(CLANG_SRC_INCLUDE) -I. ${CXXFLAGS} \
|
||||||
-D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS \
|
-D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS \
|
||||||
-D__STDC_LIMIT_MACROS -O3 -fomit-frame-pointer -fno-exceptions \
|
-D__STDC_LIMIT_MACROS -fomit-frame-pointer -fno-exceptions \
|
||||||
-fno-rtti -fPIC -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing \
|
-fno-rtti -fPIC -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing \
|
||||||
-pedantic -Wno-long-long -Wall -W -Wno-unused-parameter \
|
-pedantic -Wno-long-long -Wall -W -Wno-unused-parameter \
|
||||||
-Wwrite-strings -static-libstdc++ -std=c++0x -shared -o libgcmole.so \
|
-Wwrite-strings -static-libstdc++ -std=c++0x -shared -o libgcmole.so \
|
||||||
|
@ -109,7 +109,7 @@ script "bootstrap.sh" mentioned above).
|
|||||||
|
|
||||||
TROUBLESHOOTING ---------------------------------------------------------------
|
TROUBLESHOOTING ---------------------------------------------------------------
|
||||||
|
|
||||||
gcmole is tighly coupled with the AST structure that Clang produces. Therefore
|
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
|
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
|
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
|
debugging process by checking weather a new AST node type is introduced which
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
|
|
||||||
LLVM_RELEASE=9.0.1
|
LLVM_RELEASE=9.0.1
|
||||||
|
|
||||||
|
BUILD_TYPE="Release"
|
||||||
|
# BUILD_TYPE="Debug"
|
||||||
THIS_DIR="$(readlink -f "$(dirname "${0}")")"
|
THIS_DIR="$(readlink -f "$(dirname "${0}")")"
|
||||||
LLVM_PROJECT_DIR="${THIS_DIR}/bootstrap/llvm"
|
LLVM_PROJECT_DIR="${THIS_DIR}/bootstrap/llvm"
|
||||||
BUILD_DIR="${THIS_DIR}/bootstrap/build"
|
BUILD_DIR="${THIS_DIR}/bootstrap/build"
|
||||||
@ -99,10 +101,11 @@ if [ ! -e "${BUILD_DIR}" ]; then
|
|||||||
fi
|
fi
|
||||||
cd "${BUILD_DIR}"
|
cd "${BUILD_DIR}"
|
||||||
cmake -GNinja -DCMAKE_CXX_FLAGS="-static-libstdc++" -DLLVM_ENABLE_TERMINFO=OFF \
|
cmake -GNinja -DCMAKE_CXX_FLAGS="-static-libstdc++" -DLLVM_ENABLE_TERMINFO=OFF \
|
||||||
-DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang \
|
-DCMAKE_BUILD_TYPE=$BUILD_TYPE -DLLVM_ENABLE_PROJECTS=clang \
|
||||||
-DLLVM_ENABLE_Z3_SOLVER=OFF "${LLVM_PROJECT_DIR}/llvm"
|
-DLLVM_ENABLE_Z3_SOLVER=OFF "${LLVM_PROJECT_DIR}/llvm"
|
||||||
MACOSX_DEPLOYMENT_TARGET=10.5 ninja -j"${NUM_JOBS}"
|
MACOSX_DEPLOYMENT_TARGET=10.5 ninja -j"${NUM_JOBS}" clang
|
||||||
|
|
||||||
|
if [[ "${BUILD_TYPE}" = "Release" ]]; then
|
||||||
# Strip the clang binary.
|
# Strip the clang binary.
|
||||||
STRIP_FLAGS=
|
STRIP_FLAGS=
|
||||||
if [ "${OS}" = "Darwin" ]; then
|
if [ "${OS}" = "Darwin" ]; then
|
||||||
@ -110,18 +113,23 @@ if [ "${OS}" = "Darwin" ]; then
|
|||||||
STRIP_FLAGS=-x
|
STRIP_FLAGS=-x
|
||||||
fi
|
fi
|
||||||
strip ${STRIP_FLAGS} bin/clang
|
strip ${STRIP_FLAGS} bin/clang
|
||||||
|
fi
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
# Build libgcmole.so
|
# Build libgcmole.so
|
||||||
make -C "${THIS_DIR}" clean
|
make -C "${THIS_DIR}" clean
|
||||||
make -C "${THIS_DIR}" LLVM_SRC_ROOT="${LLVM_PROJECT_DIR}/llvm" \
|
make -C "${THIS_DIR}" LLVM_SRC_ROOT="${LLVM_PROJECT_DIR}/llvm" \
|
||||||
CLANG_SRC_ROOT="${LLVM_PROJECT_DIR}/clang" \
|
CLANG_SRC_ROOT="${LLVM_PROJECT_DIR}/clang" \
|
||||||
BUILD_ROOT="${BUILD_DIR}" libgcmole.so
|
BUILD_ROOT="${BUILD_DIR}" $BUILD_TYPE
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
|
echo '#########################################################################'
|
||||||
|
echo 'Congratulations you compiled clang and libgcmole.so'
|
||||||
echo
|
echo
|
||||||
echo You can now run gcmole using this command:
|
echo '# You can now run gcmole:'
|
||||||
echo
|
echo 'tools/gcmole/gcmole.py \'
|
||||||
echo CLANG_BIN=\"tools/gcmole/gcmole-tools/bin\" python tools/gcmole/gcmole.py
|
echo ' --clang-bin-dir="tools/gcmole/bootstrap/build/bin" \'
|
||||||
|
echo ' --clang-plugins-dir="tools/gcmole" \'
|
||||||
|
echo ' --v8-target-cpu=$CPU'
|
||||||
echo
|
echo
|
||||||
|
@ -48,6 +48,8 @@ namespace {
|
|||||||
|
|
||||||
bool g_tracing_enabled = false;
|
bool g_tracing_enabled = false;
|
||||||
bool g_dead_vars_analysis = false;
|
bool g_dead_vars_analysis = false;
|
||||||
|
bool g_verbose = false;
|
||||||
|
bool g_print_gc_call_chain = false;
|
||||||
|
|
||||||
#define TRACE(str) \
|
#define TRACE(str) \
|
||||||
do { \
|
do { \
|
||||||
@ -80,8 +82,8 @@ typedef std::map<MangledName, MangledName> CalleesMap;
|
|||||||
static bool GetMangledName(clang::MangleContext* ctx,
|
static bool GetMangledName(clang::MangleContext* ctx,
|
||||||
const clang::NamedDecl* decl,
|
const clang::NamedDecl* decl,
|
||||||
MangledName* result) {
|
MangledName* result) {
|
||||||
if (!llvm::isa<clang::CXXConstructorDecl>(decl) &&
|
if (llvm::isa<clang::CXXConstructorDecl>(decl)) return false;
|
||||||
!llvm::isa<clang::CXXDestructorDecl>(decl)) {
|
if (llvm::isa<clang::CXXDestructorDecl>(decl)) return false;
|
||||||
llvm::SmallVector<char, 512> output;
|
llvm::SmallVector<char, 512> output;
|
||||||
llvm::raw_svector_ostream out(output);
|
llvm::raw_svector_ostream out(output);
|
||||||
ctx->mangleName(decl, out);
|
ctx->mangleName(decl, out);
|
||||||
@ -89,9 +91,6 @@ static bool GetMangledName(clang::MangleContext* ctx,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool InV8Namespace(const clang::NamedDecl* decl) {
|
static bool InV8Namespace(const clang::NamedDecl* decl) {
|
||||||
return decl->getQualifiedNameAsString().compare(0, 4, "v8::") == 0;
|
return decl->getQualifiedNameAsString().compare(0, 4, "v8::") == 0;
|
||||||
@ -217,8 +216,7 @@ struct Resolver {
|
|||||||
|
|
||||||
class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
|
class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
|
||||||
public:
|
public:
|
||||||
explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {
|
explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {}
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool VisitCallExpr(clang::CallExpr* expr) {
|
virtual bool VisitCallExpr(clang::CallExpr* expr) {
|
||||||
const clang::FunctionDecl* callee = expr->getDirectCallee();
|
const clang::FunctionDecl* callee = expr->getDirectCallee();
|
||||||
@ -236,8 +234,9 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzeFunction(const clang::FunctionDecl* f) {
|
void AnalyzeFunction(const clang::FunctionDecl* f) {
|
||||||
|
if (!InV8Namespace(f)) return;
|
||||||
MangledName name;
|
MangledName name;
|
||||||
if (InV8Namespace(f) && GetMangledName(ctx_, f, &name)) {
|
if (!GetMangledName(ctx_, f, &name)) return;
|
||||||
const std::string& function = f->getNameAsString();
|
const std::string& function = f->getNameAsString();
|
||||||
AddCallee(name, function);
|
AddCallee(name, function);
|
||||||
|
|
||||||
@ -248,7 +247,6 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
|
|||||||
LeaveScope();
|
LeaveScope();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
typedef std::map<MangledName, CalleesSet* > Callgraph;
|
typedef std::map<MangledName, CalleesSet* > Callgraph;
|
||||||
|
|
||||||
@ -303,17 +301,18 @@ class FunctionDeclarationFinder
|
|||||||
: public clang::ASTConsumer,
|
: public clang::ASTConsumer,
|
||||||
public clang::RecursiveASTVisitor<FunctionDeclarationFinder> {
|
public clang::RecursiveASTVisitor<FunctionDeclarationFinder> {
|
||||||
public:
|
public:
|
||||||
explicit FunctionDeclarationFinder(clang::DiagnosticsEngine& d,
|
explicit FunctionDeclarationFinder(
|
||||||
clang::SourceManager& sm,
|
clang::DiagnosticsEngine& diagnostics_engine,
|
||||||
|
clang::SourceManager& source_manager,
|
||||||
const std::vector<std::string>& args)
|
const std::vector<std::string>& args)
|
||||||
: d_(d), sm_(sm) {}
|
: diagnostics_engine_(diagnostics_engine),
|
||||||
|
source_manager_(source_manager) {}
|
||||||
|
|
||||||
virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
|
virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
|
||||||
mangle_context_ = clang::ItaniumMangleContext::create(ctx, d_);
|
mangle_context_ =
|
||||||
|
clang::ItaniumMangleContext::create(ctx, diagnostics_engine_);
|
||||||
callees_printer_ = new CalleesPrinter(mangle_context_);
|
callees_printer_ = new CalleesPrinter(mangle_context_);
|
||||||
|
|
||||||
TraverseDecl(ctx.getTranslationUnitDecl());
|
TraverseDecl(ctx.getTranslationUnitDecl());
|
||||||
|
|
||||||
callees_printer_->PrintCallGraph();
|
callees_printer_->PrintCallGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,8 +322,8 @@ class FunctionDeclarationFinder
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
clang::DiagnosticsEngine& d_;
|
clang::DiagnosticsEngine& diagnostics_engine_;
|
||||||
clang::SourceManager& sm_;
|
clang::SourceManager& source_manager_;
|
||||||
clang::MangleContext* mangle_context_;
|
clang::MangleContext* mangle_context_;
|
||||||
|
|
||||||
CalleesPrinter* callees_printer_;
|
CalleesPrinter* callees_printer_;
|
||||||
@ -333,8 +332,39 @@ class FunctionDeclarationFinder
|
|||||||
static bool gc_suspects_loaded = false;
|
static bool gc_suspects_loaded = false;
|
||||||
static CalleesSet gc_suspects;
|
static CalleesSet gc_suspects;
|
||||||
static CalleesSet gc_functions;
|
static CalleesSet gc_functions;
|
||||||
static bool whitelist_loaded = false;
|
|
||||||
static CalleesSet suspects_whitelist;
|
static bool allowlist_loaded = false;
|
||||||
|
static CalleesSet suspects_allowlist;
|
||||||
|
|
||||||
|
static bool gc_causes_loaded = false;
|
||||||
|
static std::map<MangledName, std::vector<MangledName>> gc_causes;
|
||||||
|
|
||||||
|
static void LoadGCCauses() {
|
||||||
|
if (gc_causes_loaded) return;
|
||||||
|
std::ifstream fin("gccauses");
|
||||||
|
std::string mangled, function;
|
||||||
|
while (!fin.eof()) {
|
||||||
|
std::getline(fin, mangled, ',');
|
||||||
|
std::getline(fin, function);
|
||||||
|
if (mangled.empty()) break;
|
||||||
|
std::string parent = mangled;
|
||||||
|
// start,nested
|
||||||
|
std::getline(fin, mangled, ',');
|
||||||
|
assert(mangled.compare("start") == 0);
|
||||||
|
std::getline(fin, function);
|
||||||
|
assert(function.compare("nested") == 0);
|
||||||
|
while (true) {
|
||||||
|
std::getline(fin, mangled, ',');
|
||||||
|
std::getline(fin, function);
|
||||||
|
if (mangled.compare("end") == 0) {
|
||||||
|
assert(function.compare("nested") == 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gc_causes[parent].push_back(mangled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gc_causes_loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
static void LoadGCSuspects() {
|
static void LoadGCSuspects() {
|
||||||
if (gc_suspects_loaded) return;
|
if (gc_suspects_loaded) return;
|
||||||
@ -352,55 +382,51 @@ static void LoadGCSuspects() {
|
|||||||
gc_suspects_loaded = true;
|
gc_suspects_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LoadSuspectsWhitelist() {
|
static void LoadSuspectsAllowList() {
|
||||||
if (whitelist_loaded) return;
|
if (allowlist_loaded) return;
|
||||||
|
|
||||||
std::ifstream fin("tools/gcmole/suspects.whitelist");
|
// TODO(cbruni): clean up once fully migrated
|
||||||
|
std::ifstream fin("tools/gcmole/suspects.allowlist");
|
||||||
|
if (!fin.is_open()) {
|
||||||
|
fin = std::ifstream("tools/gcmole/suspects.whitelist");
|
||||||
|
}
|
||||||
std::string s;
|
std::string s;
|
||||||
|
|
||||||
while (fin >> s) suspects_whitelist.insert(s);
|
while (fin >> s) suspects_allowlist.insert(s);
|
||||||
|
|
||||||
whitelist_loaded = true;
|
allowlist_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks for exact match of the mangled name.
|
// Looks for exact match of the mangled name.
|
||||||
static bool KnownToCauseGC(clang::MangleContext* ctx,
|
static bool IsKnownToCauseGC(clang::MangleContext* ctx,
|
||||||
const clang::FunctionDecl* decl) {
|
const clang::FunctionDecl* decl) {
|
||||||
LoadGCSuspects();
|
LoadGCSuspects();
|
||||||
|
|
||||||
if (!InV8Namespace(decl)) return false;
|
if (!InV8Namespace(decl)) return false;
|
||||||
|
if (suspects_allowlist.find(decl->getNameAsString()) !=
|
||||||
if (suspects_whitelist.find(decl->getNameAsString()) !=
|
suspects_allowlist.end()) {
|
||||||
suspects_whitelist.end()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MangledName name;
|
MangledName name;
|
||||||
if (GetMangledName(ctx, decl, &name)) {
|
if (GetMangledName(ctx, decl, &name)) {
|
||||||
return gc_suspects.find(name) != gc_suspects.end();
|
return gc_suspects.find(name) != gc_suspects.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks for partial match of only the function name.
|
// Looks for partial match of only the function name.
|
||||||
static bool SuspectedToCauseGC(clang::MangleContext* ctx,
|
static bool IsSuspectedToCauseGC(clang::MangleContext* ctx,
|
||||||
const clang::FunctionDecl* decl) {
|
const clang::FunctionDecl* decl) {
|
||||||
LoadGCSuspects();
|
LoadGCSuspects();
|
||||||
|
|
||||||
if (!InV8Namespace(decl)) return false;
|
if (!InV8Namespace(decl)) return false;
|
||||||
|
LoadSuspectsAllowList();
|
||||||
LoadSuspectsWhitelist();
|
if (suspects_allowlist.find(decl->getNameAsString()) !=
|
||||||
if (suspects_whitelist.find(decl->getNameAsString()) !=
|
suspects_allowlist.end()) {
|
||||||
suspects_whitelist.end()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gc_functions.find(decl->getNameAsString()) != gc_functions.end()) {
|
if (gc_functions.find(decl->getNameAsString()) != gc_functions.end()) {
|
||||||
TRACE_LLVM_DECL("Suspected by ", decl);
|
TRACE_LLVM_DECL("Suspected by ", decl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,10 +475,9 @@ class ExprEffect {
|
|||||||
intptr_t effect_;
|
intptr_t effect_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const std::string BAD_EXPR_MSG(
|
||||||
const std::string BAD_EXPR_MSG("Possible problem with evaluation order.");
|
"Possible problem with evaluation order with interleaved GCs.");
|
||||||
const std::string DEAD_VAR_MSG("Possibly dead variable.");
|
const std::string DEAD_VAR_MSG("Possibly stale variable due to GCs.");
|
||||||
|
|
||||||
|
|
||||||
class Environment {
|
class Environment {
|
||||||
public:
|
public:
|
||||||
@ -612,22 +637,16 @@ class CallProps {
|
|||||||
|
|
||||||
ExprEffect ComputeCumulativeEffect(bool result_is_raw) {
|
ExprEffect ComputeCumulativeEffect(bool result_is_raw) {
|
||||||
ExprEffect out = ExprEffect::NoneWithEnv(env_);
|
ExprEffect out = ExprEffect::NoneWithEnv(env_);
|
||||||
if (gc_.any()) {
|
if (gc_.any()) out.setGC();
|
||||||
out.setGC();
|
|
||||||
}
|
|
||||||
if (raw_use_.any()) out.setRawUse();
|
if (raw_use_.any()) out.setRawUse();
|
||||||
if (result_is_raw) out.setRawDef();
|
if (result_is_raw) out.setRawDef();
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSafe() {
|
bool IsSafe() {
|
||||||
if (!gc_.any()) {
|
if (!gc_.any()) return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
std::bitset<kMaxNumberOfArguments> raw = (raw_def_ | raw_use_);
|
std::bitset<kMaxNumberOfArguments> raw = (raw_def_ | raw_use_);
|
||||||
if (!raw.any()) {
|
if (!raw.any()) return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool result = gc_.count() == 1 && !((raw ^ gc_).any());
|
bool result = gc_.count() == 1 && !((raw ^ gc_).any());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -950,13 +969,10 @@ class FunctionAnalyzer {
|
|||||||
ExprEffect Parallel(clang::Expr* parent, int n, clang::Expr** exprs,
|
ExprEffect Parallel(clang::Expr* parent, int n, clang::Expr** exprs,
|
||||||
const Environment& env) {
|
const Environment& env) {
|
||||||
CallProps props;
|
CallProps props;
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
props.SetEffect(i, VisitExpr(exprs[i], env));
|
props.SetEffect(i, VisitExpr(exprs[i], env));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props.IsSafe()) ReportUnsafe(parent, BAD_EXPR_MSG);
|
if (!props.IsSafe()) ReportUnsafe(parent, BAD_EXPR_MSG);
|
||||||
|
|
||||||
return props.ComputeCumulativeEffect(
|
return props.ComputeCumulativeEffect(
|
||||||
RepresentsRawPointerType(parent->getType()));
|
RepresentsRawPointerType(parent->getType()));
|
||||||
}
|
}
|
||||||
@ -984,27 +1000,24 @@ class FunctionAnalyzer {
|
|||||||
const clang::QualType& var_type,
|
const clang::QualType& var_type,
|
||||||
const std::string& var_name,
|
const std::string& var_name,
|
||||||
const Environment& env) {
|
const Environment& env) {
|
||||||
if (RepresentsRawPointerType(var_type)) {
|
if (!g_dead_vars_analysis) return ExprEffect::None();
|
||||||
|
if (!RepresentsRawPointerType(var_type)) return ExprEffect::None();
|
||||||
// We currently care only about our internal pointer types and not about
|
// We currently care only about our internal pointer types and not about
|
||||||
// raw C++ pointers, because normally special care is taken when storing
|
// raw C++ pointers, because normally special care is taken when storing
|
||||||
// raw pointers to the managed heap. Furthermore, checking for raw
|
// raw pointers to the managed heap. Furthermore, checking for raw
|
||||||
// pointers produces too many false positives in the dead variable
|
// pointers produces too many false positives in the dead variable
|
||||||
// analysis.
|
// analysis.
|
||||||
if (IsInternalPointerType(var_type) && !env.IsAlive(var_name) &&
|
if (!IsInternalPointerType(var_type)) return ExprEffect::None();
|
||||||
!HasActiveGuard() && g_dead_vars_analysis) {
|
if (env.IsAlive(var_name)) return ExprEffect::None();
|
||||||
|
if (HasActiveGuard()) return ExprEffect::None();
|
||||||
ReportUnsafe(parent, DEAD_VAR_MSG);
|
ReportUnsafe(parent, DEAD_VAR_MSG);
|
||||||
}
|
|
||||||
return ExprEffect::RawUse();
|
return ExprEffect::RawUse();
|
||||||
}
|
}
|
||||||
return ExprEffect::None();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprEffect Use(const clang::Expr* parent,
|
ExprEffect Use(const clang::Expr* parent,
|
||||||
const clang::ValueDecl* var,
|
const clang::ValueDecl* var,
|
||||||
const Environment& env) {
|
const Environment& env) {
|
||||||
if (IsExternalVMState(var)) {
|
if (IsExternalVMState(var)) return ExprEffect::GC();
|
||||||
return ExprEffect::GC();
|
|
||||||
}
|
|
||||||
return Use(parent, var->getType(), var->getNameAsString(), env);
|
return Use(parent, var->getType(), var->getNameAsString(), env);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,43 +1075,40 @@ class FunctionAnalyzer {
|
|||||||
RepresentsRawPointerType(call->getType()));
|
RepresentsRawPointerType(call->getType()));
|
||||||
|
|
||||||
clang::FunctionDecl* callee = call->getDirectCallee();
|
clang::FunctionDecl* callee = call->getDirectCallee();
|
||||||
if (callee != NULL) {
|
if (callee == NULL) return out;
|
||||||
if (KnownToCauseGC(ctx_, callee)) {
|
|
||||||
|
if (IsKnownToCauseGC(ctx_, callee)) {
|
||||||
out.setGC();
|
out.setGC();
|
||||||
scopes_.back().SetGCCauseLocation(
|
scopes_.back().SetGCCauseLocation(
|
||||||
clang::FullSourceLoc(call->getExprLoc(), sm_));
|
clang::FullSourceLoc(call->getExprLoc(), sm_), callee);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support for virtual methods that might be GC suspects.
|
// Support for virtual methods that might be GC suspects.
|
||||||
|
if (memcall == NULL) return out;
|
||||||
clang::CXXMethodDecl* method =
|
clang::CXXMethodDecl* method =
|
||||||
llvm::dyn_cast_or_null<clang::CXXMethodDecl>(callee);
|
llvm::dyn_cast_or_null<clang::CXXMethodDecl>(callee);
|
||||||
if (method != NULL && method->isVirtual()) {
|
if (method == NULL) return out;
|
||||||
clang::CXXMemberCallExpr* memcall =
|
if (!method->isVirtual()) return out;
|
||||||
llvm::dyn_cast_or_null<clang::CXXMemberCallExpr>(call);
|
|
||||||
if (memcall != NULL) {
|
|
||||||
clang::CXXMethodDecl* target = method->getDevirtualizedMethod(
|
clang::CXXMethodDecl* target = method->getDevirtualizedMethod(
|
||||||
memcall->getImplicitObjectArgument(), false);
|
memcall->getImplicitObjectArgument(), false);
|
||||||
if (target != NULL) {
|
if (target != NULL) {
|
||||||
if (KnownToCauseGC(ctx_, target)) {
|
if (IsKnownToCauseGC(ctx_, target)) {
|
||||||
out.setGC();
|
out.setGC();
|
||||||
scopes_.back().SetGCCauseLocation(
|
scopes_.back().SetGCCauseLocation(
|
||||||
clang::FullSourceLoc(call->getExprLoc(), sm_));
|
clang::FullSourceLoc(call->getExprLoc(), sm_), target);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// According to the documentation, {getDevirtualizedMethod} might
|
// According to the documentation, {getDevirtualizedMethod} might
|
||||||
// return NULL, in which case we still want to use the partial
|
// return NULL, in which case we still want to use the partial
|
||||||
// match of the {method}'s name against the GC suspects in order
|
// match of the {method}'s name against the GC suspects in order
|
||||||
// to increase coverage.
|
// to increase coverage.
|
||||||
if (SuspectedToCauseGC(ctx_, method)) {
|
if (IsSuspectedToCauseGC(ctx_, method)) {
|
||||||
out.setGC();
|
out.setGC();
|
||||||
scopes_.back().SetGCCauseLocation(
|
scopes_.back().SetGCCauseLocation(
|
||||||
clang::FullSourceLoc(call->getExprLoc(), sm_));
|
clang::FullSourceLoc(call->getExprLoc(), sm_), method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1185,12 +1195,10 @@ class FunctionAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool changed() {
|
bool changed() {
|
||||||
if (changed_) {
|
if (!changed_) return false;
|
||||||
changed_ = false;
|
changed_ = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Environment& in() {
|
const Environment& in() {
|
||||||
return in_;
|
return in_;
|
||||||
@ -1455,7 +1463,7 @@ class FunctionAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HasActiveGuard() {
|
bool HasActiveGuard() {
|
||||||
for (auto s : scopes_) {
|
for (const auto s : scopes_) {
|
||||||
if (s.IsBeforeGCCause()) return true;
|
if (s.IsBeforeGCCause()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1466,6 +1474,36 @@ class FunctionAnalyzer {
|
|||||||
d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_),
|
d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_),
|
||||||
d_.getCustomDiagID(clang::DiagnosticsEngine::Warning, "%0"))
|
d_.getCustomDiagID(clang::DiagnosticsEngine::Warning, "%0"))
|
||||||
<< msg;
|
<< msg;
|
||||||
|
if (scopes_.empty()) return;
|
||||||
|
GCScope scope = scopes_[0];
|
||||||
|
if (!scope.gccause_location.isValid()) return;
|
||||||
|
d_.Report(scope.gccause_location,
|
||||||
|
d_.getCustomDiagID(clang::DiagnosticsEngine::Note,
|
||||||
|
"Call might cause unexpected GC."));
|
||||||
|
clang::FunctionDecl* gccause_decl = scope.gccause_decl;
|
||||||
|
d_.Report(
|
||||||
|
clang::FullSourceLoc(gccause_decl->getBeginLoc(), sm_),
|
||||||
|
d_.getCustomDiagID(clang::DiagnosticsEngine::Note, "GC call here."));
|
||||||
|
|
||||||
|
if (!g_print_gc_call_chain) return;
|
||||||
|
// TODO(cbruni, v8::10009): print call-chain to gc with proper source
|
||||||
|
// positions.
|
||||||
|
LoadGCCauses();
|
||||||
|
MangledName name;
|
||||||
|
if (!GetMangledName(ctx_, gccause_decl, &name)) return;
|
||||||
|
std::cout << "Potential GC call chain:\n";
|
||||||
|
std::set<MangledName> stack;
|
||||||
|
while (true) {
|
||||||
|
if (!stack.insert(name).second) break;
|
||||||
|
std::cout << "\t" << name << "\n";
|
||||||
|
auto next = gc_causes.find(name);
|
||||||
|
if (next == gc_causes.end()) break;
|
||||||
|
std::vector<MangledName> calls = next->second;
|
||||||
|
for (MangledName call : calls) {
|
||||||
|
name = call;
|
||||||
|
if (stack.find(call) != stack.end()) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1484,10 +1522,11 @@ class FunctionAnalyzer {
|
|||||||
struct GCScope {
|
struct GCScope {
|
||||||
clang::FullSourceLoc guard_location;
|
clang::FullSourceLoc guard_location;
|
||||||
clang::FullSourceLoc gccause_location;
|
clang::FullSourceLoc gccause_location;
|
||||||
|
clang::FunctionDecl* gccause_decl;
|
||||||
|
|
||||||
// We're only interested in guards that are declared before any further GC
|
// We're only interested in guards that are declared before any further GC
|
||||||
// causing calls (see TestGuardedDeadVarAnalysisMidFunction for example).
|
// causing calls (see TestGuardedDeadVarAnalysisMidFunction for example).
|
||||||
bool IsBeforeGCCause() {
|
bool IsBeforeGCCause() const {
|
||||||
if (!guard_location.isValid()) return false;
|
if (!guard_location.isValid()) return false;
|
||||||
if (!gccause_location.isValid()) return true;
|
if (!gccause_location.isValid()) return true;
|
||||||
return guard_location.isBeforeInTranslationUnitThan(gccause_location);
|
return guard_location.isBeforeInTranslationUnitThan(gccause_location);
|
||||||
@ -1495,9 +1534,11 @@ class FunctionAnalyzer {
|
|||||||
|
|
||||||
// After we set the first GC cause in the scope, we don't need the later
|
// After we set the first GC cause in the scope, we don't need the later
|
||||||
// ones.
|
// ones.
|
||||||
void SetGCCauseLocation(clang::FullSourceLoc gccause_location_) {
|
void SetGCCauseLocation(clang::FullSourceLoc gccause_location_,
|
||||||
|
clang::FunctionDecl* decl) {
|
||||||
if (gccause_location.isValid()) return;
|
if (gccause_location.isValid()) return;
|
||||||
gccause_location = gccause_location_;
|
gccause_location = gccause_location_;
|
||||||
|
gccause_decl = decl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
std::vector<GCScope> scopes_;
|
std::vector<GCScope> scopes_;
|
||||||
@ -1513,9 +1554,8 @@ class ProblemsFinder : public clang::ASTConsumer,
|
|||||||
if (args[i] == "--dead-vars") {
|
if (args[i] == "--dead-vars") {
|
||||||
g_dead_vars_analysis = true;
|
g_dead_vars_analysis = true;
|
||||||
}
|
}
|
||||||
if (args[i] == "--verbose") {
|
if (args[i] == "--verbose-trace") g_tracing_enabled = true;
|
||||||
g_tracing_enabled = true;
|
if (args[i] == "--verbose") g_verbose = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1571,7 +1611,7 @@ class ProblemsFinder : public clang::ASTConsumer,
|
|||||||
clang::ItaniumMangleContext::create(ctx, d_), object_decl,
|
clang::ItaniumMangleContext::create(ctx, d_), object_decl,
|
||||||
maybe_object_decl, smi_decl, no_gc_mole_decl, d_, sm_);
|
maybe_object_decl, smi_decl, no_gc_mole_decl, d_, sm_);
|
||||||
TraverseDecl(ctx.getTranslationUnitDecl());
|
TraverseDecl(ctx.getTranslationUnitDecl());
|
||||||
} else {
|
} else if (g_verbose) {
|
||||||
if (object_decl == NULL) {
|
if (object_decl == NULL) {
|
||||||
llvm::errs() << "Failed to resolve v8::internal::Object\n";
|
llvm::errs() << "Failed to resolve v8::internal::Object\n";
|
||||||
}
|
}
|
||||||
@ -1609,7 +1649,6 @@ class ProblemsFinder : public clang::ASTConsumer,
|
|||||||
FunctionAnalyzer* function_analyzer_;
|
FunctionAnalyzer* function_analyzer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename ConsumerType>
|
template<typename ConsumerType>
|
||||||
class Action : public clang::PluginASTAction {
|
class Action : public clang::PluginASTAction {
|
||||||
protected:
|
protected:
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
# This is main driver for gcmole tool. See README for more details.
|
# This is main driver for gcmole tool. See README for more details.
|
||||||
# Usage: CLANG_BIN=clang-bin-dir python tools/gcmole/gcmole.py [arm|arm64|ia32|x64]
|
# Usage: CLANG_BIN=clang-bin-dir python tools/gcmole/gcmole.py [arm|arm64|ia32|x64]
|
||||||
|
|
||||||
|
|
||||||
# for py2/py3 compatibility
|
# for py2/py3 compatibility
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
@ -14,13 +13,13 @@ from multiprocessing import cpu_count
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
import difflib
|
import difflib
|
||||||
|
import json
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import json
|
|
||||||
|
|
||||||
if sys.version_info.major > 2:
|
if sys.version_info.major > 2:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -82,6 +81,15 @@ else:
|
|||||||
ArchCfg = collections.namedtuple(
|
ArchCfg = collections.namedtuple(
|
||||||
"ArchCfg", ["name", "cpu", "triple", "arch_define", "arch_options"])
|
"ArchCfg", ["name", "cpu", "triple", "arch_define", "arch_options"])
|
||||||
|
|
||||||
|
# TODO(cbruni): use gn desc by default for platform-specific settings
|
||||||
|
OPTIONS_64BIT = [
|
||||||
|
"-DV8_COMPRESS_POINTERS",
|
||||||
|
"-DV8_COMPRESS_POINTERS_IN_SHARED_CAGE",
|
||||||
|
"-DV8_EXTERNAL_CODE_SPACE",
|
||||||
|
"-DV8_SHORT_BUILTIN_CALLS",
|
||||||
|
"-DV8_SHARED_RO_HEAP",
|
||||||
|
]
|
||||||
|
|
||||||
ARCHITECTURES = {
|
ARCHITECTURES = {
|
||||||
"ia32":
|
"ia32":
|
||||||
ArchCfg(
|
ArchCfg(
|
||||||
@ -99,14 +107,15 @@ ARCHITECTURES = {
|
|||||||
arch_define="V8_TARGET_ARCH_ARM",
|
arch_define="V8_TARGET_ARCH_ARM",
|
||||||
arch_options=["-m32"],
|
arch_options=["-m32"],
|
||||||
),
|
),
|
||||||
|
# TODO(cbruni): Use detailed settings:
|
||||||
|
# arch_options = OPTIONS_64BIT + [ "-DV8_WIN64_UNWINDING_INFO" ]
|
||||||
"x64":
|
"x64":
|
||||||
ArchCfg(
|
ArchCfg(
|
||||||
name="x64",
|
name="x64",
|
||||||
cpu="x64",
|
cpu="x64",
|
||||||
triple="x86_64-unknown-linux",
|
triple="x86_64-unknown-linux",
|
||||||
arch_define="V8_TARGET_ARCH_X64",
|
arch_define="V8_TARGET_ARCH_X64",
|
||||||
arch_options=[],
|
arch_options=[]),
|
||||||
),
|
|
||||||
"arm64":
|
"arm64":
|
||||||
ArchCfg(
|
ArchCfg(
|
||||||
name="arm64",
|
name="arm64",
|
||||||
@ -148,7 +157,7 @@ def make_clang_command_line(plugin, plugin_args, options):
|
|||||||
icu_src_dir = options.v8_root_dir / 'third_party/icu/source'
|
icu_src_dir = options.v8_root_dir / 'third_party/icu/source'
|
||||||
return ([
|
return ([
|
||||||
options.clang_bin_dir / "clang++",
|
options.clang_bin_dir / "clang++",
|
||||||
"-std=c++14",
|
"-std=c++17",
|
||||||
"-c",
|
"-c",
|
||||||
"-Xclang",
|
"-Xclang",
|
||||||
"-load",
|
"-load",
|
||||||
@ -164,11 +173,13 @@ def make_clang_command_line(plugin, plugin_args, options):
|
|||||||
"-Xclang",
|
"-Xclang",
|
||||||
arch_cfg.triple,
|
arch_cfg.triple,
|
||||||
"-fno-exceptions",
|
"-fno-exceptions",
|
||||||
|
"-Wno-everything",
|
||||||
"-D",
|
"-D",
|
||||||
arch_cfg.arch_define,
|
arch_cfg.arch_define,
|
||||||
"-DENABLE_DEBUGGER_SUPPORT",
|
"-DENABLE_DEBUGGER_SUPPORT",
|
||||||
"-DV8_INTL_SUPPORT",
|
|
||||||
"-DV8_ENABLE_WEBASSEMBLY",
|
"-DV8_ENABLE_WEBASSEMBLY",
|
||||||
|
"-DV8_GC_MOLE",
|
||||||
|
"-DV8_INTL_SUPPORT",
|
||||||
"-I{}".format(options.v8_root_dir),
|
"-I{}".format(options.v8_root_dir),
|
||||||
"-I{}".format(options.v8_root_dir / 'include'),
|
"-I{}".format(options.v8_root_dir / 'include'),
|
||||||
"-I{}".format(options.v8_build_dir / 'gen'),
|
"-I{}".format(options.v8_build_dir / 'gen'),
|
||||||
@ -253,7 +264,7 @@ def invoke_clang_plugin_for_each_file(filenames, plugin, plugin_args, options):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
filename, returncode, stdout, stderr = output
|
filename, returncode, stdout, stderr = output
|
||||||
log(filename, level=1)
|
log(filename, level=2)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
sys.stderr.write(stderr)
|
sys.stderr.write(stderr)
|
||||||
sys.exit(returncode)
|
sys.exit(returncode)
|
||||||
@ -439,25 +450,44 @@ def generate_gc_suspects(files, options):
|
|||||||
collector.parse(stdout.splitlines())
|
collector.parse(stdout.splitlines())
|
||||||
collector.propagate()
|
collector.propagate()
|
||||||
# TODO(cbruni): remove once gcmole.cc is migrated
|
# TODO(cbruni): remove once gcmole.cc is migrated
|
||||||
write_gc_suspects(collector, options.v8_root_dir)
|
write_gcmole_results(collector, options, options.v8_root_dir)
|
||||||
write_gc_suspects(collector, options.out_dir)
|
write_gcmole_results(collector, options, options.out_dir)
|
||||||
log("GCSuspects generated for {}", options.v8_target_cpu)
|
|
||||||
|
|
||||||
|
|
||||||
def write_gc_suspects(collector, dst):
|
def write_gcmole_results(collector, options, dst):
|
||||||
|
# gcsuspects contains a list("mangled_full_name,name") of all functions that
|
||||||
|
# could cause a gc (directly or indirectly).
|
||||||
|
#
|
||||||
|
# EXAMPLE
|
||||||
|
# _ZN2v88internal4Heap16CreateApiObjectsEv,CreateApiObjects
|
||||||
|
# _ZN2v88internal4Heap17CreateInitialMapsEv,CreateInitialMaps
|
||||||
|
# ...
|
||||||
with open(dst / "gcsuspects", "w") as out:
|
with open(dst / "gcsuspects", "w") as out:
|
||||||
for name, value in collector.gc.items():
|
for name, value in list(collector.gc.items()):
|
||||||
if value:
|
if value:
|
||||||
out.write(name + "\n")
|
out.write(name + "\n")
|
||||||
|
# gccauses contains a map["mangled_full_name,name"] => list(inner gcsuspects)
|
||||||
|
# Where the inner gcsuspects are functions directly called in the outer
|
||||||
|
# function that can cause a gc. The format is encoded for simplified
|
||||||
|
# deserialization in gcmole.cc.
|
||||||
|
#
|
||||||
|
# EXAMPLE:
|
||||||
|
# _ZN2v88internal4Heap17CreateHeapObjectsEv,CreateHeapObjects
|
||||||
|
# start,nested
|
||||||
|
# _ZN2v88internal4Heap16CreateApiObjectsEv,CreateApiObjects
|
||||||
|
# _ZN2v88internal4Heap17CreateInitialMapsEv,CreateInitialMaps
|
||||||
|
# ...
|
||||||
|
# end,nested
|
||||||
|
# ...
|
||||||
with open(dst / "gccauses", "w") as out:
|
with open(dst / "gccauses", "w") as out:
|
||||||
out.write("GC = {\n")
|
for name, causes in list(collector.gc_caused.items()):
|
||||||
for name, causes in collector.gc_caused.items():
|
out.write("{}\n".format(name))
|
||||||
out.write(" '{}': [\n".format(name))
|
out.write("start,nested\n")
|
||||||
for cause in causes:
|
for cause in causes:
|
||||||
out.write(" '{}',\n".format(cause))
|
out.write("{}\n".format(cause))
|
||||||
out.write(" ],\n")
|
out.write("end,nested\n")
|
||||||
out.write("}\n")
|
log("GCSuspects and gccauses generated for {} in '{}'", options.v8_target_cpu,
|
||||||
|
dst)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@ -498,7 +528,7 @@ def check_correctness_for_arch(options, for_test):
|
|||||||
sys.stdout.write(stderr)
|
sys.stdout.write(stderr)
|
||||||
|
|
||||||
log("Done processing {} files.", processed_files)
|
log("Done processing {} files.", processed_files)
|
||||||
log("Errors found" if errors_found else "## No errors found")
|
log("Errors found" if errors_found else "No errors found")
|
||||||
|
|
||||||
return errors_found, output
|
return errors_found, output
|
||||||
|
|
||||||
@ -598,7 +628,7 @@ def main(args):
|
|||||||
parser.add_option(
|
parser.add_option(
|
||||||
"--out-dir",
|
"--out-dir",
|
||||||
metavar="DIR",
|
metavar="DIR",
|
||||||
help="Output location for gcsuspect and gcauses file."
|
help="Output location for the gcsuspect and gcauses file."
|
||||||
"Default: BUILD_DIR/gen/tools/gcmole")
|
"Default: BUILD_DIR/gen/tools/gcmole")
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"--is-bot",
|
"--is-bot",
|
||||||
@ -639,9 +669,9 @@ def main(args):
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
default=True,
|
default=True,
|
||||||
dest="allowlist",
|
dest="allowlist",
|
||||||
help="""When building gcsuspects allowlist certain functions as if they can be
|
help="When building gcsuspects allowlist certain functions as if they can be "
|
||||||
causing GC. Currently used to reduce number of false positives in dead
|
"causing GC. Currently used to reduce number of false positives in dead "
|
||||||
variables analysis. See TODO for ALLOWLIST in gcmole.py""")
|
"variables analysis. See TODO for ALLOWLIST in gcmole.py")
|
||||||
group.add_option(
|
group.add_option(
|
||||||
"--test-run",
|
"--test-run",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
@ -14,6 +14,7 @@ PACKAGE_DIR="${THIS_DIR}/gcmole-tools"
|
|||||||
PACKAGE_FILE="${THIS_DIR}/gcmole-tools.tar.gz"
|
PACKAGE_FILE="${THIS_DIR}/gcmole-tools.tar.gz"
|
||||||
PACKAGE_SUM="${THIS_DIR}/gcmole-tools.tar.gz.sha1"
|
PACKAGE_SUM="${THIS_DIR}/gcmole-tools.tar.gz.sha1"
|
||||||
BUILD_DIR="${THIS_DIR}/bootstrap/build"
|
BUILD_DIR="${THIS_DIR}/bootstrap/build"
|
||||||
|
V8_ROOT_DIR= `realpath "${THIS_DIR}/../.."`
|
||||||
|
|
||||||
# Echo all commands
|
# Echo all commands
|
||||||
set -x
|
set -x
|
||||||
@ -72,5 +73,8 @@ echo "sudo chroot \$CHROOT_DIR bash -c 'PATH=/docs/depot_tools:\$PATH; /docs/v8/
|
|||||||
echo
|
echo
|
||||||
echo You can now run gcmole using this command:
|
echo You can now run gcmole using this command:
|
||||||
echo
|
echo
|
||||||
echo CLANG_BIN=\"tools/gcmole/gcmole-tools/bin\" python tools/gcmole/gcmole.py
|
echo 'tools/gcmole/gcmole.py \'
|
||||||
|
echo ' --clang-bin-dir="tools/gcmole/gcmole-tools/bin" \'
|
||||||
|
echo ' --clang-plugins-dir="tools/gcmole/gcmole-tools" \'
|
||||||
|
echo ' --v8-target-cpu=$CPU'
|
||||||
echo
|
echo
|
||||||
|
Loading…
Reference in New Issue
Block a user