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
# when the tracking is stopped.
optional boolean reportProgress
optional boolean treatGlobalObjectsAsRoots
# If true, numerical values are included in the snapshot
optional boolean captureNumericValue
command takeHeapSnapshot
parameters
......@@ -714,6 +716,8 @@ experimental domain HeapProfiler
optional boolean reportProgress
# If true, a raw snapshot without artifical roots will be generated
optional boolean treatGlobalObjectsAsRoots
# If true, numerical values are included in the snapshot
optional boolean captureNumericValue
event addHeapSnapshotChunk
parameters
......
......@@ -900,7 +900,8 @@ class V8_EXPORT HeapProfiler {
const HeapSnapshot* TakeHeapSnapshot(
ActivityControl* control = 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
......
......@@ -9707,10 +9707,11 @@ void HeapProfiler::ClearObjectIds() {
const HeapSnapshot* HeapProfiler::TakeHeapSnapshot(
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*>(
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) {
......
......@@ -232,10 +232,12 @@ Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
}
Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) {
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) {
requestHeapStatsUpdate();
takeHeapSnapshot(std::move(reportProgress),
std::move(treatGlobalObjectsAsRoots));
std::move(treatGlobalObjectsAsRoots),
std::move(captureNumericValue));
stopTrackingHeapObjectsInternal();
return Response::Success();
}
......@@ -258,7 +260,8 @@ Response V8HeapProfilerAgentImpl::disable() {
}
Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) {
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) {
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) return Response::ServerError("Cannot access v8 heap profiler");
std::unique_ptr<HeapSnapshotProgress> progress;
......@@ -267,7 +270,8 @@ Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
GlobalObjectNameResolver resolver(m_session);
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");
HeapSnapshotOutputStream stream(&m_frontend);
snapshot->Serialize(&stream);
......
......@@ -34,14 +34,15 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
Response enable() override;
Response startTrackingHeapObjects(Maybe<bool> trackAllocations) override;
Response stopTrackingHeapObjects(
Maybe<bool> reportProgress,
Maybe<bool> treatGlobalObjectsAsRoots) override;
Response stopTrackingHeapObjects(Maybe<bool> reportProgress,
Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) override;
Response disable() override;
Response takeHeapSnapshot(Maybe<bool> reportProgress,
Maybe<bool> treatGlobalObjectsAsRoots) override;
Maybe<bool> treatGlobalObjectsAsRoots,
Maybe<bool> captureNumericValue) override;
Response getObjectByHeapObjectId(
const String16& heapSnapshotObjectId, Maybe<String16> objectGroup,
......
......@@ -81,9 +81,10 @@ v8::EmbedderGraph::Node::Detachedness HeapProfiler::GetDetachedness(
HeapSnapshot* HeapProfiler::TakeSnapshot(
v8::ActivityControl* control,
v8::HeapProfiler::ObjectNameResolver* resolver,
bool treat_global_objects_as_roots) {
bool treat_global_objects_as_roots, bool capture_numeric_value) {
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());
if (!generator.GenerateSnapshot()) {
......
......@@ -33,7 +33,8 @@ class HeapProfiler : public HeapObjectAllocationTracker {
HeapSnapshot* TakeSnapshot(v8::ActivityControl* control,
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,
v8::HeapProfiler::SamplingFlags);
......
This diff is collapsed.
......@@ -188,7 +188,8 @@ class HeapEntry {
// HeapSnapshotGenerator fills in a HeapSnapshot.
class HeapSnapshot {
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& operator=(const HeapSnapshot&) = delete;
void Delete();
......@@ -213,6 +214,7 @@ class HeapSnapshot {
bool treat_global_objects_as_roots() const {
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);
HeapEntry* AddEntry(HeapEntry::Type type,
......@@ -245,6 +247,7 @@ class HeapSnapshot {
std::vector<SourceLocation> locations_;
SnapshotObjectId max_snapshot_js_object_id_ = -1;
bool treat_global_objects_as_roots_;
bool capture_numeric_value_;
};
......@@ -277,6 +280,10 @@ class HeapObjectsMap {
SnapshotObjectId last_assigned_id() const {
return next_id_ - kObjectIdStep;
}
SnapshotObjectId get_next_id() {
next_id_ += kObjectIdStep;
return next_id_ - kObjectIdStep;
}
void StopHeapObjectsTracking();
SnapshotObjectId PushHeapObjectsStats(OutputStream* stream,
......@@ -322,6 +329,7 @@ class HeapEntriesAllocator {
public:
virtual ~HeapEntriesAllocator() = default;
virtual HeapEntry* AllocateEntry(HeapThing ptr) = 0;
virtual HeapEntry* AllocateEntry(Smi smi) = 0;
};
class SnapshottingProgressReportingInterface {
......@@ -342,6 +350,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator {
V8HeapExplorer& operator=(const V8HeapExplorer&) = delete;
HeapEntry* AllocateEntry(HeapThing ptr) override;
HeapEntry* AllocateEntry(Smi smi) override;
int EstimateObjectsCount();
bool IterateAndExtractReferences(HeapSnapshotGenerator* generator);
void CollectGlobalObjectsTags();
......@@ -397,6 +406,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator {
void ExtractJSGeneratorObjectReferences(HeapEntry* entry,
JSGeneratorObject generator);
void ExtractFixedArrayReferences(HeapEntry* entry, FixedArray array);
void ExtractNumberReference(HeapEntry* entry, Object number);
void ExtractFeedbackVectorReferences(HeapEntry* entry,
FeedbackVector feedback_vector);
void ExtractDescriptorArrayReferences(HeapEntry* entry,
......@@ -501,6 +511,9 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
// The HeapEntriesMap instance is used to track a mapping between
// real heap objects and their representations in heap snapshots.
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,
v8::ActivityControl* control,
......@@ -515,16 +528,31 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
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) {
return entries_map_.emplace(ptr, allocator->AllocateEntry(ptr))
.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* entry = FindEntry(ptr);
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:
bool FillReferences();
void ProgressStep() override;
......@@ -537,6 +565,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
NativeObjectsExplorer dom_explorer_;
// Mapping from HeapThing pointers to HeapEntry indices.
HeapEntriesMap entries_map_;
SmiEntriesMap smis_map_;
// Used during snapshot generation.
int progress_counter_;
int progress_total_;
......
......@@ -508,6 +508,34 @@ TEST(HeapSnapshotHeapNumbers) {
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) {
LocalContext env;
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