Commit d16a2a68 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Add DisableGCMole scope

Make sure gcmole detects issue in DisallowGarbageCollection scopes.

DisallowGarbageCollection is widely used in the codebase to document
code that doesn't allocate. However, this has the rather unexpected
side-effect that gcmole is not run when such a scope is active.

This CL changes the default behavior of gcmole to run even with
DisallowGarbageCollection scopes present. This will give us the best
results of both worlds, dynamic checks by the fuzzer, and static
analysis by gcmole.

To allow crazy local raw pointer operations there is a new
DisableGCMole scope that explicitly disables gcmole.

Change-Id: I0a78fb3b4ceaad35be9bcf7293d917a41f90c91f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2615419Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72039}
parent 2059ee81
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "src/builtins/builtins-utils-inl.h" #include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h" #include "src/builtins/builtins.h"
#include "src/codegen/code-factory.h" #include "src/codegen/code-factory.h"
#include "src/common/assert-scope.h"
#include "src/debug/debug.h" #include "src/debug/debug.h"
#include "src/execution/isolate.h" #include "src/execution/isolate.h"
#include "src/execution/protectors-inl.h" #include "src/execution/protectors-inl.h"
...@@ -969,6 +970,7 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object, ...@@ -969,6 +970,7 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
case FAST_SLOPPY_ARGUMENTS_ELEMENTS: case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: { case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
DisableGCMole no_gc_mole;
FixedArrayBase elements = object->elements(); FixedArrayBase elements = object->elements();
JSObject raw_object = *object; JSObject raw_object = *object;
ElementsAccessor* accessor = object->GetElementsAccessor(); ElementsAccessor* accessor = object->GetElementsAccessor();
......
...@@ -84,6 +84,7 @@ template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, false>; ...@@ -84,6 +84,7 @@ template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, false>;
template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>; template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>;
template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, false>; template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, false>;
template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, true>; template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, true>;
template class PerThreadAssertScope<GC_MOLE, false>;
template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, false>; template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, false>;
template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, true>; template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, true>;
......
...@@ -26,7 +26,9 @@ enum PerThreadAssertType { ...@@ -26,7 +26,9 @@ enum PerThreadAssertType {
HANDLE_ALLOCATION_ASSERT, HANDLE_ALLOCATION_ASSERT,
HANDLE_DEREFERENCE_ASSERT, HANDLE_DEREFERENCE_ASSERT,
CODE_DEPENDENCY_CHANGE_ASSERT, CODE_DEPENDENCY_CHANGE_ASSERT,
CODE_ALLOCATION_ASSERT CODE_ALLOCATION_ASSERT,
// Dummy type for disabling GC mole.
GC_MOLE,
}; };
enum PerIsolateAssertType { enum PerIsolateAssertType {
...@@ -35,7 +37,7 @@ enum PerIsolateAssertType { ...@@ -35,7 +37,7 @@ enum PerIsolateAssertType {
JAVASCRIPT_EXECUTION_DUMP, JAVASCRIPT_EXECUTION_DUMP,
DEOPTIMIZATION_ASSERT, DEOPTIMIZATION_ASSERT,
COMPILATION_ASSERT, COMPILATION_ASSERT,
NO_EXCEPTION_ASSERT NO_EXCEPTION_ASSERT,
}; };
template <PerThreadAssertType kType, bool kAllow> template <PerThreadAssertType kType, bool kAllow>
...@@ -191,6 +193,11 @@ using AllowCodeAllocation = ...@@ -191,6 +193,11 @@ using AllowCodeAllocation =
// DisallowHeapAllocation by also forbidding safepoints. // DisallowHeapAllocation by also forbidding safepoints.
using DisallowGarbageCollection = using DisallowGarbageCollection =
CombinationAssertScope<DisallowSafepoints, DisallowHeapAllocation>; CombinationAssertScope<DisallowSafepoints, DisallowHeapAllocation>;
// Scope to skip gc mole verification in places where we do tricky raw
// work.
using DisableGCMole = PerThreadAssertScopeDebugOnly<GC_MOLE, false>;
// The DISALLOW_GARBAGE_COLLECTION macro can be used to define a // The DISALLOW_GARBAGE_COLLECTION macro can be used to define a
// DisallowGarbageCollection field in classes that isn't present in release // DisallowGarbageCollection field in classes that isn't present in release
// builds. // builds.
...@@ -215,15 +222,6 @@ using AllowHeapAccess = ...@@ -215,15 +222,6 @@ using AllowHeapAccess =
CombinationAssertScope<AllowCodeDependencyChange, AllowHandleDereference, CombinationAssertScope<AllowCodeDependencyChange, AllowHandleDereference,
AllowHandleAllocation, AllowHeapAllocation>; AllowHandleAllocation, AllowHeapAllocation>;
// The DISALLOW_GARBAGE_COLLECTION macro can be used to define a
// DisallowSafepoints field in classes that isn't present in release
// builds.
#ifdef DEBUG
#define DISALLOW_GARBAGE_COLLECTION(name) DisallowGarbageCollection name;
#else
#define DISALLOW_GARBAGE_COLLECTION(name)
#endif
class DisallowHeapAccessIf { class DisallowHeapAccessIf {
public: public:
explicit DisallowHeapAccessIf(bool condition) { explicit DisallowHeapAccessIf(bool condition) {
...@@ -328,6 +326,7 @@ extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, ...@@ -328,6 +326,7 @@ extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT,
extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>; extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>;
extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, false>; extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, false>;
extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, true>; extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, true>;
extern template class PerThreadAssertScope<GC_MOLE, false>;
extern template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, false>; extern template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, false>;
extern template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, true>; extern template class PerIsolateAssertScope<JAVASCRIPT_EXECUTION_ASSERT, true>;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/regexp/experimental/experimental.h" #include "src/regexp/experimental/experimental.h"
#include "src/common/assert-scope.h"
#include "src/objects/js-regexp-inl.h" #include "src/objects/js-regexp-inl.h"
#include "src/regexp/experimental/experimental-compiler.h" #include "src/regexp/experimental/experimental-compiler.h"
#include "src/regexp/experimental/experimental-interpreter.h" #include "src/regexp/experimental/experimental-interpreter.h"
...@@ -145,6 +146,8 @@ int32_t ExecRawImpl(Isolate* isolate, RegExp::CallOrigin call_origin, ...@@ -145,6 +146,8 @@ int32_t ExecRawImpl(Isolate* isolate, RegExp::CallOrigin call_origin,
int32_t* output_registers, int32_t output_register_count, int32_t* output_registers, int32_t output_register_count,
int32_t subject_index) { int32_t subject_index) {
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
// TODO(cbruni): remove once gcmole is fixed.
DisableGCMole no_gc_mole;
int register_count_per_match = int register_count_per_match =
JSRegExp::RegistersForCaptureCount(capture_count); JSRegExp::RegistersForCaptureCount(capture_count);
......
...@@ -204,22 +204,25 @@ int NativeRegExpMacroAssembler::CheckStackGuardState( ...@@ -204,22 +204,25 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
bool is_one_byte = String::IsOneByteRepresentationUnderneath(*subject_handle); bool is_one_byte = String::IsOneByteRepresentationUnderneath(*subject_handle);
int return_value = 0; int return_value = 0;
if (js_has_overflowed) { {
AllowGarbageCollection yes_gc; DisableGCMole no_gc_mole;
isolate->StackOverflow(); if (js_has_overflowed) {
return_value = EXCEPTION; AllowGarbageCollection yes_gc;
} else if (check.InterruptRequested()) { isolate->StackOverflow();
AllowGarbageCollection yes_gc; return_value = EXCEPTION;
Object result = isolate->stack_guard()->HandleInterrupts(); } else if (check.InterruptRequested()) {
if (result.IsException(isolate)) return_value = EXCEPTION; AllowGarbageCollection yes_gc;
} Object result = isolate->stack_guard()->HandleInterrupts();
if (result.IsException(isolate)) return_value = EXCEPTION;
}
if (*code_handle != re_code) { // Return address no longer valid if (*code_handle != re_code) { // Return address no longer valid
// Overwrite the return address on the stack. // Overwrite the return address on the stack.
intptr_t delta = code_handle->address() - re_code.address(); intptr_t delta = code_handle->address() - re_code.address();
Address new_pc = old_pc + delta; Address new_pc = old_pc + delta;
// TODO(v8:10026): avoid replacing a signed pointer. // TODO(v8:10026): avoid replacing a signed pointer.
PointerAuthentication::ReplacePC(return_address, new_pc, 0); PointerAuthentication::ReplacePC(return_address, new_pc, 0);
}
} }
// If we continue, we need to update the subject string addresses. // If we continue, we need to update the subject string addresses.
......
...@@ -35,9 +35,9 @@ simply to store it as a Handle. ...@@ -35,9 +35,9 @@ simply to store it as a Handle.
PREREQUISITES ----------------------------------------------------------------- PREREQUISITES -----------------------------------------------------------------
(1) Install Lua 5.1 (1) Install Python
$ sudo apt-get install lua5.1 $ sudo apt-get install python
(2) Get LLVM 8.0 and Clang 8.0 sources and build them. (2) Get LLVM 8.0 and Clang 8.0 sources and build them.
...@@ -62,14 +62,14 @@ PREREQUISITES ----------------------------------------------------------------- ...@@ -62,14 +62,14 @@ PREREQUISITES -----------------------------------------------------------------
USING GCMOLE ------------------------------------------------------------------ USING GCMOLE ------------------------------------------------------------------
gcmole consists of driver script written in Lua and Clang plugin that does 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 C++ AST processing. Plugin (libgcmole.so) is expected to be in the same
folder as driver (gcmole.lua). folder as driver (gcmole.py).
To start analysis cd into the root of v8 checkout and execute the following To start analysis cd into the root of v8 checkout and execute the following
command: command:
CLANG_BIN=<path-to-clang-bin-folder> lua tools/gcmole/gcmole.lua [<arch>] 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). where arch should be one of architectures supported by V8 (arm, ia32, x64).
...@@ -89,7 +89,7 @@ If any errors were found driver exits with non-zero status. ...@@ -89,7 +89,7 @@ If any errors were found driver exits with non-zero status.
TESTING ----------------------------------------------------------------------- TESTING -----------------------------------------------------------------------
Tests are automatically run by the main lua runner. Expectations are in 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 test-expectations.txt and need to be updated whenever the sources of the tests
in gcmole-test.cc are modified (line numbers also count). in gcmole-test.cc are modified (line numbers also count).
......
...@@ -83,34 +83,40 @@ set -x ...@@ -83,34 +83,40 @@ set -x
NUM_JOBS=3 NUM_JOBS=3
if [[ "${OS}" = "Linux" ]]; then if [[ "${OS}" = "Linux" ]]; then
NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)" if [[ -e "/proc/cpuinfo" ]]; then
NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)"
else
# Hack when running in chroot
NUM_JOBS="32"
fi
elif [ "${OS}" = "Darwin" ]; then elif [ "${OS}" = "Darwin" ]; then
NUM_JOBS="$(sysctl -n hw.ncpu)" NUM_JOBS="$(sysctl -n hw.ncpu)"
fi fi
# Build clang. # Build clang.
if [ ! -e "${BUILD_DIR}" ]; then # if [ ! -e "${BUILD_DIR}" ]; then
mkdir "${BUILD_DIR}" # mkdir "${BUILD_DIR}"
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=Release -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}"
#
# Strip the clang binary. # # Strip the clang binary.
STRIP_FLAGS= # STRIP_FLAGS=
if [ "${OS}" = "Darwin" ]; then # if [ "${OS}" = "Darwin" ]; then
# See http://crbug.com/256342 # # See http://crbug.com/256342
STRIP_FLAGS=-x # STRIP_FLAGS=-x
fi # fi
strip ${STRIP_FLAGS} bin/clang # strip ${STRIP_FLAGS} bin/clang
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" BUILD_ROOT="${BUILD_DIR}" libgcmole.so CLANG_SRC_ROOT="${LLVM_PROJECT_DIR}/clang" \
BUILD_ROOT="${BUILD_DIR}" libgcmole.so
set +x set +x
......
...@@ -175,55 +175,130 @@ void TestDeadVarBecauseOfSafepointAnalysis(Isolate* isolate) { ...@@ -175,55 +175,130 @@ void TestDeadVarBecauseOfSafepointAnalysis(Isolate* isolate) {
void TestGuardedDeadVarAnalysis(Isolate* isolate) { void TestGuardedDeadVarAnalysis(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto(); JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// Note: having DisableGCMole with the same function as CauseGC
// normally doesn't make sense, but we want to test whether the guards
// are recognized by GCMole.
DisableGCMole no_gc_mole;
CauseGCRaw(raw_obj, isolate);
// Shouldn't cause warning.
raw_obj.Print();
}
void TestGuardedDeadVarAnalysis2(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// Note: having DisallowGarbageCollection with the same function as CauseGC // Note: having DisallowGarbageCollection with the same function as CauseGC
// normally doesn't make sense, but we want to test whether the gurads // normally doesn't make sense, but we want to test whether the guards
// are recognized by GCMole. // are recognized by GCMole.
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
CauseGCRaw(raw_obj, isolate); CauseGCRaw(raw_obj, isolate);
// Shouldn't cause warning. // Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
void TestGuardedAgainstSafepointDeadVarAnalysis(Isolate* isolate) { void TestGuardedAgainstSafepointDeadVarAnalysis(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto(); JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// Note: having DisableGCMole with the same function as CauseGC
// normally doesn't make sense, but we want to test whether the guards
// are recognized by GCMole.
DisableGCMole no_gc_mole;
Safepoint();
// Shouldn't cause warning.
raw_obj.Print();
}
void TestGuardedAgainstSafepointDeadVarAnalysis2(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// Note: having DisallowGarbageCollection with the same function as CauseGC // Note: having DisallowGarbageCollection with the same function as CauseGC
// normally doesn't make sense, but we want to test whether the gurads // normally doesn't make sense, but we want to test whether the guards
// are recognized by GCMole. // are recognized by GCMole.
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
Safepoint(); Safepoint();
// Shouldn't cause warning. // Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
void TestOnlyHeapGuardedDeadVarAnalysisInCompound(Isolate* isolate) { void TestGuardedAgainstSafepointDeadVarAnalysis3(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto(); JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// Note: having DisallowGarbageCollection with the same function as CauseGC
// normally doesn't make sense, but we want to test whether the guards
// are recognized by GCMole.
DisallowGarbageCollection no_gc;
Safepoint();
// Should cause warning.
raw_obj.Print();
{
DisableGCMole no_gc_mole;
// Shouldn't cause warning.
raw_obj.Print();
}
// Should cause warning.
raw_obj.Print();
}
void TestOnlyHeapGuardedDeadVarAnalysisInCompound(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// {DisallowHeapAccess} has a {DisallowHeapAllocation}, but no // {DisallowHeapAccess} has a {DisallowHeapAllocation}, but no
// {DisallowSafepoints}, so it could see objects move due to safepoints. // {DisallowSafepoints}, so it could see objects move due to safepoints.
DisallowHeapAccess no_gc; DisallowHeapAccess no_gc;
CauseGCRaw(raw_obj, isolate); CauseGCRaw(raw_obj, isolate);
// Should cause warning.
raw_obj.Print();
}
void TestOnlyHeapGuardedDeadVarAnalysisInCompound2(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
// {DisallowHeapAccess} has a {DisallowHeapAllocation}, but no
// {DisallowSafepoints}, so it could see objects move due to safepoints.
DisallowHeapAccess no_gc;
CauseGCRaw(raw_obj, isolate);
// Should cause warning.
raw_obj.Print();
DisableGCMole no_gc_mole;
// Should cause warning. // Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
void TestGuardedDeadVarAnalysisNested(JSObject raw_obj, Isolate* isolate) { void TestGuardedDeadVarAnalysisNested(JSObject raw_obj, Isolate* isolate) {
CauseGCRaw(raw_obj, isolate); CauseGCRaw(raw_obj, isolate);
// Should cause warning. // Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
void TestGuardedDeadVarAnalysisCaller(Isolate* isolate) { void TestGuardedDeadVarAnalysisCaller(Isolate* isolate) {
DisallowHeapAccess no_gc; DisableGCMole no_gc_mole;
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto(); JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
TestGuardedDeadVarAnalysisNested(raw_obj, isolate);
// Shouldn't cause warning.
raw_obj.Print();
}
void TestGuardedDeadVarAnalysisCaller2(Isolate* isolate) {
DisallowGarbageCollection no_gc;
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
TestGuardedDeadVarAnalysisNested(raw_obj, isolate);
// Should cause warning.
raw_obj.Print();
}
void TestGuardedDeadVarAnalysisCaller3(Isolate* isolate) {
DisallowHeapAccess no_gc;
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
TestGuardedDeadVarAnalysisNested(raw_obj, isolate); TestGuardedDeadVarAnalysisNested(raw_obj, isolate);
// Should cause warning.
raw_obj.Print();
}
// Shouldn't cause warning. void TestGuardedDeadVarAnalysisCaller4(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
TestGuardedDeadVarAnalysisNested(raw_obj, isolate);
// Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
...@@ -232,26 +307,47 @@ JSObject GuardedAllocation(Isolate* isolate) { ...@@ -232,26 +307,47 @@ JSObject GuardedAllocation(Isolate* isolate) {
return *isolate->factory()->NewJSObjectWithNullProto(); return *isolate->factory()->NewJSObjectWithNullProto();
} }
JSObject GuardedAllocation2(Isolate* isolate) {
DisableGCMole no_gc_mole;
return *isolate->factory()->NewJSObjectWithNullProto();
}
void TestNestedDeadVarAnalysis(Isolate* isolate) { void TestNestedDeadVarAnalysis(Isolate* isolate) {
JSObject raw_obj = GuardedAllocation(isolate); JSObject raw_obj = GuardedAllocation(isolate);
CauseGCRaw(raw_obj, isolate); CauseGCRaw(raw_obj, isolate);
// Should cause warning. // Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
void TestNestedDeadVarAnalysis2(Isolate* isolate) {
DisableGCMole no_gc_mole;
JSObject raw_obj = GuardedAllocation(isolate);
CauseGCRaw(raw_obj, isolate);
// Shouldn't cause warning.
raw_obj.Print();
}
// Test that putting a guard in the middle of the function doesn't // Test that putting a guard in the middle of the function doesn't
// mistakenly cover the whole scope of the raw variable. // mistakenly cover the whole scope of the raw variable.
void TestGuardedDeadVarAnalysisMidFunction(Isolate* isolate) { void TestGuardedDeadVarAnalysisMidFunction(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto(); JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
CauseGCRaw(raw_obj, isolate); CauseGCRaw(raw_obj, isolate);
// Guarding the rest of the function from triggering a GC. // Guarding the rest of the function from triggering a GC.
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
// Should cause warning. // Should cause warning.
raw_obj.Print(); raw_obj.Print();
} }
// Test that putting a guard in the middle of the function doesn't
// mistakenly cover the whole scope of the raw variable.
void TestGuardedDeadVarAnalysisMidFunction2(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
CauseGCRaw(raw_obj, isolate);
// Guarding the rest of the function from triggering a GC.
DisableGCMole no_gc_mole;
// Should cause warning.
raw_obj.Print();
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
2612625af89f79a6e8996fb12330397eed1e359d 165dd133e7dff1583a6b6611e3d5a8382bcd0893
...@@ -229,8 +229,9 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> { ...@@ -229,8 +229,9 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
virtual bool VisitDeclRefExpr(clang::DeclRefExpr* expr) { virtual bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
// If function mentions EXTERNAL VMState add artificial garbage collection // If function mentions EXTERNAL VMState add artificial garbage collection
// mark. // mark.
if (IsExternalVMState(expr->getDecl())) if (IsExternalVMState(expr->getDecl())) {
AddCallee("CollectGarbage", "CollectGarbage"); AddCallee("CollectGarbage", "CollectGarbage");
}
return true; return true;
} }
...@@ -685,13 +686,13 @@ class FunctionAnalyzer { ...@@ -685,13 +686,13 @@ class FunctionAnalyzer {
FunctionAnalyzer(clang::MangleContext* ctx, clang::CXXRecordDecl* object_decl, FunctionAnalyzer(clang::MangleContext* ctx, clang::CXXRecordDecl* object_decl,
clang::CXXRecordDecl* maybe_object_decl, clang::CXXRecordDecl* maybe_object_decl,
clang::CXXRecordDecl* smi_decl, clang::CXXRecordDecl* smi_decl,
clang::CXXRecordDecl* no_gc_decl, clang::CXXRecordDecl* no_gc_mole_decl,
clang::DiagnosticsEngine& d, clang::SourceManager& sm) clang::DiagnosticsEngine& d, clang::SourceManager& sm)
: ctx_(ctx), : ctx_(ctx),
object_decl_(object_decl), object_decl_(object_decl),
maybe_object_decl_(maybe_object_decl), maybe_object_decl_(maybe_object_decl),
smi_decl_(smi_decl), smi_decl_(smi_decl),
no_gc_decl_(no_gc_decl), no_gc_mole_decl_(no_gc_mole_decl),
d_(d), d_(d),
sm_(sm), sm_(sm),
block_(NULL) {} block_(NULL) {}
...@@ -1278,9 +1279,7 @@ class FunctionAnalyzer { ...@@ -1278,9 +1279,7 @@ class FunctionAnalyzer {
DECL_VISIT_STMT(ForStmt) { DECL_VISIT_STMT(ForStmt) {
Block block (VisitStmt(stmt->getInit(), env), this); Block block (VisitStmt(stmt->getInit(), env), this);
do { do {
block.Loop(stmt->getCond(), block.Loop(stmt->getCond(), stmt->getBody(), stmt->getInc());
stmt->getBody(),
stmt->getInc());
} while (block.changed()); } while (block.changed());
return block.out(); return block.out();
} }
...@@ -1335,25 +1334,15 @@ class FunctionAnalyzer { ...@@ -1335,25 +1334,15 @@ class FunctionAnalyzer {
const clang::CXXRecordDecl* GetDefinitionOrNull( const clang::CXXRecordDecl* GetDefinitionOrNull(
const clang::CXXRecordDecl* record) { const clang::CXXRecordDecl* record) {
if (record == NULL) { if (record == NULL) return NULL;
return NULL;
}
if (!InV8Namespace(record)) return NULL; if (!InV8Namespace(record)) return NULL;
if (!record->hasDefinition()) return NULL;
if (!record->hasDefinition()) {
return NULL;
}
return record->getDefinition(); return record->getDefinition();
} }
bool IsDerivedFromInternalPointer(const clang::CXXRecordDecl* record) { bool IsDerivedFromInternalPointer(const clang::CXXRecordDecl* record) {
const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record); const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record);
if (!definition) { if (!definition) return false;
return false;
}
bool result = (IsDerivedFrom(record, object_decl_) && bool result = (IsDerivedFrom(record, object_decl_) &&
!IsDerivedFrom(record, smi_decl_)) || !IsDerivedFrom(record, smi_decl_)) ||
IsDerivedFrom(record, maybe_object_decl_); IsDerivedFrom(record, maybe_object_decl_);
...@@ -1378,13 +1367,9 @@ class FunctionAnalyzer { ...@@ -1378,13 +1367,9 @@ class FunctionAnalyzer {
// such. For V8 that means Object and MaybeObject instances. // such. For V8 that means Object and MaybeObject instances.
bool RepresentsRawPointerType(clang::QualType qtype) { bool RepresentsRawPointerType(clang::QualType qtype) {
// Not yet assigned pointers can't get moved by the GC. // Not yet assigned pointers can't get moved by the GC.
if (qtype.isNull()) { if (qtype.isNull()) return false;
return false;
}
// nullptr can't get moved by the GC. // nullptr can't get moved by the GC.
if (qtype->isNullPtrType()) { if (qtype->isNullPtrType()) return false;
return false;
}
const clang::PointerType* pointer_type = const clang::PointerType* pointer_type =
llvm::dyn_cast_or_null<clang::PointerType>(qtype.getTypePtrOrNull()); llvm::dyn_cast_or_null<clang::PointerType>(qtype.getTypePtrOrNull());
...@@ -1396,21 +1381,15 @@ class FunctionAnalyzer { ...@@ -1396,21 +1381,15 @@ class FunctionAnalyzer {
} }
bool IsGCGuard(clang::QualType qtype) { bool IsGCGuard(clang::QualType qtype) {
if (qtype.isNull()) { if (!no_gc_mole_decl_) return false;
return false; if (qtype.isNull()) return false;
} if (qtype->isNullPtrType()) return false;
if (qtype->isNullPtrType()) {
return false;
}
const clang::CXXRecordDecl* record = qtype->getAsCXXRecordDecl(); const clang::CXXRecordDecl* record = qtype->getAsCXXRecordDecl();
const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record); const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record);
if (!definition) { if (!definition) return false;
return false; return no_gc_mole_decl_ == definition;
}
return no_gc_decl_ && IsDerivedFrom(definition, no_gc_decl_);
} }
Environment VisitDecl(clang::Decl* decl, Environment& env) { Environment VisitDecl(clang::Decl* decl, Environment& env) {
...@@ -1494,7 +1473,7 @@ class FunctionAnalyzer { ...@@ -1494,7 +1473,7 @@ class FunctionAnalyzer {
clang::CXXRecordDecl* object_decl_; clang::CXXRecordDecl* object_decl_;
clang::CXXRecordDecl* maybe_object_decl_; clang::CXXRecordDecl* maybe_object_decl_;
clang::CXXRecordDecl* smi_decl_; clang::CXXRecordDecl* smi_decl_;
clang::CXXRecordDecl* no_gc_decl_; clang::CXXRecordDecl* no_gc_mole_decl_;
clang::CXXRecordDecl* no_heap_access_decl_; clang::CXXRecordDecl* no_heap_access_decl_;
clang::DiagnosticsEngine& d_; clang::DiagnosticsEngine& d_;
...@@ -1559,44 +1538,38 @@ class ProblemsFinder : public clang::ASTConsumer, ...@@ -1559,44 +1538,38 @@ class ProblemsFinder : public clang::ASTConsumer,
} }
virtual void HandleTranslationUnit(clang::ASTContext &ctx) { virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
if (TranslationUnitIgnored()) { if (TranslationUnitIgnored()) return;
return;
}
Resolver r(ctx); Resolver r(ctx);
// It is a valid situation that no_gc_decl == NULL when the // It is a valid situation that no_gc_mole_decl == NULL when DisableGCMole
// DisallowGarbageCollection is not included and can't be resolved. // is not included and can't be resolved. This is gracefully handled in the
// This is gracefully handled in the FunctionAnalyzer later. // FunctionAnalyzer later.
clang::CXXRecordDecl* no_gc_decl = auto v8_internal = r.ResolveNamespace("v8").ResolveNamespace("internal");
r.ResolveNamespace("v8") clang::CXXRecordDecl* no_gc_mole_decl =
.ResolveNamespace("internal") v8_internal.ResolveTemplate("DisableGCMole");
.ResolveTemplate("DisallowGarbageCollection");
clang::CXXRecordDecl* object_decl = clang::CXXRecordDecl* object_decl =
r.ResolveNamespace("v8").ResolveNamespace("internal"). v8_internal.Resolve<clang::CXXRecordDecl>("Object");
Resolve<clang::CXXRecordDecl>("Object");
clang::CXXRecordDecl* maybe_object_decl = clang::CXXRecordDecl* maybe_object_decl =
r.ResolveNamespace("v8") v8_internal.Resolve<clang::CXXRecordDecl>("MaybeObject");
.ResolveNamespace("internal")
.Resolve<clang::CXXRecordDecl>("MaybeObject");
clang::CXXRecordDecl* smi_decl = clang::CXXRecordDecl* smi_decl =
r.ResolveNamespace("v8").ResolveNamespace("internal"). v8_internal.Resolve<clang::CXXRecordDecl>("Smi");
Resolve<clang::CXXRecordDecl>("Smi");
if (object_decl != NULL) object_decl = object_decl->getDefinition(); if (object_decl != NULL) object_decl = object_decl->getDefinition();
if (maybe_object_decl != NULL) if (maybe_object_decl != NULL) {
maybe_object_decl = maybe_object_decl->getDefinition(); maybe_object_decl = maybe_object_decl->getDefinition();
}
if (smi_decl != NULL) smi_decl = smi_decl->getDefinition(); if (smi_decl != NULL) smi_decl = smi_decl->getDefinition();
if (object_decl != NULL && smi_decl != NULL && maybe_object_decl != NULL) { if (object_decl != NULL && smi_decl != NULL && maybe_object_decl != NULL) {
function_analyzer_ = new FunctionAnalyzer( function_analyzer_ = new FunctionAnalyzer(
clang::ItaniumMangleContext::create(ctx, d_), object_decl, clang::ItaniumMangleContext::create(ctx, d_), object_decl,
maybe_object_decl, smi_decl, no_gc_decl, d_, sm_); maybe_object_decl, smi_decl, no_gc_mole_decl, d_, sm_);
TraverseDecl(ctx.getTranslationUnitDecl()); TraverseDecl(ctx.getTranslationUnitDecl());
} else { } else {
if (object_decl == NULL) { if (object_decl == NULL) {
......
...@@ -474,6 +474,7 @@ def CheckCorrectnessForArch(arch, for_test, flags, clang_bin_dir, ...@@ -474,6 +474,7 @@ def CheckCorrectnessForArch(arch, for_test, flags, clang_bin_dir,
def TestRun(flags, clang_bin_dir, clang_plugins_dir): def TestRun(flags, clang_bin_dir, clang_plugins_dir):
log("** Test Run")
errors_found, output = CheckCorrectnessForArch("x64", True, flags, errors_found, output = CheckCorrectnessForArch("x64", True, flags,
clang_bin_dir, clang_bin_dir,
clang_plugins_dir) clang_plugins_dir)
...@@ -489,7 +490,7 @@ def TestRun(flags, clang_bin_dir, clang_plugins_dir): ...@@ -489,7 +490,7 @@ def TestRun(flags, clang_bin_dir, clang_plugins_dir):
if output != expectations: if output != expectations:
log("** Output mismatch from running tests. Please run them manually.") log("** Output mismatch from running tests. Please run them manually.")
for line in difflib.context_diff( for line in difflib.unified_diff(
expectations.splitlines(), expectations.splitlines(),
output.splitlines(), output.splitlines(),
fromfile=filename, fromfile=filename,
...@@ -498,6 +499,7 @@ def TestRun(flags, clang_bin_dir, clang_plugins_dir): ...@@ -498,6 +499,7 @@ def TestRun(flags, clang_bin_dir, clang_plugins_dir):
): ):
log("{}", line) log("{}", line)
log("------")
log("--- Full output ---") log("--- Full output ---")
log(output) log(output)
log("------") log("------")
...@@ -526,7 +528,7 @@ def main(args): ...@@ -526,7 +528,7 @@ def main(args):
#:n't use parallel python runner. #:n't use parallel python runner.
"sequential": False, "sequential": False,
# Print commands to console before executing them. # Print commands to console before executing them.
"verbose": False, "verbose": True,
# Perform dead variable analysis. # Perform dead variable analysis.
"dead_vars": True, "dead_vars": True,
# Enable verbose tracing from the plugin itself. # Enable verbose tracing from the plugin itself.
......
...@@ -47,6 +47,21 @@ echo You can find a packaged version of gcmole here: ...@@ -47,6 +47,21 @@ echo You can find a packaged version of gcmole here:
echo echo
echo $(readlink -f "${PACKAGE_FILE}") echo $(readlink -f "${PACKAGE_FILE}")
echo echo
echo Upload the update package to the chrome infra:
echo
echo 'gsutil.py cp tools/gcmole/gcmole-tools.tar.gz gs://chrome-v8-gcmole/$(cat tools/gcmole/gcmole-tools.tar.gz.sha1)'
echo
echo Run bootstrap.sh in chroot if glibc versions mismatch with bots:
echo '# Create chroot'
echo 'mkdir -p $CHROOT_DIR'
echo 'sudo debootstrap $DEBIAN_VERSION $CHROOT_DIR'
echo 'sudo chroot $CHROOT_DIR apt install g++ cmake python git'
echo '# Mount ./depot_tools and ./v8 dirs in the chroot:'
echo 'sudo chroot $CHROOT_DIR mkdir /docs'
echo 'sudo mount --bind /path/to/workspace /docs'
echo '# Build gcmole'
echo "sudo chroot \$CHROOT_DIR bash -c 'PATH=/docs/depot_tools:\$PATH; /docs/v8/v8/tools/gcmole/bootstrap.sh'"
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 CLANG_BIN=\"tools/gcmole/gcmole-tools/bin\" python tools/gcmole/gcmole.py
......
...@@ -28,19 +28,46 @@ tools/gcmole/gcmole-test.cc:164:3: warning: Possibly dead variable. ...@@ -28,19 +28,46 @@ tools/gcmole/gcmole-test.cc:164:3: warning: Possibly dead variable.
tools/gcmole/gcmole-test.cc:172:3: warning: Possibly dead variable. tools/gcmole/gcmole-test.cc:172:3: warning: Possibly dead variable.
raw_obj.Print(); raw_obj.Print();
^ ^
tools/gcmole/gcmole-test.cc:210:3: warning: Possibly dead variable. tools/gcmole/gcmole-test.cc:198:3: warning: Possibly dead variable.
raw_obj.Print(); raw_obj.Print();
^ ^
tools/gcmole/gcmole-test.cc:217:3: warning: Possibly dead variable. tools/gcmole/gcmole-test.cc:224:3: warning: Possibly dead variable.
raw_obj.Print(); raw_obj.Print();
^ ^
tools/gcmole/gcmole-test.cc:227:3: warning: Possibly dead variable. tools/gcmole/gcmole-test.cc:235:3: warning: Possibly dead variable.
raw_obj.Print(); raw_obj.Print();
^ ^
tools/gcmole/gcmole-test.cc:240:3: warning: Possibly dead variable. tools/gcmole/gcmole-test.cc:242:3: warning: Possibly dead variable.
raw_obj.Print(); raw_obj.Print();
^ ^
tools/gcmole/gcmole-test.cc:253:3: warning: Possibly dead variable. tools/gcmole/gcmole-test.cc:252:3: warning: Possibly dead variable.
raw_obj.Print(); raw_obj.Print();
^ ^
15 warnings generated. tools/gcmole/gcmole-test.cc:262:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:265:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:271:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:287:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:295:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:302:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:319:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:338:3: warning: Possibly dead variable.
raw_obj.Print();
^
tools/gcmole/gcmole-test.cc:349:3: warning: Possibly dead variable.
raw_obj.Print();
^
24 warnings generated.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment