[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:
parent
fe5e07d763
commit
64e04c96af
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user