Commit 7f52e4f9 authored by Arno Renevier's avatar Arno Renevier Committed by Commit Bot

Heap Number encoding

- represent smis as fake heap numbers
- numbers nodes (both smi and heap numbers) reference a child node whose
  name is "value" and whose entry is the string representation of that
  number

That feature is disabled by default, and can be enabled by passing
captureNumericValue: true when calling HeapProfiler.takeHeapSnapshot

This patch slightly refactors some functions that operate on "essential
objects". We now check that the object is essential before trying to
create the entry. Otherwise, we would end up with smi objects created,
but not referenced anywhere.

Design doc:
https://docs.google.com/document/d/1Qh1zxyn0SS5wzJzitD6ecBJTdFbQkJogSMwxDRsn44o/edit

Change-Id: Ibbe6e79a54c4f9eace72bc0a0ccb622a97698e00
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2806747Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Arnaud Renevier <arenevier@fb.com>
Cr-Commit-Position: refs/heads/master@{#73985}
parent 80aaae9e
...@@ -707,6 +707,8 @@ experimental domain HeapProfiler ...@@ -707,6 +707,8 @@ experimental domain HeapProfiler
# when the tracking is stopped. # when the tracking is stopped.
optional boolean reportProgress optional boolean reportProgress
optional boolean treatGlobalObjectsAsRoots optional boolean treatGlobalObjectsAsRoots
# If true, numerical values are included in the snapshot
optional boolean captureNumericValue
command takeHeapSnapshot command takeHeapSnapshot
parameters parameters
...@@ -714,6 +716,8 @@ experimental domain HeapProfiler ...@@ -714,6 +716,8 @@ experimental domain HeapProfiler
optional boolean reportProgress optional boolean reportProgress
# If true, a raw snapshot without artifical roots will be generated # If true, a raw snapshot without artifical roots will be generated
optional boolean treatGlobalObjectsAsRoots optional boolean treatGlobalObjectsAsRoots
# If true, numerical values are included in the snapshot
optional boolean captureNumericValue
event addHeapSnapshotChunk event addHeapSnapshotChunk
parameters parameters
......
...@@ -900,7 +900,8 @@ class V8_EXPORT HeapProfiler { ...@@ -900,7 +900,8 @@ class V8_EXPORT HeapProfiler {
const HeapSnapshot* TakeHeapSnapshot( const HeapSnapshot* TakeHeapSnapshot(
ActivityControl* control = nullptr, ActivityControl* control = nullptr,
ObjectNameResolver* global_object_name_resolver = nullptr, ObjectNameResolver* global_object_name_resolver = nullptr,
bool treat_global_objects_as_roots = true); bool treat_global_objects_as_roots = true,
bool capture_numeric_value = false);
/** /**
* Starts tracking of heap objects population statistics. After calling * Starts tracking of heap objects population statistics. After calling
......
...@@ -9707,10 +9707,11 @@ void HeapProfiler::ClearObjectIds() { ...@@ -9707,10 +9707,11 @@ void HeapProfiler::ClearObjectIds() {
const HeapSnapshot* HeapProfiler::TakeHeapSnapshot( const HeapSnapshot* HeapProfiler::TakeHeapSnapshot(
ActivityControl* control, ObjectNameResolver* resolver, ActivityControl* control, ObjectNameResolver* resolver,
bool treat_global_objects_as_roots) { bool treat_global_objects_as_roots, bool capture_numeric_value) {
return reinterpret_cast<const HeapSnapshot*>( return reinterpret_cast<const HeapSnapshot*>(
reinterpret_cast<i::HeapProfiler*>(this)->TakeSnapshot( reinterpret_cast<i::HeapProfiler*>(this)->TakeSnapshot(
control, resolver, treat_global_objects_as_roots)); control, resolver, treat_global_objects_as_roots,
capture_numeric_value));
} }
void HeapProfiler::StartTrackingHeapObjects(bool track_allocations) { void HeapProfiler::StartTrackingHeapObjects(bool track_allocations) {
......
...@@ -232,10 +232,12 @@ Response V8HeapProfilerAgentImpl::startTrackingHeapObjects( ...@@ -232,10 +232,12 @@ Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
} }
Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects( Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) { Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) {
requestHeapStatsUpdate(); requestHeapStatsUpdate();
takeHeapSnapshot(std::move(reportProgress), takeHeapSnapshot(std::move(reportProgress),
std::move(treatGlobalObjectsAsRoots)); std::move(treatGlobalObjectsAsRoots),
std::move(captureNumericValue));
stopTrackingHeapObjectsInternal(); stopTrackingHeapObjectsInternal();
return Response::Success(); return Response::Success();
} }
...@@ -258,7 +260,8 @@ Response V8HeapProfilerAgentImpl::disable() { ...@@ -258,7 +260,8 @@ Response V8HeapProfilerAgentImpl::disable() {
} }
Response V8HeapProfilerAgentImpl::takeHeapSnapshot( Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) { Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) {
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) return Response::ServerError("Cannot access v8 heap profiler"); if (!profiler) return Response::ServerError("Cannot access v8 heap profiler");
std::unique_ptr<HeapSnapshotProgress> progress; std::unique_ptr<HeapSnapshotProgress> progress;
...@@ -267,7 +270,8 @@ Response V8HeapProfilerAgentImpl::takeHeapSnapshot( ...@@ -267,7 +270,8 @@ Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
GlobalObjectNameResolver resolver(m_session); GlobalObjectNameResolver resolver(m_session);
const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot( const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(
progress.get(), &resolver, treatGlobalObjectsAsRoots.fromMaybe(true)); progress.get(), &resolver, treatGlobalObjectsAsRoots.fromMaybe(true),
captureNumericValue.fromMaybe(false));
if (!snapshot) return Response::ServerError("Failed to take heap snapshot"); if (!snapshot) return Response::ServerError("Failed to take heap snapshot");
HeapSnapshotOutputStream stream(&m_frontend); HeapSnapshotOutputStream stream(&m_frontend);
snapshot->Serialize(&stream); snapshot->Serialize(&stream);
......
...@@ -34,14 +34,15 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { ...@@ -34,14 +34,15 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
Response enable() override; Response enable() override;
Response startTrackingHeapObjects(Maybe<bool> trackAllocations) override; Response startTrackingHeapObjects(Maybe<bool> trackAllocations) override;
Response stopTrackingHeapObjects( Response stopTrackingHeapObjects(Maybe<bool> reportProgress,
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> treatGlobalObjectsAsRoots) override; Maybe<bool> captureNumericValue) override;
Response disable() override; Response disable() override;
Response takeHeapSnapshot(Maybe<bool> reportProgress, Response takeHeapSnapshot(Maybe<bool> reportProgress,
Maybe<bool> treatGlobalObjectsAsRoots) override; Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) override;
Response getObjectByHeapObjectId( Response getObjectByHeapObjectId(
const String16& heapSnapshotObjectId, Maybe<String16> objectGroup, const String16& heapSnapshotObjectId, Maybe<String16> objectGroup,
......
...@@ -81,9 +81,10 @@ v8::EmbedderGraph::Node::Detachedness HeapProfiler::GetDetachedness( ...@@ -81,9 +81,10 @@ v8::EmbedderGraph::Node::Detachedness HeapProfiler::GetDetachedness(
HeapSnapshot* HeapProfiler::TakeSnapshot( HeapSnapshot* HeapProfiler::TakeSnapshot(
v8::ActivityControl* control, v8::ActivityControl* control,
v8::HeapProfiler::ObjectNameResolver* resolver, v8::HeapProfiler::ObjectNameResolver* resolver,
bool treat_global_objects_as_roots) { bool treat_global_objects_as_roots, bool capture_numeric_value) {
is_taking_snapshot_ = true; is_taking_snapshot_ = true;
HeapSnapshot* result = new HeapSnapshot(this, treat_global_objects_as_roots); HeapSnapshot* result = new HeapSnapshot(this, treat_global_objects_as_roots,
capture_numeric_value);
{ {
HeapSnapshotGenerator generator(result, control, resolver, heap()); HeapSnapshotGenerator generator(result, control, resolver, heap());
if (!generator.GenerateSnapshot()) { if (!generator.GenerateSnapshot()) {
......
...@@ -33,7 +33,8 @@ class HeapProfiler : public HeapObjectAllocationTracker { ...@@ -33,7 +33,8 @@ class HeapProfiler : public HeapObjectAllocationTracker {
HeapSnapshot* TakeSnapshot(v8::ActivityControl* control, HeapSnapshot* TakeSnapshot(v8::ActivityControl* control,
v8::HeapProfiler::ObjectNameResolver* resolver, v8::HeapProfiler::ObjectNameResolver* resolver,
bool treat_global_objects_as_roots); bool treat_global_objects_as_roots,
bool capture_numeric_value);
bool StartSamplingHeapProfiler(uint64_t sample_interval, int stack_depth, bool StartSamplingHeapProfiler(uint64_t sample_interval, int stack_depth,
v8::HeapProfiler::SamplingFlags); v8::HeapProfiler::SamplingFlags);
......
This diff is collapsed.
...@@ -188,7 +188,8 @@ class HeapEntry { ...@@ -188,7 +188,8 @@ class HeapEntry {
// HeapSnapshotGenerator fills in a HeapSnapshot. // HeapSnapshotGenerator fills in a HeapSnapshot.
class HeapSnapshot { class HeapSnapshot {
public: public:
explicit HeapSnapshot(HeapProfiler* profiler, bool global_objects_as_roots); explicit HeapSnapshot(HeapProfiler* profiler, bool global_objects_as_roots,
bool capture_numeric_value);
HeapSnapshot(const HeapSnapshot&) = delete; HeapSnapshot(const HeapSnapshot&) = delete;
HeapSnapshot& operator=(const HeapSnapshot&) = delete; HeapSnapshot& operator=(const HeapSnapshot&) = delete;
void Delete(); void Delete();
...@@ -213,6 +214,7 @@ class HeapSnapshot { ...@@ -213,6 +214,7 @@ class HeapSnapshot {
bool treat_global_objects_as_roots() const { bool treat_global_objects_as_roots() const {
return treat_global_objects_as_roots_; return treat_global_objects_as_roots_;
} }
bool capture_numeric_value() const { return capture_numeric_value_; }
void AddLocation(HeapEntry* entry, int scriptId, int line, int col); void AddLocation(HeapEntry* entry, int scriptId, int line, int col);
HeapEntry* AddEntry(HeapEntry::Type type, HeapEntry* AddEntry(HeapEntry::Type type,
...@@ -245,6 +247,7 @@ class HeapSnapshot { ...@@ -245,6 +247,7 @@ class HeapSnapshot {
std::vector<SourceLocation> locations_; std::vector<SourceLocation> locations_;
SnapshotObjectId max_snapshot_js_object_id_ = -1; SnapshotObjectId max_snapshot_js_object_id_ = -1;
bool treat_global_objects_as_roots_; bool treat_global_objects_as_roots_;
bool capture_numeric_value_;
}; };
...@@ -277,6 +280,10 @@ class HeapObjectsMap { ...@@ -277,6 +280,10 @@ class HeapObjectsMap {
SnapshotObjectId last_assigned_id() const { SnapshotObjectId last_assigned_id() const {
return next_id_ - kObjectIdStep; return next_id_ - kObjectIdStep;
} }
SnapshotObjectId get_next_id() {
next_id_ += kObjectIdStep;
return next_id_ - kObjectIdStep;
}
void StopHeapObjectsTracking(); void StopHeapObjectsTracking();
SnapshotObjectId PushHeapObjectsStats(OutputStream* stream, SnapshotObjectId PushHeapObjectsStats(OutputStream* stream,
...@@ -322,6 +329,7 @@ class HeapEntriesAllocator { ...@@ -322,6 +329,7 @@ class HeapEntriesAllocator {
public: public:
virtual ~HeapEntriesAllocator() = default; virtual ~HeapEntriesAllocator() = default;
virtual HeapEntry* AllocateEntry(HeapThing ptr) = 0; virtual HeapEntry* AllocateEntry(HeapThing ptr) = 0;
virtual HeapEntry* AllocateEntry(Smi smi) = 0;
}; };
class SnapshottingProgressReportingInterface { class SnapshottingProgressReportingInterface {
...@@ -342,6 +350,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator { ...@@ -342,6 +350,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator {
V8HeapExplorer& operator=(const V8HeapExplorer&) = delete; V8HeapExplorer& operator=(const V8HeapExplorer&) = delete;
HeapEntry* AllocateEntry(HeapThing ptr) override; HeapEntry* AllocateEntry(HeapThing ptr) override;
HeapEntry* AllocateEntry(Smi smi) override;
int EstimateObjectsCount(); int EstimateObjectsCount();
bool IterateAndExtractReferences(HeapSnapshotGenerator* generator); bool IterateAndExtractReferences(HeapSnapshotGenerator* generator);
void CollectGlobalObjectsTags(); void CollectGlobalObjectsTags();
...@@ -397,6 +406,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator { ...@@ -397,6 +406,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator {
void ExtractJSGeneratorObjectReferences(HeapEntry* entry, void ExtractJSGeneratorObjectReferences(HeapEntry* entry,
JSGeneratorObject generator); JSGeneratorObject generator);
void ExtractFixedArrayReferences(HeapEntry* entry, FixedArray array); void ExtractFixedArrayReferences(HeapEntry* entry, FixedArray array);
void ExtractNumberReference(HeapEntry* entry, Object number);
void ExtractFeedbackVectorReferences(HeapEntry* entry, void ExtractFeedbackVectorReferences(HeapEntry* entry,
FeedbackVector feedback_vector); FeedbackVector feedback_vector);
void ExtractDescriptorArrayReferences(HeapEntry* entry, void ExtractDescriptorArrayReferences(HeapEntry* entry,
...@@ -501,6 +511,9 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { ...@@ -501,6 +511,9 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
// The HeapEntriesMap instance is used to track a mapping between // The HeapEntriesMap instance is used to track a mapping between
// real heap objects and their representations in heap snapshots. // real heap objects and their representations in heap snapshots.
using HeapEntriesMap = std::unordered_map<HeapThing, HeapEntry*>; using HeapEntriesMap = std::unordered_map<HeapThing, HeapEntry*>;
// The SmiEntriesMap instance is used to track a mapping between smi and
// their representations in heap snapshots.
using SmiEntriesMap = std::unordered_map<int, HeapEntry*>;
HeapSnapshotGenerator(HeapSnapshot* snapshot, HeapSnapshotGenerator(HeapSnapshot* snapshot,
v8::ActivityControl* control, v8::ActivityControl* control,
...@@ -515,16 +528,31 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { ...@@ -515,16 +528,31 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
return it != entries_map_.end() ? it->second : nullptr; return it != entries_map_.end() ? it->second : nullptr;
} }
HeapEntry* FindEntry(Smi smi) {
auto it = smis_map_.find(smi.value());
return it != smis_map_.end() ? it->second : nullptr;
}
HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
return entries_map_.emplace(ptr, allocator->AllocateEntry(ptr)) return entries_map_.emplace(ptr, allocator->AllocateEntry(ptr))
.first->second; .first->second;
} }
HeapEntry* AddEntry(Smi smi, HeapEntriesAllocator* allocator) {
return smis_map_.emplace(smi.value(), allocator->AllocateEntry(smi))
.first->second;
}
HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
HeapEntry* entry = FindEntry(ptr); HeapEntry* entry = FindEntry(ptr);
return entry != nullptr ? entry : AddEntry(ptr, allocator); return entry != nullptr ? entry : AddEntry(ptr, allocator);
} }
HeapEntry* FindOrAddEntry(Smi smi, HeapEntriesAllocator* allocator) {
HeapEntry* entry = FindEntry(smi);
return entry != nullptr ? entry : AddEntry(smi, allocator);
}
private: private:
bool FillReferences(); bool FillReferences();
void ProgressStep() override; void ProgressStep() override;
...@@ -537,6 +565,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { ...@@ -537,6 +565,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
NativeObjectsExplorer dom_explorer_; NativeObjectsExplorer dom_explorer_;
// Mapping from HeapThing pointers to HeapEntry indices. // Mapping from HeapThing pointers to HeapEntry indices.
HeapEntriesMap entries_map_; HeapEntriesMap entries_map_;
SmiEntriesMap smis_map_;
// Used during snapshot generation. // Used during snapshot generation.
int progress_counter_; int progress_counter_;
int progress_total_; int progress_total_;
......
...@@ -508,6 +508,34 @@ TEST(HeapSnapshotHeapNumbers) { ...@@ -508,6 +508,34 @@ TEST(HeapSnapshotHeapNumbers) {
CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
} }
TEST(HeapSnapshotHeapNumbersCaptureNumericValue) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"a = 1; // a is Smi\n"
"b = 2.5; // b is HeapNumber");
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(nullptr, nullptr, true, true);
CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* a =
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
CHECK(a);
CHECK_EQ(1, a->GetChildrenCount());
v8::String::Utf8Value value_a(CcTest::isolate(),
a->GetChild(0)->GetToNode()->GetName());
CHECK_EQ(0, strcmp("1", *value_a));
const v8::HeapGraphNode* b =
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
CHECK(b);
CHECK_EQ(2, b->GetChildrenCount());
v8::String::Utf8Value value_b(CcTest::isolate(),
b->GetChild(0)->GetToNode()->GetName());
CHECK_EQ(0, strcmp("2.5", *value_b));
}
TEST(HeapSnapshotHeapBigInts) { TEST(HeapSnapshotHeapBigInts) {
LocalContext env; LocalContext env;
v8::HandleScope scope(env->GetIsolate()); v8::HandleScope scope(env->GetIsolate());
......
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