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/+/2806747 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Commit-Queue: Arnaud Renevier <arenevier@fb.com> Cr-Commit-Position: refs/heads/master@{#73985}
This commit is contained in:
parent
80aaae9e74
commit
7f52e4f92d
@ -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);
|
||||
|
@ -183,9 +183,11 @@ const char* HeapEntry::TypeAsString() const {
|
||||
}
|
||||
}
|
||||
|
||||
HeapSnapshot::HeapSnapshot(HeapProfiler* profiler, bool global_objects_as_roots)
|
||||
HeapSnapshot::HeapSnapshot(HeapProfiler* profiler, bool global_objects_as_roots,
|
||||
bool capture_numeric_value)
|
||||
: profiler_(profiler),
|
||||
treat_global_objects_as_roots_(global_objects_as_roots) {
|
||||
treat_global_objects_as_roots_(global_objects_as_roots),
|
||||
capture_numeric_value_(capture_numeric_value) {
|
||||
// It is very important to keep objects that form a heap snapshot
|
||||
// as small as possible. Check assumptions about data structure sizes.
|
||||
STATIC_ASSERT(kSystemPointerSize != 4 || sizeof(HeapGraphEdge) == 12);
|
||||
@ -387,8 +389,7 @@ SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
|
||||
return entry_info.id;
|
||||
}
|
||||
entry->value = reinterpret_cast<void*>(entries_.size());
|
||||
SnapshotObjectId id = next_id_;
|
||||
next_id_ += kObjectIdStep;
|
||||
SnapshotObjectId id = get_next_id();
|
||||
entries_.push_back(EntryInfo(id, addr, size, accessed));
|
||||
DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
|
||||
return id;
|
||||
@ -553,6 +554,16 @@ HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
|
||||
return AddEntry(HeapObject::cast(Object(reinterpret_cast<Address>(ptr))));
|
||||
}
|
||||
|
||||
HeapEntry* V8HeapExplorer::AllocateEntry(Smi smi) {
|
||||
SnapshotObjectId id = heap_object_map_->get_next_id();
|
||||
HeapEntry* entry =
|
||||
snapshot_->AddEntry(HeapEntry::kHeapNumber, "smi number", id, 0, 0);
|
||||
// XXX: Smis do not appear in CombinedHeapObjectIterator, so we need to
|
||||
// extract the references here
|
||||
ExtractNumberReference(entry, smi);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void V8HeapExplorer::ExtractLocation(HeapEntry* entry, HeapObject object) {
|
||||
if (object.IsJSFunction()) {
|
||||
JSFunction func = JSFunction::cast(object);
|
||||
@ -638,7 +649,7 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject object) {
|
||||
object.IsByteArray()) {
|
||||
return AddEntry(object, HeapEntry::kArray, "");
|
||||
} else if (object.IsHeapNumber()) {
|
||||
return AddEntry(object, HeapEntry::kHeapNumber, "number");
|
||||
return AddEntry(object, HeapEntry::kHeapNumber, "heap number");
|
||||
}
|
||||
return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
|
||||
}
|
||||
@ -837,6 +848,10 @@ void V8HeapExplorer::ExtractReferences(HeapEntry* entry, HeapObject obj) {
|
||||
ExtractEphemeronHashTableReferences(entry, EphemeronHashTable::cast(obj));
|
||||
} else if (obj.IsFixedArray()) {
|
||||
ExtractFixedArrayReferences(entry, FixedArray::cast(obj));
|
||||
} else if (obj.IsHeapNumber()) {
|
||||
if (snapshot_->capture_numeric_value()) {
|
||||
ExtractNumberReference(entry, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,6 +1268,11 @@ class JSArrayBufferDataEntryAllocator : public HeapEntriesAllocator {
|
||||
HeapEntry::kNative, "system / JSArrayBufferData",
|
||||
size_);
|
||||
}
|
||||
HeapEntry* AllocateEntry(Smi smi) override {
|
||||
DCHECK(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
V8HeapExplorer* explorer_;
|
||||
@ -1298,6 +1318,30 @@ void V8HeapExplorer::ExtractFixedArrayReferences(HeapEntry* entry,
|
||||
}
|
||||
}
|
||||
|
||||
void V8HeapExplorer::ExtractNumberReference(HeapEntry* entry, Object number) {
|
||||
DCHECK(number.IsNumber());
|
||||
|
||||
// Must be large enough to fit any double, int, or size_t.
|
||||
char arr[32];
|
||||
Vector<char> buffer(arr, arraysize(arr));
|
||||
|
||||
const char* string;
|
||||
if (number.IsSmi()) {
|
||||
int int_value = Smi::ToInt(number);
|
||||
string = IntToCString(int_value, buffer);
|
||||
} else {
|
||||
double double_value = HeapNumber::cast(number).value();
|
||||
string = DoubleToCString(double_value, buffer);
|
||||
}
|
||||
|
||||
const char* name = names_->GetCopy(string);
|
||||
|
||||
SnapshotObjectId id = heap_object_map_->get_next_id();
|
||||
HeapEntry* child_entry =
|
||||
snapshot_->AddEntry(HeapEntry::kString, name, id, 0, 0);
|
||||
entry->SetNamedReference(HeapGraphEdge::kInternal, "value", child_entry);
|
||||
}
|
||||
|
||||
void V8HeapExplorer::ExtractFeedbackVectorReferences(
|
||||
HeapEntry* entry, FeedbackVector feedback_vector) {
|
||||
MaybeObject code = feedback_vector.maybe_optimized_code();
|
||||
@ -1352,8 +1396,10 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject js_obj,
|
||||
PropertyDetails details = descs.GetDetails(i);
|
||||
switch (details.location()) {
|
||||
case kField: {
|
||||
Representation r = details.representation();
|
||||
if (r.IsSmi() || r.IsDouble()) break;
|
||||
if (!snapshot_->capture_numeric_value()) {
|
||||
Representation r = details.representation();
|
||||
if (r.IsSmi() || r.IsDouble()) break;
|
||||
}
|
||||
|
||||
Name k = descs.GetKey(i);
|
||||
FieldIndex field_index = FieldIndex::ForDescriptor(js_obj.map(), i);
|
||||
@ -1483,9 +1529,15 @@ String V8HeapExplorer::GetConstructorName(JSObject object) {
|
||||
}
|
||||
|
||||
HeapEntry* V8HeapExplorer::GetEntry(Object obj) {
|
||||
return obj.IsHeapObject() ? generator_->FindOrAddEntry(
|
||||
reinterpret_cast<void*>(obj.ptr()), this)
|
||||
: nullptr;
|
||||
if (obj.IsHeapObject()) {
|
||||
return generator_->FindOrAddEntry(reinterpret_cast<void*>(obj.ptr()), this);
|
||||
}
|
||||
|
||||
DCHECK(obj.IsSmi());
|
||||
if (!snapshot_->capture_numeric_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
return generator_->FindOrAddEntry(Smi::cast(obj), this);
|
||||
}
|
||||
|
||||
class RootsReferencesExtractor : public RootVisitor {
|
||||
@ -1657,23 +1709,25 @@ void V8HeapExplorer::SetElementReference(HeapEntry* parent_entry, int index,
|
||||
void V8HeapExplorer::SetInternalReference(HeapEntry* parent_entry,
|
||||
const char* reference_name,
|
||||
Object child_obj, int field_offset) {
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry == nullptr) return;
|
||||
if (IsEssentialObject(child_obj)) {
|
||||
parent_entry->SetNamedReference(HeapGraphEdge::kInternal, reference_name,
|
||||
child_entry);
|
||||
if (!IsEssentialObject(child_obj)) {
|
||||
return;
|
||||
}
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
DCHECK_NOT_NULL(child_entry);
|
||||
parent_entry->SetNamedReference(HeapGraphEdge::kInternal, reference_name,
|
||||
child_entry);
|
||||
MarkVisitedField(field_offset);
|
||||
}
|
||||
|
||||
void V8HeapExplorer::SetInternalReference(HeapEntry* parent_entry, int index,
|
||||
Object child_obj, int field_offset) {
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry == nullptr) return;
|
||||
if (IsEssentialObject(child_obj)) {
|
||||
parent_entry->SetNamedReference(HeapGraphEdge::kInternal,
|
||||
names_->GetName(index), child_entry);
|
||||
if (!IsEssentialObject(child_obj)) {
|
||||
return;
|
||||
}
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
DCHECK_NOT_NULL(child_entry);
|
||||
parent_entry->SetNamedReference(HeapGraphEdge::kInternal,
|
||||
names_->GetName(index), child_entry);
|
||||
MarkVisitedField(field_offset);
|
||||
}
|
||||
|
||||
@ -1682,9 +1736,12 @@ void V8HeapExplorer::SetHiddenReference(HeapObject parent_obj,
|
||||
Object child_obj, int field_offset) {
|
||||
DCHECK_EQ(parent_entry, GetEntry(parent_obj));
|
||||
DCHECK(!MapWord::IsPacked(child_obj.ptr()));
|
||||
if (!IsEssentialObject(child_obj)) {
|
||||
return;
|
||||
}
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry != nullptr && IsEssentialObject(child_obj) &&
|
||||
IsEssentialHiddenReference(parent_obj, field_offset)) {
|
||||
DCHECK_NOT_NULL(child_entry);
|
||||
if (IsEssentialHiddenReference(parent_obj, field_offset)) {
|
||||
parent_entry->SetIndexedReference(HeapGraphEdge::kHidden, index,
|
||||
child_entry);
|
||||
}
|
||||
@ -1693,23 +1750,25 @@ void V8HeapExplorer::SetHiddenReference(HeapObject parent_obj,
|
||||
void V8HeapExplorer::SetWeakReference(HeapEntry* parent_entry,
|
||||
const char* reference_name,
|
||||
Object child_obj, int field_offset) {
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry == nullptr) return;
|
||||
if (IsEssentialObject(child_obj)) {
|
||||
parent_entry->SetNamedReference(HeapGraphEdge::kWeak, reference_name,
|
||||
child_entry);
|
||||
if (!IsEssentialObject(child_obj)) {
|
||||
return;
|
||||
}
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
DCHECK_NOT_NULL(child_entry);
|
||||
parent_entry->SetNamedReference(HeapGraphEdge::kWeak, reference_name,
|
||||
child_entry);
|
||||
MarkVisitedField(field_offset);
|
||||
}
|
||||
|
||||
void V8HeapExplorer::SetWeakReference(HeapEntry* parent_entry, int index,
|
||||
Object child_obj, int field_offset) {
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry == nullptr) return;
|
||||
if (IsEssentialObject(child_obj)) {
|
||||
parent_entry->SetNamedReference(
|
||||
HeapGraphEdge::kWeak, names_->GetFormatted("%d", index), child_entry);
|
||||
if (!IsEssentialObject(child_obj)) {
|
||||
return;
|
||||
}
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
DCHECK_NOT_NULL(child_entry);
|
||||
parent_entry->SetNamedReference(
|
||||
HeapGraphEdge::kWeak, names_->GetFormatted("%d", index), child_entry);
|
||||
MarkVisitedField(field_offset);
|
||||
}
|
||||
|
||||
@ -1767,6 +1826,13 @@ void V8HeapExplorer::SetGcRootsReference(Root root) {
|
||||
|
||||
void V8HeapExplorer::SetGcSubrootReference(Root root, const char* description,
|
||||
bool is_weak, Object child_obj) {
|
||||
if (child_obj.IsSmi()) {
|
||||
// TODO(arenevier): if we handle smis here, the snapshot gets 2 to 3 times
|
||||
// slower on large heaps. According to perf, The bulk of the extra works
|
||||
// happens in TemplateHashMapImpl::Probe method, when tyring to get
|
||||
// names->GetFormatted("%d / %s", index, description)
|
||||
return;
|
||||
}
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry == nullptr) return;
|
||||
const char* name = GetStrongGcSubrootName(child_obj);
|
||||
@ -1944,6 +2010,7 @@ class EmbedderGraphEntriesAllocator : public HeapEntriesAllocator {
|
||||
names_(snapshot_->profiler()->names()),
|
||||
heap_object_map_(snapshot_->profiler()->heap_object_map()) {}
|
||||
HeapEntry* AllocateEntry(HeapThing ptr) override;
|
||||
HeapEntry* AllocateEntry(Smi smi) override;
|
||||
|
||||
private:
|
||||
HeapSnapshot* snapshot_;
|
||||
@ -1994,6 +2061,11 @@ HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(HeapThing ptr) {
|
||||
return heap_entry;
|
||||
}
|
||||
|
||||
HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(Smi smi) {
|
||||
DCHECK(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NativeObjectsExplorer::NativeObjectsExplorer(
|
||||
HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
|
||||
: isolate_(
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user