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

[heap-profiler] Store locations in snapshot

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 <dinfuehr@google.com>
Reviewed-by: 's avatarAlexei Filippov <alph@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55245}
parent fe5e07d7
...@@ -69,8 +69,12 @@ uint32_t HeapSnapshotJSONSerializer::StringHash(const void* string) { ...@@ -69,8 +69,12 @@ uint32_t HeapSnapshotJSONSerializer::StringHash(const void* string) {
v8::internal::kZeroHashSeed); v8::internal::kZeroHashSeed);
} }
int HeapSnapshotJSONSerializer::entry_index(const HeapEntry* e) { int HeapSnapshotJSONSerializer::to_node_index(const HeapEntry* e) {
return e->index() * kNodeFieldsCount; return to_node_index(e->index());
}
int HeapSnapshotJSONSerializer::to_node_index(int entry_index) {
return entry_index * kNodeFieldsCount;
} }
} // namespace internal } // namespace internal
......
...@@ -249,6 +249,9 @@ HeapEntry* HeapSnapshot::AddGcSubrootEntry(Root root, SnapshotObjectId id) { ...@@ -249,6 +249,9 @@ HeapEntry* HeapSnapshot::AddGcSubrootEntry(Root root, SnapshotObjectId id) {
return entry; 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, HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name, const char* name,
...@@ -611,6 +614,18 @@ HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) { ...@@ -611,6 +614,18 @@ HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
return AddEntry(reinterpret_cast<HeapObject*>(ptr)); return AddEntry(reinterpret_cast<HeapObject*>(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) { HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
if (object->IsJSFunction()) { if (object->IsJSFunction()) {
...@@ -1603,6 +1618,9 @@ bool V8HeapExplorer::IterateAndExtractReferences(SnapshotFiller* filler) { ...@@ -1603,6 +1618,9 @@ bool V8HeapExplorer::IterateAndExtractReferences(SnapshotFiller* filler) {
DCHECK(!visited_fields_[i]); DCHECK(!visited_fields_[i]);
} }
// Extract location for specific object types
ExtractLocation(entry, obj);
if (!progress_->ProgressReport(false)) interrupted = true; if (!progress_->ProgressReport(false)) interrupted = true;
} }
...@@ -2631,6 +2649,11 @@ void HeapSnapshotJSONSerializer::SerializeImpl() { ...@@ -2631,6 +2649,11 @@ void HeapSnapshotJSONSerializer::SerializeImpl() {
if (writer_->aborted()) return; if (writer_->aborted()) return;
writer_->AddString("],\n"); writer_->AddString("],\n");
writer_->AddString("\"locations\":[");
SerializeLocations();
if (writer_->aborted()) return;
writer_->AddString("],\n");
writer_->AddString("\"strings\":["); writer_->AddString("\"strings\":[");
SerializeStrings(); SerializeStrings();
if (writer_->aborted()) return; if (writer_->aborted()) return;
...@@ -2710,7 +2733,7 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge, ...@@ -2710,7 +2733,7 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
buffer[buffer_pos++] = ','; buffer[buffer_pos++] = ',';
buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos); buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
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++] = '\n';
buffer[buffer_pos++] = '\0'; buffer[buffer_pos++] = '\0';
writer_->AddString(buffer.start()); writer_->AddString(buffer.start());
...@@ -2735,7 +2758,7 @@ void HeapSnapshotJSONSerializer::SerializeNode(const HeapEntry* entry) { ...@@ -2735,7 +2758,7 @@ void HeapSnapshotJSONSerializer::SerializeNode(const HeapEntry* entry) {
+ 6 + 1 + 1; + 6 + 1 + 1;
EmbeddedVector<char, kBufferSize> buffer; EmbeddedVector<char, kBufferSize> buffer;
int buffer_pos = 0; int buffer_pos = 0;
if (entry_index(entry) != 0) { if (to_node_index(entry) != 0) {
buffer[buffer_pos++] = ','; buffer[buffer_pos++] = ',';
} }
buffer_pos = utoa(entry->type(), buffer, buffer_pos); buffer_pos = utoa(entry->type(), buffer, buffer_pos);
...@@ -2768,6 +2791,8 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() { ...@@ -2768,6 +2791,8 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() {
writer_->AddString("\"meta\":"); writer_->AddString("\"meta\":");
// The object describing node serialization layout. // The object describing node serialization layout.
// We use a set of macros to improve readability. // We use a set of macros to improve readability.
// clang-format off
#define JSON_A(s) "[" s "]" #define JSON_A(s) "[" s "]"
#define JSON_O(s) "{" s "}" #define JSON_O(s) "{" s "}"
#define JSON_S(s) "\"" s "\"" #define JSON_S(s) "\"" s "\""
...@@ -2831,7 +2856,13 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() { ...@@ -2831,7 +2856,13 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() {
JSON_S("children")) "," JSON_S("children")) ","
JSON_S("sample_fields") ":" JSON_A( JSON_S("sample_fields") ":" JSON_A(
JSON_S("timestamp_us") "," 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_S
#undef JSON_O #undef JSON_O
#undef JSON_A #undef JSON_A
...@@ -3038,6 +3069,33 @@ void HeapSnapshotJSONSerializer::SerializeStrings() { ...@@ -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<sizeof(unsigned)>::kUnsigned * 4 + 3 + 2;
EmbeddedVector<char, kBufferSize> 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<SourceLocation>& 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 internal
} // namespace v8 } // namespace v8
...@@ -33,6 +33,16 @@ class JSCollection; ...@@ -33,6 +33,16 @@ class JSCollection;
class JSWeakCollection; class JSWeakCollection;
class SnapshotFiller; 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 { class HeapGraphEdge BASE_EMBEDDED {
public: public:
enum Type { enum Type {
...@@ -173,11 +183,13 @@ class HeapSnapshot { ...@@ -173,11 +183,13 @@ class HeapSnapshot {
std::vector<HeapEntry>& entries() { return entries_; } std::vector<HeapEntry>& entries() { return entries_; }
std::deque<HeapGraphEdge>& edges() { return edges_; } std::deque<HeapGraphEdge>& edges() { return edges_; }
std::deque<HeapGraphEdge*>& children() { return children_; } std::deque<HeapGraphEdge*>& children() { return children_; }
const std::vector<SourceLocation>& locations() const { return locations_; }
void RememberLastJSObjectId(); void RememberLastJSObjectId();
SnapshotObjectId max_snapshot_js_object_id() const { SnapshotObjectId max_snapshot_js_object_id() const {
return max_snapshot_js_object_id_; return max_snapshot_js_object_id_;
} }
void AddLocation(int entry, int scriptId, int line, int col);
HeapEntry* AddEntry(HeapEntry::Type type, HeapEntry* AddEntry(HeapEntry::Type type,
const char* name, const char* name,
SnapshotObjectId id, SnapshotObjectId id,
...@@ -203,6 +215,7 @@ class HeapSnapshot { ...@@ -203,6 +215,7 @@ class HeapSnapshot {
std::deque<HeapGraphEdge> edges_; std::deque<HeapGraphEdge> edges_;
std::deque<HeapGraphEdge*> children_; std::deque<HeapGraphEdge*> children_;
std::vector<HeapEntry*> sorted_entries_; std::vector<HeapEntry*> sorted_entries_;
std::vector<SourceLocation> locations_;
SnapshotObjectId max_snapshot_js_object_id_; SnapshotObjectId max_snapshot_js_object_id_;
friend class HeapSnapshotTester; friend class HeapSnapshotTester;
...@@ -363,6 +376,7 @@ class V8HeapExplorer : public HeapEntriesAllocator { ...@@ -363,6 +376,7 @@ class V8HeapExplorer : public HeapEntriesAllocator {
const char* GetSystemEntryName(HeapObject* object); const char* GetSystemEntryName(HeapObject* object);
void ExtractLocation(int entry, HeapObject* object);
void ExtractReferences(int entry, HeapObject* obj); void ExtractReferences(int entry, HeapObject* obj);
void ExtractJSGlobalProxyReferences(int entry, JSGlobalProxy* proxy); void ExtractJSGlobalProxyReferences(int entry, JSGlobalProxy* proxy);
void ExtractJSObjectReferences(int entry, JSObject* js_obj); void ExtractJSObjectReferences(int entry, JSObject* js_obj);
...@@ -593,7 +607,8 @@ class HeapSnapshotJSONSerializer { ...@@ -593,7 +607,8 @@ class HeapSnapshotJSONSerializer {
V8_INLINE static uint32_t StringHash(const void* string); V8_INLINE static uint32_t StringHash(const void* string);
int GetStringId(const char* s); 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 SerializeEdge(HeapGraphEdge* edge, bool first_edge);
void SerializeEdges(); void SerializeEdges();
void SerializeImpl(); void SerializeImpl();
...@@ -606,6 +621,8 @@ class HeapSnapshotJSONSerializer { ...@@ -606,6 +621,8 @@ class HeapSnapshotJSONSerializer {
void SerializeSamples(); void SerializeSamples();
void SerializeString(const unsigned char* s); void SerializeString(const unsigned char* s);
void SerializeStrings(); void SerializeStrings();
void SerializeLocation(const SourceLocation& location);
void SerializeLocations();
static const int kEdgeFieldsCount; static const int kEdgeFieldsCount;
static const int kNodeFieldsCount; static const int kNodeFieldsCount;
......
...@@ -49,7 +49,9 @@ using i::AllocationTraceNode; ...@@ -49,7 +49,9 @@ using i::AllocationTraceNode;
using i::AllocationTraceTree; using i::AllocationTraceTree;
using i::AllocationTracker; using i::AllocationTracker;
using i::ArrayVector; using i::ArrayVector;
using i::SourceLocation;
using i::Vector; using i::Vector;
using v8::base::Optional;
namespace { namespace {
...@@ -151,6 +153,23 @@ static const v8::HeapGraphNode* GetRootChild(const v8::HeapSnapshot* snapshot, ...@@ -151,6 +153,23 @@ static const v8::HeapGraphNode* GetRootChild(const v8::HeapSnapshot* snapshot,
return GetChildByName(snapshot->GetRoot(), name); return GetChildByName(snapshot->GetRoot(), name);
} }
static Optional<SourceLocation> GetLocation(const v8::HeapSnapshot* s,
const v8::HeapGraphNode* node) {
const i::HeapSnapshot* snapshot = reinterpret_cast<const i::HeapSnapshot*>(s);
const std::vector<SourceLocation>& locations = snapshot->locations();
const int index =
const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
->index();
for (const auto& loc : locations) {
if (loc.entry_index == index) {
return Optional<SourceLocation>(loc);
}
}
return Optional<SourceLocation>();
}
static const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate, static const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate,
const v8::HeapGraphNode* node, const v8::HeapGraphNode* node,
v8::HeapGraphEdge::Type type, v8::HeapGraphEdge::Type type,
...@@ -258,6 +277,27 @@ TEST(HeapSnapshot) { ...@@ -258,6 +277,27 @@ TEST(HeapSnapshot) {
CHECK(det.has_C2); 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<SourceLocation> location = GetLocation(snapshot, x);
CHECK(location);
CHECK_EQ(0, location->line);
CHECK_EQ(31, location->col);
}
TEST(HeapSnapshotObjectSizes) { TEST(HeapSnapshotObjectSizes) {
LocalContext env; LocalContext env;
...@@ -1045,6 +1085,7 @@ TEST(HeapSnapshotJSONSerialization) { ...@@ -1045,6 +1085,7 @@ TEST(HeapSnapshotJSONSerialization) {
CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust()); 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("nodes")).FromJust());
CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).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()); CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust());
// Get node and edge "member" offsets. // Get node and edge "member" offsets.
......
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