[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: Alexei Filippov <alph@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55245}
This commit is contained in:
Dominik Inführ 2018-08-20 14:14:25 +02:00 committed by Commit Bot
parent fe5e07d763
commit 64e04c96af
4 changed files with 126 additions and 6 deletions

View File

@ -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

View File

@ -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<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) {
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<char, kBufferSize> 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<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 v8

View File

@ -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<HeapEntry>& entries() { return entries_; }
std::deque<HeapGraphEdge>& edges() { return edges_; }
std::deque<HeapGraphEdge*>& children() { return children_; }
const std::vector<SourceLocation>& 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<HeapGraphEdge> edges_;
std::deque<HeapGraphEdge*> children_;
std::vector<HeapEntry*> sorted_entries_;
std::vector<SourceLocation> 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;

View File

@ -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<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,
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<SourceLocation> 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.