Commit ff743410 authored by Dominik Inführ's avatar Dominik Inführ Committed by Commit Bot

[heap-profiler] Location for object's constructor

Add location information in heap snapshot for objects where the
constructor can be determined.

Bug: chromium:854097
Change-Id: Ieb2ab70a65809ecc9dfa0d73a33fa57add430465
Reviewed-on: https://chromium-review.googlesource.com/1179156
Commit-Queue: Dominik Inführ <dinfuehr@google.com>
Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarAlexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55387}
parent 13565ee2
......@@ -3815,8 +3815,9 @@ void HeapObject::RehashBasedOnMap(Isolate* isolate) {
}
}
// static
Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) {
namespace {
std::pair<MaybeHandle<JSFunction>, Handle<String>> GetConstructorHelper(
Handle<JSReceiver> receiver) {
Isolate* isolate = receiver->GetIsolate();
// If the object was instantiated simply with base == new.target, the
......@@ -3831,37 +3832,61 @@ Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) {
String* name = constructor->shared()->DebugName();
if (name->length() != 0 &&
!name->Equals(ReadOnlyRoots(isolate).Object_string())) {
return handle(name, isolate);
return std::make_pair(handle(constructor, isolate),
handle(name, isolate));
}
} else if (maybe_constructor->IsFunctionTemplateInfo()) {
FunctionTemplateInfo* info =
FunctionTemplateInfo::cast(maybe_constructor);
if (info->class_name()->IsString()) {
return handle(String::cast(info->class_name()), isolate);
return std::make_pair(
MaybeHandle<JSFunction>(),
handle(String::cast(info->class_name()), isolate));
}
}
}
Handle<Object> maybe_tag = JSReceiver::GetDataProperty(
receiver, isolate->factory()->to_string_tag_symbol());
if (maybe_tag->IsString()) return Handle<String>::cast(maybe_tag);
if (maybe_tag->IsString())
return std::make_pair(MaybeHandle<JSFunction>(),
Handle<String>::cast(maybe_tag));
PrototypeIterator iter(isolate, receiver);
if (iter.IsAtEnd()) return handle(receiver->class_name(), isolate);
if (iter.IsAtEnd()) {
return std::make_pair(MaybeHandle<JSFunction>(),
handle(receiver->class_name(), isolate));
}
Handle<JSReceiver> start = PrototypeIterator::GetCurrent<JSReceiver>(iter);
LookupIterator it(receiver, isolate->factory()->constructor_string(), start,
LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
Handle<Object> maybe_constructor = JSReceiver::GetDataProperty(&it);
Handle<String> result = isolate->factory()->Object_string();
if (maybe_constructor->IsJSFunction()) {
JSFunction* constructor = JSFunction::cast(*maybe_constructor);
String* name = constructor->shared()->DebugName();
if (name->length() > 0) result = handle(name, isolate);
if (name->length() != 0 &&
!name->Equals(ReadOnlyRoots(isolate).Object_string())) {
return std::make_pair(handle(constructor, isolate),
handle(name, isolate));
}
}
return result.is_identical_to(isolate->factory()->Object_string())
? handle(receiver->class_name(), isolate)
: result;
return std::make_pair(MaybeHandle<JSFunction>(),
handle(receiver->class_name(), isolate));
}
} // anonymous namespace
// static
MaybeHandle<JSFunction> JSReceiver::GetConstructor(
Handle<JSReceiver> receiver) {
return GetConstructorHelper(receiver).first;
}
// static
Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) {
return GetConstructorHelper(receiver).second;
}
Handle<Context> JSReceiver::GetCreationContext() {
......
......@@ -2147,6 +2147,10 @@ class JSReceiver : public HeapObject, public NeverReadOnlySpaceObject {
// Returns the class name ([[Class]] property in the specification).
V8_EXPORT_PRIVATE String* class_name();
// Returns the constructor (the function that was used to instantiate the
// object).
static MaybeHandle<JSFunction> GetConstructor(Handle<JSReceiver> receiver);
// Returns the constructor name (the name (possibly, inferred name) of the
// function that was used to instantiate the object).
static Handle<String> GetConstructorName(Handle<JSReceiver> receiver);
......
......@@ -622,6 +622,13 @@ void V8HeapExplorer::ExtractLocation(int entry, HeapObject* object) {
} else if (object->IsJSGeneratorObject()) {
JSGeneratorObject* gen = JSGeneratorObject::cast(object);
ExtractLocationForJSFunction(entry, gen->function());
} else if (object->IsJSObject()) {
JSObject* obj = JSObject::cast(object);
JSFunction* maybe_constructor = GetConstructor(obj);
if (maybe_constructor)
ExtractLocationForJSFunction(entry, maybe_constructor);
}
}
......@@ -1537,6 +1544,17 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) {
}
}
JSFunction* V8HeapExplorer::GetConstructor(JSReceiver* receiver) {
Isolate* isolate = receiver->GetIsolate();
DisallowHeapAllocation no_gc;
HandleScope scope(isolate);
MaybeHandle<JSFunction> maybe_constructor =
JSReceiver::GetConstructor(handle(receiver, isolate));
if (maybe_constructor.is_null()) return nullptr;
return *maybe_constructor.ToHandleChecked();
}
String* V8HeapExplorer::GetConstructorName(JSObject* object) {
Isolate* isolate = object->GetIsolate();
......
......@@ -364,6 +364,7 @@ class V8HeapExplorer : public HeapEntriesAllocator {
const char* name,
size_t size);
static JSFunction* GetConstructor(JSReceiver* receiver);
static String* GetConstructorName(JSObject* object);
private:
......
......@@ -285,8 +285,10 @@ TEST(HeapSnapshotLocations) {
CompileRun(
"function X(a) { return function() { return a; } }\n"
"function* getid() { yield 1; }\n"
"class A {}\n"
"var x = X(1);\n"
"var g = getid();");
"var g = getid();\n"
"var o = new A();");
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
CHECK(ValidateSnapshot(snapshot));
......@@ -308,6 +310,15 @@ TEST(HeapSnapshotLocations) {
CHECK(g_loc);
CHECK_EQ(1, g_loc->line);
CHECK_EQ(15, g_loc->col);
const v8::HeapGraphNode* o =
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "o");
CHECK(x);
Optional<SourceLocation> o_loc = GetLocation(snapshot, o);
CHECK(o_loc);
CHECK_EQ(2, o_loc->line);
CHECK_EQ(0, o_loc->col);
}
TEST(HeapSnapshotObjectSizes) {
......@@ -1921,6 +1932,63 @@ static int StringCmp(const char* ref, i::String* act) {
return result;
}
TEST(GetConstructor) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
CompileRun(
"function Constructor1() {};\n"
"var obj1 = new Constructor1();\n"
"var Constructor2 = function() {};\n"
"var obj2 = new Constructor2();\n"
"var obj3 = {};\n"
"obj3.__proto__ = { constructor: function Constructor3() {} };\n"
"var obj4 = {};\n"
"// Slow properties\n"
"for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
"obj4.__proto__ = { constructor: function Constructor4() {} };\n"
"var obj5 = {};\n"
"var obj6 = {};\n"
"obj6.constructor = 6;");
v8::Local<v8::Object> js_global =
env->Global()->GetPrototype().As<v8::Object>();
v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
.ToLocalChecked()
.As<v8::Object>();
i::Handle<i::JSObject> js_obj1 =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
CHECK(i::V8HeapExplorer::GetConstructor(*js_obj1));
v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
.ToLocalChecked()
.As<v8::Object>();
i::Handle<i::JSObject> js_obj2 =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
CHECK(i::V8HeapExplorer::GetConstructor(*js_obj2));
v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
.ToLocalChecked()
.As<v8::Object>();
i::Handle<i::JSObject> js_obj3 =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
CHECK(i::V8HeapExplorer::GetConstructor(*js_obj3));
v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
.ToLocalChecked()
.As<v8::Object>();
i::Handle<i::JSObject> js_obj4 =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
CHECK(i::V8HeapExplorer::GetConstructor(*js_obj4));
v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
.ToLocalChecked()
.As<v8::Object>();
i::Handle<i::JSObject> js_obj5 =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj5));
v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
.ToLocalChecked()
.As<v8::Object>();
i::Handle<i::JSObject> js_obj6 =
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj6));
}
TEST(GetConstructorName) {
LocalContext env;
......
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