From 64e04c96af6a7b001ab747180bb440e6962d04b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= Date: Mon, 20 Aug 2018 14:14:25 +0200 Subject: [PATCH] [heap-profiler] Store locations in snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start storing locations in heap snapshot file. Initial support for closure, additional object types might be added in the future. Needed to show source code locations for objects in the DevTools heap snapshot viewer. Bug: chromium:854097 Change-Id: I12659373ce1adf67b55c6a10ea1d0465fcdb4a10 Reviewed-on: https://chromium-review.googlesource.com/1174257 Commit-Queue: Dominik Inführ Reviewed-by: Alexei Filippov Reviewed-by: Ulan Degenbaev Cr-Commit-Position: refs/heads/master@{#55245} --- src/profiler/heap-snapshot-generator-inl.h | 8 ++- src/profiler/heap-snapshot-generator.cc | 64 +++++++++++++++++++++- src/profiler/heap-snapshot-generator.h | 19 ++++++- test/cctest/test-heap-profiler.cc | 41 ++++++++++++++ 4 files changed, 126 insertions(+), 6 deletions(-) diff --git a/src/profiler/heap-snapshot-generator-inl.h b/src/profiler/heap-snapshot-generator-inl.h index a3e79be691..edf6559706 100644 --- a/src/profiler/heap-snapshot-generator-inl.h +++ b/src/profiler/heap-snapshot-generator-inl.h @@ -69,8 +69,12 @@ uint32_t HeapSnapshotJSONSerializer::StringHash(const void* string) { v8::internal::kZeroHashSeed); } -int HeapSnapshotJSONSerializer::entry_index(const HeapEntry* e) { - return e->index() * kNodeFieldsCount; +int HeapSnapshotJSONSerializer::to_node_index(const HeapEntry* e) { + return to_node_index(e->index()); +} + +int HeapSnapshotJSONSerializer::to_node_index(int entry_index) { + return entry_index * kNodeFieldsCount; } } // namespace internal diff --git a/src/profiler/heap-snapshot-generator.cc b/src/profiler/heap-snapshot-generator.cc index 112fce3340..06a6c62db6 100644 --- a/src/profiler/heap-snapshot-generator.cc +++ b/src/profiler/heap-snapshot-generator.cc @@ -249,6 +249,9 @@ HeapEntry* HeapSnapshot::AddGcSubrootEntry(Root root, SnapshotObjectId id) { return entry; } +void HeapSnapshot::AddLocation(int entry, int scriptId, int line, int col) { + locations_.emplace_back(entry, scriptId, line, col); +} HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, const char* name, @@ -611,6 +614,18 @@ HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) { return AddEntry(reinterpret_cast(ptr)); } +void V8HeapExplorer::ExtractLocation(int entry, HeapObject* object) { + if (!object->IsJSFunction()) return; + + JSFunction* func = JSFunction::cast(object); + if (!func->shared()->script()->IsScript()) return; + Script* script = Script::cast(func->shared()->script()); + int scriptId = script->id(); + int start = func->shared()->StartPosition(); + int line = script->GetLineNumber(start); + int col = script->GetColumnNumber(start); + snapshot_->AddLocation(entry, scriptId, line, col); +} HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) { if (object->IsJSFunction()) { @@ -1603,6 +1618,9 @@ bool V8HeapExplorer::IterateAndExtractReferences(SnapshotFiller* filler) { DCHECK(!visited_fields_[i]); } + // Extract location for specific object types + ExtractLocation(entry, obj); + if (!progress_->ProgressReport(false)) interrupted = true; } @@ -2631,6 +2649,11 @@ void HeapSnapshotJSONSerializer::SerializeImpl() { if (writer_->aborted()) return; writer_->AddString("],\n"); + writer_->AddString("\"locations\":["); + SerializeLocations(); + if (writer_->aborted()) return; + writer_->AddString("],\n"); + writer_->AddString("\"strings\":["); SerializeStrings(); if (writer_->aborted()) return; @@ -2710,7 +2733,7 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge, buffer[buffer_pos++] = ','; buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos); buffer[buffer_pos++] = ','; - buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos); + buffer_pos = utoa(to_node_index(edge->to()), buffer, buffer_pos); buffer[buffer_pos++] = '\n'; buffer[buffer_pos++] = '\0'; writer_->AddString(buffer.start()); @@ -2735,7 +2758,7 @@ void HeapSnapshotJSONSerializer::SerializeNode(const HeapEntry* entry) { + 6 + 1 + 1; EmbeddedVector buffer; int buffer_pos = 0; - if (entry_index(entry) != 0) { + if (to_node_index(entry) != 0) { buffer[buffer_pos++] = ','; } buffer_pos = utoa(entry->type(), buffer, buffer_pos); @@ -2768,6 +2791,8 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() { writer_->AddString("\"meta\":"); // The object describing node serialization layout. // We use a set of macros to improve readability. + +// clang-format off #define JSON_A(s) "[" s "]" #define JSON_O(s) "{" s "}" #define JSON_S(s) "\"" s "\"" @@ -2831,7 +2856,13 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() { JSON_S("children")) "," JSON_S("sample_fields") ":" JSON_A( JSON_S("timestamp_us") "," - JSON_S("last_assigned_id")))); + JSON_S("last_assigned_id")) "," + JSON_S("location_fields") ":" JSON_A( + JSON_S("object_index") "," + JSON_S("script_id") "," + JSON_S("line") "," + JSON_S("column")))); +// clang-format on #undef JSON_S #undef JSON_O #undef JSON_A @@ -3038,6 +3069,33 @@ void HeapSnapshotJSONSerializer::SerializeStrings() { } } +void HeapSnapshotJSONSerializer::SerializeLocation( + const SourceLocation& location) { + // The buffer needs space for 4 unsigned ints, 3 commas, \n and \0 + static const int kBufferSize = + MaxDecimalDigitsIn::kUnsigned * 4 + 3 + 2; + EmbeddedVector buffer; + int buffer_pos = 0; + buffer_pos = utoa(to_node_index(location.entry_index), buffer, buffer_pos); + buffer[buffer_pos++] = ','; + buffer_pos = utoa(location.scriptId, buffer, buffer_pos); + buffer[buffer_pos++] = ','; + buffer_pos = utoa(location.line, buffer, buffer_pos); + buffer[buffer_pos++] = ','; + buffer_pos = utoa(location.col, buffer, buffer_pos); + buffer[buffer_pos++] = '\n'; + buffer[buffer_pos++] = '\0'; + writer_->AddString(buffer.start()); +} + +void HeapSnapshotJSONSerializer::SerializeLocations() { + const std::vector& locations = snapshot_->locations(); + for (size_t i = 0; i < locations.size(); i++) { + if (i > 0) writer_->AddCharacter(','); + SerializeLocation(locations[i]); + if (writer_->aborted()) return; + } +} } // namespace internal } // namespace v8 diff --git a/src/profiler/heap-snapshot-generator.h b/src/profiler/heap-snapshot-generator.h index e8e5529074..c08ed90ed5 100644 --- a/src/profiler/heap-snapshot-generator.h +++ b/src/profiler/heap-snapshot-generator.h @@ -33,6 +33,16 @@ class JSCollection; class JSWeakCollection; class SnapshotFiller; +struct SourceLocation { + SourceLocation(int entry_index, int scriptId, int line, int col) + : entry_index(entry_index), scriptId(scriptId), line(line), col(col) {} + + const int entry_index; + const int scriptId; + const int line; + const int col; +}; + class HeapGraphEdge BASE_EMBEDDED { public: enum Type { @@ -173,11 +183,13 @@ class HeapSnapshot { std::vector& entries() { return entries_; } std::deque& edges() { return edges_; } std::deque& children() { return children_; } + const std::vector& locations() const { return locations_; } void RememberLastJSObjectId(); SnapshotObjectId max_snapshot_js_object_id() const { return max_snapshot_js_object_id_; } + void AddLocation(int entry, int scriptId, int line, int col); HeapEntry* AddEntry(HeapEntry::Type type, const char* name, SnapshotObjectId id, @@ -203,6 +215,7 @@ class HeapSnapshot { std::deque edges_; std::deque children_; std::vector sorted_entries_; + std::vector locations_; SnapshotObjectId max_snapshot_js_object_id_; friend class HeapSnapshotTester; @@ -363,6 +376,7 @@ class V8HeapExplorer : public HeapEntriesAllocator { const char* GetSystemEntryName(HeapObject* object); + void ExtractLocation(int entry, HeapObject* object); void ExtractReferences(int entry, HeapObject* obj); void ExtractJSGlobalProxyReferences(int entry, JSGlobalProxy* proxy); void ExtractJSObjectReferences(int entry, JSObject* js_obj); @@ -593,7 +607,8 @@ class HeapSnapshotJSONSerializer { V8_INLINE static uint32_t StringHash(const void* string); int GetStringId(const char* s); - V8_INLINE int entry_index(const HeapEntry* e); + V8_INLINE int to_node_index(const HeapEntry* e); + V8_INLINE int to_node_index(int entry_index); void SerializeEdge(HeapGraphEdge* edge, bool first_edge); void SerializeEdges(); void SerializeImpl(); @@ -606,6 +621,8 @@ class HeapSnapshotJSONSerializer { void SerializeSamples(); void SerializeString(const unsigned char* s); void SerializeStrings(); + void SerializeLocation(const SourceLocation& location); + void SerializeLocations(); static const int kEdgeFieldsCount; static const int kNodeFieldsCount; diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc index 033a827ab7..ac6acdd384 100644 --- a/test/cctest/test-heap-profiler.cc +++ b/test/cctest/test-heap-profiler.cc @@ -49,7 +49,9 @@ using i::AllocationTraceNode; using i::AllocationTraceTree; using i::AllocationTracker; using i::ArrayVector; +using i::SourceLocation; using i::Vector; +using v8::base::Optional; namespace { @@ -151,6 +153,23 @@ static const v8::HeapGraphNode* GetRootChild(const v8::HeapSnapshot* snapshot, return GetChildByName(snapshot->GetRoot(), name); } +static Optional GetLocation(const v8::HeapSnapshot* s, + const v8::HeapGraphNode* node) { + const i::HeapSnapshot* snapshot = reinterpret_cast(s); + const std::vector& locations = snapshot->locations(); + const int index = + const_cast(reinterpret_cast(node)) + ->index(); + + for (const auto& loc : locations) { + if (loc.entry_index == index) { + return Optional(loc); + } + } + + return Optional(); +} + static const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate, const v8::HeapGraphNode* node, v8::HeapGraphEdge::Type type, @@ -258,6 +277,27 @@ TEST(HeapSnapshot) { CHECK(det.has_C2); } +TEST(HeapSnapshotLocations) { + LocalContext env; + v8::HandleScope scope(env->GetIsolate()); + v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); + + CompileRun( + "function X(a) { return function() { return a; } }\n" + "var x = X(1);"); + const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); + CHECK(ValidateSnapshot(snapshot)); + + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* x = + GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x"); + CHECK(x); + + Optional location = GetLocation(snapshot, x); + CHECK(location); + CHECK_EQ(0, location->line); + CHECK_EQ(31, location->col); +} TEST(HeapSnapshotObjectSizes) { LocalContext env; @@ -1045,6 +1085,7 @@ TEST(HeapSnapshotJSONSerialization) { CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust()); CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust()); CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust()); + CHECK(parsed_snapshot->Has(env.local(), v8_str("locations")).FromJust()); CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust()); // Get node and edge "member" offsets.