summaryrefslogtreecommitdiff
path: root/deps/v8/tools/gcmole
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/tools/gcmole')
-rw-r--r--deps/v8/tools/gcmole/BUILD.gn4
-rw-r--r--deps/v8/tools/gcmole/GCMOLE.gn6
-rw-r--r--deps/v8/tools/gcmole/README6
-rw-r--r--deps/v8/tools/gcmole/gcmole-test.cc69
-rw-r--r--deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha12
-rw-r--r--deps/v8/tools/gcmole/gcmole.cc91
-rw-r--r--deps/v8/tools/gcmole/gcmole.lua69
-rw-r--r--deps/v8/tools/gcmole/suspects.whitelist4
-rw-r--r--deps/v8/tools/gcmole/test-expectations.txt20
9 files changed, 239 insertions, 32 deletions
diff --git a/deps/v8/tools/gcmole/BUILD.gn b/deps/v8/tools/gcmole/BUILD.gn
index 0434a64ff5..51b9ef527f 100644
--- a/deps/v8/tools/gcmole/BUILD.gn
+++ b/deps/v8/tools/gcmole/BUILD.gn
@@ -9,12 +9,16 @@ group("v8_run_gcmole") {
data = [
"gccause.lua",
+ "GCMOLE.gn",
"gcmole.lua",
"gcmole-tools/",
"parallel.py",
"run-gcmole.py",
+ "suspects.whitelist",
+ "test-expectations.txt",
# The following contains all relevant source and build files.
+ "../debug_helper/debug-helper.h",
"../../BUILD.gn",
"../../base/",
"../../include/",
diff --git a/deps/v8/tools/gcmole/GCMOLE.gn b/deps/v8/tools/gcmole/GCMOLE.gn
new file mode 100644
index 0000000000..62da0a084b
--- /dev/null
+++ b/deps/v8/tools/gcmole/GCMOLE.gn
@@ -0,0 +1,6 @@
+action("gcmole") {
+ sources = [
+ ### gcmole(all) ###
+ "tools/gcmole/gcmole-test.cc",
+ ]
+}
diff --git a/deps/v8/tools/gcmole/README b/deps/v8/tools/gcmole/README
index 578ea56219..48785b871a 100644
--- a/deps/v8/tools/gcmole/README
+++ b/deps/v8/tools/gcmole/README
@@ -71,6 +71,12 @@ 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
diff --git a/deps/v8/tools/gcmole/gcmole-test.cc b/deps/v8/tools/gcmole/gcmole-test.cc
index c00c6e5539..3af1bac3b5 100644
--- a/deps/v8/tools/gcmole/gcmole-test.cc
+++ b/deps/v8/tools/gcmole/gcmole-test.cc
@@ -5,26 +5,43 @@
#include "src/execution/isolate.h"
#include "src/handles/handles-inl.h"
#include "src/handles/handles.h"
+#include "src/objects/foreign-inl.h"
+#include "src/objects/managed.h"
#include "src/objects/maybe-object.h"
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
+// ------- Test simple argument evaluation order problems ---------
+
Handle<Object> CauseGC(Handle<Object> obj, Isolate* isolate) {
isolate->heap()->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kTesting);
return obj;
}
+Object CauseGCRaw(Object obj, Isolate* isolate) {
+ isolate->heap()->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kTesting);
+
+ return obj;
+}
+
+Managed<Smi> CauseGCManaged(int i, Isolate* isolate) {
+ isolate->heap()->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kTesting);
+
+ return Managed<Smi>::cast(Smi::FromInt(i));
+}
+
void TwoArgumentsFunction(Object a, Object b) {
- a->Print();
- b->Print();
+ a.Print();
+ b.Print();
}
void TestTwoArguments(Isolate* isolate) {
Handle<JSObject> obj1 = isolate->factory()->NewJSObjectWithNullProto();
Handle<JSObject> obj2 = isolate->factory()->NewJSObjectWithNullProto();
+ // Should cause warning.
TwoArgumentsFunction(*CauseGC(obj1, isolate), *CauseGC(obj2, isolate));
}
@@ -36,13 +53,16 @@ void TwoSizeTArgumentsFunction(size_t a, size_t b) {
void TestTwoSizeTArguments(Isolate* isolate) {
Handle<JSObject> obj1 = isolate->factory()->NewJSObjectWithNullProto();
Handle<JSObject> obj2 = isolate->factory()->NewJSObjectWithNullProto();
+ // Should cause warning.
TwoSizeTArgumentsFunction(sizeof(*CauseGC(obj1, isolate)),
sizeof(*CauseGC(obj2, isolate)));
}
+// --------- Test problems with method arguments ----------
+
class SomeObject : public Object {
public:
- void Method(Object a) { a->Print(); }
+ void Method(Object a) { a.Print(); }
SomeObject& operator=(const Object& b) {
this->Print();
@@ -58,14 +78,57 @@ void TestMethodCall(Isolate* isolate) {
SomeObject obj;
Handle<SomeObject> so = handle(obj, isolate);
Handle<JSObject> obj1 = isolate->factory()->NewJSObjectWithNullProto();
+ // Should cause warning.
so->Method(*CauseGC(obj1, isolate));
+ // Should cause warning.
+ so->Method(CauseGCRaw(*obj1, isolate));
}
void TestOperatorCall(Isolate* isolate) {
SomeObject obj;
Handle<JSObject> obj1 = isolate->factory()->NewJSObjectWithNullProto();
+ // Should not cause warning.
obj = *CauseGC(obj1, isolate);
}
+// --------- Test for templated sub-classes of Object ----------
+
+void TestFollowingTemplates(Isolate* isolate) {
+ // Should cause warning.
+ CauseGCManaged(42, isolate);
+}
+
+// --------- Test for correctly resolving virtual methods ----------
+
+class BaseObject {
+ public:
+ virtual Handle<Object> VirtualCauseGC(Handle<Object> obj, Isolate* isolate) {
+ return obj;
+ }
+};
+
+class DerivedObject : public BaseObject {
+ public:
+ Handle<Object> VirtualCauseGC(Handle<Object> obj, Isolate* isolate) override {
+ isolate->heap()->CollectGarbage(OLD_SPACE,
+ GarbageCollectionReason::kTesting);
+
+ return obj;
+ }
+};
+
+void TestFollowingVirtualFunctions(Isolate* isolate) {
+ DerivedObject derived;
+ BaseObject* base = &derived;
+ Handle<JSObject> obj1 = isolate->factory()->NewJSObjectWithNullProto();
+
+ SomeObject so;
+ Handle<SomeObject> so_handle = handle(so, isolate);
+ // Should cause warning.
+ so_handle->Method(*derived.VirtualCauseGC(obj1, isolate));
+ // Should cause warning.
+ so_handle->Method(*base->VirtualCauseGC(obj1, isolate));
+}
+
} // namespace internal
} // namespace v8
diff --git a/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1 b/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1
index 718e967e3b..8f7876fa51 100644
--- a/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1
+++ b/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1
@@ -1 +1 @@
-3d4ba1759c3d5bc7e98c466d24fa0c43f186ba79 \ No newline at end of file
+d2f949820bf1ee7343a7b5f46987a3657aaea2e9 \ No newline at end of file
diff --git a/deps/v8/tools/gcmole/gcmole.cc b/deps/v8/tools/gcmole/gcmole.cc
index 6631583478..806100d381 100644
--- a/deps/v8/tools/gcmole/gcmole.cc
+++ b/deps/v8/tools/gcmole/gcmole.cc
@@ -47,6 +47,7 @@ namespace {
typedef std::string MangledName;
typedef std::set<MangledName> CalleesSet;
+typedef std::map<MangledName, MangledName> CalleesMap;
static bool GetMangledName(clang::MangleContext* ctx,
const clang::NamedDecl* decl,
@@ -138,14 +139,16 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
virtual bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
// If function mentions EXTERNAL VMState add artificial garbage collection
// mark.
- if (IsExternalVMState(expr->getDecl())) AddCallee("CollectGarbage");
+ if (IsExternalVMState(expr->getDecl()))
+ AddCallee("CollectGarbage", "CollectGarbage");
return true;
}
void AnalyzeFunction(const clang::FunctionDecl* f) {
MangledName name;
if (InV8Namespace(f) && GetMangledName(ctx_, f, &name)) {
- AddCallee(name);
+ const std::string& function = f->getNameAsString();
+ AddCallee(name, function);
const clang::FunctionDecl* body = NULL;
if (f->hasBody(body) && !Analyzed(name)) {
@@ -176,21 +179,22 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
scopes_.pop();
}
- void AddCallee(const MangledName& name) {
+ void AddCallee(const MangledName& name, const MangledName& function) {
if (!scopes_.empty()) scopes_.top()->insert(name);
+ mangled_to_function_[name] = function;
}
void PrintCallGraph() {
for (Callgraph::const_iterator i = callgraph_.begin(), e = callgraph_.end();
i != e;
++i) {
- std::cout << i->first << "\n";
+ std::cout << i->first << "," << mangled_to_function_[i->first] << "\n";
CalleesSet* callees = i->second;
for (CalleesSet::const_iterator j = callees->begin(), e = callees->end();
j != e;
++j) {
- std::cout << "\t" << *j << "\n";
+ std::cout << "\t" << *j << "," << mangled_to_function_[*j] << "\n";
}
}
}
@@ -200,6 +204,7 @@ class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
std::stack<CalleesSet* > scopes_;
Callgraph callgraph_;
+ CalleesMap mangled_to_function_;
};
@@ -234,23 +239,40 @@ class FunctionDeclarationFinder
CalleesPrinter* callees_printer_;
};
-
-static bool loaded = false;
+static bool gc_suspects_loaded = false;
static CalleesSet gc_suspects;
-
+static CalleesSet gc_functions;
+static bool whitelist_loaded = false;
+static CalleesSet suspects_whitelist;
static void LoadGCSuspects() {
- if (loaded) return;
+ if (gc_suspects_loaded) return;
std::ifstream fin("gcsuspects");
- std::string s;
+ std::string mangled, function;
- while (fin >> s) gc_suspects.insert(s);
+ while (!fin.eof()) {
+ std::getline(fin, mangled, ',');
+ gc_suspects.insert(mangled);
+ std::getline(fin, function);
+ gc_functions.insert(function);
+ }
- loaded = true;
+ gc_suspects_loaded = true;
}
+static void LoadSuspectsWhitelist() {
+ if (whitelist_loaded) return;
+
+ std::ifstream fin("tools/gcmole/suspects.whitelist");
+ std::string s;
+
+ while (fin >> s) suspects_whitelist.insert(s);
+ whitelist_loaded = true;
+}
+
+// Looks for exact match of the mangled name
static bool KnownToCauseGC(clang::MangleContext* ctx,
const clang::FunctionDecl* decl) {
LoadGCSuspects();
@@ -265,6 +287,25 @@ static bool KnownToCauseGC(clang::MangleContext* ctx,
return false;
}
+// Looks for partial match of only the function name
+static bool SuspectedToCauseGC(clang::MangleContext* ctx,
+ const clang::FunctionDecl* decl) {
+ LoadGCSuspects();
+
+ if (!InV8Namespace(decl)) return false;
+
+ LoadSuspectsWhitelist();
+ if (suspects_whitelist.find(decl->getNameAsString()) !=
+ suspects_whitelist.end()) {
+ return false;
+ }
+
+ if (gc_functions.find(decl->getNameAsString()) != gc_functions.end()) {
+ return true;
+ }
+
+ return false;
+}
static const int kNoEffect = 0;
static const int kCausesGC = 1;
@@ -910,8 +951,30 @@ class FunctionAnalyzer {
RepresentsRawPointerType(call->getType()));
clang::FunctionDecl* callee = call->getDirectCallee();
- if ((callee != NULL) && KnownToCauseGC(ctx_, callee)) {
- out.setGC();
+ if (callee != NULL) {
+ if (KnownToCauseGC(ctx_, callee)) {
+ out.setGC();
+ }
+
+ clang::CXXMethodDecl* method =
+ llvm::dyn_cast_or_null<clang::CXXMethodDecl>(callee);
+ if (method != NULL && method->isVirtual()) {
+ clang::CXXMemberCallExpr* memcall =
+ llvm::dyn_cast_or_null<clang::CXXMemberCallExpr>(call);
+ if (memcall != NULL) {
+ clang::CXXMethodDecl* target = method->getDevirtualizedMethod(
+ memcall->getImplicitObjectArgument(), false);
+ if (target != NULL) {
+ if (KnownToCauseGC(ctx_, target)) {
+ out.setGC();
+ }
+ } else {
+ if (SuspectedToCauseGC(ctx_, method)) {
+ out.setGC();
+ }
+ }
+ }
+ }
}
return out;
diff --git a/deps/v8/tools/gcmole/gcmole.lua b/deps/v8/tools/gcmole/gcmole.lua
index ae17fdc5f6..6758973457 100644
--- a/deps/v8/tools/gcmole/gcmole.lua
+++ b/deps/v8/tools/gcmole/gcmole.lua
@@ -183,12 +183,19 @@ end
-------------------------------------------------------------------------------
-local function ParseGNFile()
+local function ParseGNFile(for_test)
local result = {}
- local gn_files = {
- { "BUILD.gn", '"([^"]-%.cc)"', "" },
- { "test/cctest/BUILD.gn", '"(test-[^"]-%.cc)"', "test/cctest/" }
- }
+ local gn_files
+ if for_test then
+ gn_files = {
+ { "tools/gcmole/GCMOLE.gn", '"([^"]-%.cc)"', "" }
+ }
+ else
+ gn_files = {
+ { "BUILD.gn", '"([^"]-%.cc)"', "" },
+ { "test/cctest/BUILD.gn", '"(test-[^"]-%.cc)"', "test/cctest/" }
+ }
+ end
for i = 1, #gn_files do
local filename = gn_files[i][1]
@@ -231,7 +238,8 @@ local function BuildFileList(sources, props)
end
-local gn_sources = ParseGNFile()
+local gn_sources = ParseGNFile(false)
+local gn_test_sources = ParseGNFile(true)
local function FilesForArch(arch)
return BuildFileList(gn_sources, { os = 'linux',
@@ -240,6 +248,13 @@ local function FilesForArch(arch)
simulator = ''})
end
+local function FilesForTest(arch)
+ return BuildFileList(gn_test_sources, { os = 'linux',
+ arch = arch,
+ mode = 'debug',
+ simulator = ''})
+end
+
local mtConfig = {}
mtConfig.__index = mtConfig
@@ -393,8 +408,13 @@ end
--------------------------------------------------------------------------------
-- Analysis
-local function CheckCorrectnessForArch(arch)
- local files = FilesForArch(arch)
+local function CheckCorrectnessForArch(arch, for_test)
+ local files
+ if for_test then
+ files = FilesForTest(arch)
+ else
+ files = FilesForArch(arch)
+ end
local cfg = ARCHITECTURES[arch]
if not FLAGS.reuse_gcsuspects then
@@ -403,6 +423,7 @@ local function CheckCorrectnessForArch(arch)
local processed_files = 0
local errors_found = false
+ local output = ""
local function SearchForErrors(filename, lines)
processed_files = processed_files + 1
for l in lines do
@@ -410,7 +431,11 @@ local function CheckCorrectnessForArch(arch)
l:match "^[^:]+:%d+:%d+:" or
l:match "error" or
l:match "warning"
- print(l)
+ if for_test then
+ output = output.."\n"..l
+ else
+ print(l)
+ end
end
end
@@ -427,18 +452,34 @@ local function CheckCorrectnessForArch(arch)
processed_files,
errors_found and "Errors found" or "No errors found")
- return errors_found
+ return errors_found, output
end
-local function SafeCheckCorrectnessForArch(arch)
- local status, errors = pcall(CheckCorrectnessForArch, arch)
+local function SafeCheckCorrectnessForArch(arch, for_test)
+ local status, errors, output = pcall(CheckCorrectnessForArch, arch, for_test)
if not status then
print(string.format("There was an error: %s", errors))
errors = true
end
- return errors
+ return errors, output
+end
+
+local function TestRun()
+ local errors, output = SafeCheckCorrectnessForArch('x64', true)
+
+ local filename = "tools/gcmole/test-expectations.txt"
+ local exp_file = assert(io.open(filename), "failed to open test expectations file")
+ local expectations = exp_file:read('*all')
+
+ if output ~= expectations then
+ log("** Output mismatch from running tests. Please run them manually.")
+ else
+ log("** Tests ran successfully")
+ end
end
+TestRun()
+
local errors = false
for _, arch in ipairs(ARCHS) do
@@ -446,7 +487,7 @@ for _, arch in ipairs(ARCHS) do
error ("Unknown arch: " .. arch)
end
- errors = SafeCheckCorrectnessForArch(arch, report) or errors
+ errors = SafeCheckCorrectnessForArch(arch, false) or errors
end
os.exit(errors and 1 or 0)
diff --git a/deps/v8/tools/gcmole/suspects.whitelist b/deps/v8/tools/gcmole/suspects.whitelist
new file mode 100644
index 0000000000..01db7401f2
--- /dev/null
+++ b/deps/v8/tools/gcmole/suspects.whitelist
@@ -0,0 +1,4 @@
+IsConstructor
+IsEval
+IsAsync
+IsPromiseAll
diff --git a/deps/v8/tools/gcmole/test-expectations.txt b/deps/v8/tools/gcmole/test-expectations.txt
new file mode 100644
index 0000000000..36a026a12e
--- /dev/null
+++ b/deps/v8/tools/gcmole/test-expectations.txt
@@ -0,0 +1,20 @@
+
+tools/gcmole/gcmole-test.cc:45:3: warning: Possible problem with evaluation order.
+ TwoArgumentsFunction(*CauseGC(obj1, isolate), *CauseGC(obj2, isolate));
+ ^
+tools/gcmole/gcmole-test.cc:57:3: warning: Possible problem with evaluation order.
+ TwoSizeTArgumentsFunction(sizeof(*CauseGC(obj1, isolate)),
+ ^
+tools/gcmole/gcmole-test.cc:82:7: warning: Possible problem with evaluation order.
+ so->Method(*CauseGC(obj1, isolate));
+ ^
+tools/gcmole/gcmole-test.cc:84:7: warning: Possible problem with evaluation order.
+ so->Method(CauseGCRaw(*obj1, isolate));
+ ^
+tools/gcmole/gcmole-test.cc:128:14: warning: Possible problem with evaluation order.
+ so_handle->Method(*derived.VirtualCauseGC(obj1, isolate));
+ ^
+tools/gcmole/gcmole-test.cc:130:14: warning: Possible problem with evaluation order.
+ so_handle->Method(*base->VirtualCauseGC(obj1, isolate));
+ ^
+6 warnings generated.