Skip to content

Commit 2ac8bb7

Browse files
mlippautzCommit Bot
authored andcommitted
profiler: Allow querying SnapshotObjectId for native objects
- Adds regular native heap entries to the HeapObjectsMap. - Adds a side map for keeping a mapping of native objects to their canonical heap entry that they have been merged into. Change-Id: Ida00628126ded1948ceb2a0cbe14da817af7f361 Bug: chromium:988350 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1720810 Commit-Queue: Michael Lippautz <[email protected]> Reviewed-by: Alexei Filippov <[email protected]> Reviewed-by: Ulan Degenbaev <[email protected]> Cr-Commit-Position: refs/heads/master@{#63140}
1 parent ac8acab commit 2ac8bb7

7 files changed

Lines changed: 242 additions & 8 deletions

File tree

include/v8-profiler.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ namespace v8 {
1818
class HeapGraphNode;
1919
struct HeapStatsUpdate;
2020

21-
typedef uint32_t SnapshotObjectId;
22-
21+
using NativeObject = void*;
22+
using SnapshotObjectId = uint32_t;
2323

2424
struct CpuProfileDeoptFrame {
2525
int script_id;
@@ -808,6 +808,12 @@ class V8_EXPORT EmbedderGraph {
808808
*/
809809
virtual const char* NamePrefix() { return nullptr; }
810810

811+
/**
812+
* Returns the NativeObject that can be used for querying the
813+
* |HeapSnapshot|.
814+
*/
815+
virtual NativeObject GetNativeObject() { return nullptr; }
816+
811817
Node(const Node&) = delete;
812818
Node& operator=(const Node&) = delete;
813819
};
@@ -870,6 +876,12 @@ class V8_EXPORT HeapProfiler {
870876
*/
871877
SnapshotObjectId GetObjectId(Local<Value> value);
872878

879+
/**
880+
* Returns SnapshotObjectId for a native object referenced by |value| if it
881+
* has been seen by the heap profiler, kUnknownObjectId otherwise.
882+
*/
883+
SnapshotObjectId GetObjectId(NativeObject value);
884+
873885
/**
874886
* Returns heap object with given SnapshotObjectId if the object is alive,
875887
* otherwise empty handle is returned.

src/api/api.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10103,6 +10103,10 @@ SnapshotObjectId HeapProfiler::GetObjectId(Local<Value> value) {
1010310103
return reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshotObjectId(obj);
1010410104
}
1010510105

10106+
SnapshotObjectId HeapProfiler::GetObjectId(NativeObject value) {
10107+
return reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshotObjectId(value);
10108+
}
10109+
1010610110
Local<Value> HeapProfiler::FindObjectById(SnapshotObjectId id) {
1010710111
i::Handle<i::Object> obj =
1010810112
reinterpret_cast<i::HeapProfiler*>(this)->FindHeapObjectById(id);

src/profiler/heap-profiler.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,17 @@ SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
151151
return ids_->FindEntry(HeapObject::cast(*obj).address());
152152
}
153153

154+
SnapshotObjectId HeapProfiler::GetSnapshotObjectId(NativeObject obj) {
155+
// Try to find id of regular native node first.
156+
SnapshotObjectId id = ids_->FindEntry(reinterpret_cast<Address>(obj));
157+
// In case no id has been found, check whether there exists an entry where the
158+
// native objects has been merged into a V8 entry.
159+
if (id == v8::HeapProfiler::kUnknownObjectId) {
160+
id = ids_->FindMergedNativeEntry(obj);
161+
}
162+
return id;
163+
}
164+
154165
void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) {
155166
base::MutexGuard guard(&profiler_mutex_);
156167
bool known_object = ids_->MoveObject(from, to, size);

src/profiler/heap-profiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class HeapProfiler : public HeapObjectAllocationTracker {
5252
int GetSnapshotsCount();
5353
HeapSnapshot* GetSnapshot(int index);
5454
SnapshotObjectId GetSnapshotObjectId(Handle<Object> obj);
55+
SnapshotObjectId GetSnapshotObjectId(NativeObject obj);
5556
void DeleteAllSnapshots();
5657
void RemoveSnapshot(HeapSnapshot* snapshot);
5758

src/profiler/heap-snapshot-generator.cc

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ void HeapObjectsMap::UpdateObjectSize(Address addr, int size) {
352352
SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
353353
base::HashMap::Entry* entry = entries_map_.Lookup(
354354
reinterpret_cast<void*>(addr), ComputeAddressHash(addr));
355-
if (entry == nullptr) return 0;
355+
if (entry == nullptr) return v8::HeapProfiler::kUnknownObjectId;
356356
int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
357357
EntryInfo& entry_info = entries_.at(entry_index);
358358
DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
@@ -386,6 +386,25 @@ SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
386386
return id;
387387
}
388388

389+
SnapshotObjectId HeapObjectsMap::FindMergedNativeEntry(NativeObject addr) {
390+
auto it = merged_native_entries_map_.find(addr);
391+
if (it == merged_native_entries_map_.end())
392+
return v8::HeapProfiler::kUnknownObjectId;
393+
return entries_[it->second].id;
394+
}
395+
396+
void HeapObjectsMap::AddMergedNativeEntry(NativeObject addr,
397+
Address canonical_addr) {
398+
base::HashMap::Entry* entry =
399+
entries_map_.Lookup(reinterpret_cast<void*>(canonical_addr),
400+
ComputeAddressHash(canonical_addr));
401+
auto result = merged_native_entries_map_.insert(
402+
{addr, reinterpret_cast<size_t>(entry->value)});
403+
if (!result.second) {
404+
result.first->second = reinterpret_cast<size_t>(entry->value);
405+
}
406+
}
407+
389408
void HeapObjectsMap::StopHeapObjectsTracking() { time_intervals_.clear(); }
390409

391410
void HeapObjectsMap::UpdateHeapObjectsMap() {
@@ -465,9 +484,20 @@ SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream,
465484
void HeapObjectsMap::RemoveDeadEntries() {
466485
DCHECK(entries_.size() > 0 && entries_.at(0).id == 0 &&
467486
entries_.at(0).addr == kNullAddress);
487+
488+
// Build up temporary reverse map.
489+
std::unordered_map<size_t, NativeObject> reverse_merged_native_entries_map;
490+
for (const auto& it : merged_native_entries_map_) {
491+
auto result =
492+
reverse_merged_native_entries_map.emplace(it.second, it.first);
493+
DCHECK(result.second);
494+
USE(result);
495+
}
496+
468497
size_t first_free_entry = 1;
469498
for (size_t i = 1; i < entries_.size(); ++i) {
470499
EntryInfo& entry_info = entries_.at(i);
500+
auto merged_reverse_it = reverse_merged_native_entries_map.find(i);
471501
if (entry_info.accessed) {
472502
if (first_free_entry != i) {
473503
entries_.at(first_free_entry) = entry_info;
@@ -478,11 +508,19 @@ void HeapObjectsMap::RemoveDeadEntries() {
478508
ComputeAddressHash(entry_info.addr));
479509
DCHECK(entry);
480510
entry->value = reinterpret_cast<void*>(first_free_entry);
511+
if (merged_reverse_it != reverse_merged_native_entries_map.end()) {
512+
auto it = merged_native_entries_map_.find(merged_reverse_it->second);
513+
DCHECK_NE(merged_native_entries_map_.end(), it);
514+
it->second = first_free_entry;
515+
}
481516
++first_free_entry;
482517
} else {
483518
if (entry_info.addr) {
484519
entries_map_.Remove(reinterpret_cast<void*>(entry_info.addr),
485520
ComputeAddressHash(entry_info.addr));
521+
if (merged_reverse_it != reverse_merged_native_entries_map.end()) {
522+
merged_native_entries_map_.erase(merged_reverse_it->second);
523+
}
486524
}
487525
}
488526
}
@@ -1853,10 +1891,14 @@ HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(HeapThing ptr) {
18531891
reinterpret_cast<EmbedderGraphImpl::Node*>(ptr);
18541892
DCHECK(node->IsEmbedderNode());
18551893
size_t size = node->SizeInBytes();
1856-
return snapshot_->AddEntry(
1857-
EmbedderGraphNodeType(node), EmbedderGraphNodeName(names_, node),
1858-
static_cast<SnapshotObjectId>(reinterpret_cast<uintptr_t>(node) << 1),
1859-
static_cast<int>(size), 0);
1894+
Address lookup_address = reinterpret_cast<Address>(node->GetNativeObject());
1895+
SnapshotObjectId id =
1896+
(lookup_address) ? heap_object_map_->FindOrAddEntry(lookup_address, 0)
1897+
: static_cast<SnapshotObjectId>(
1898+
reinterpret_cast<uintptr_t>(node) << 1);
1899+
return snapshot_->AddEntry(EmbedderGraphNodeType(node),
1900+
EmbedderGraphNodeName(names_, node), id,
1901+
static_cast<int>(size), 0);
18601902
}
18611903

18621904
NativeObjectsExplorer::NativeObjectsExplorer(
@@ -1865,12 +1907,14 @@ NativeObjectsExplorer::NativeObjectsExplorer(
18651907
Isolate::FromHeap(snapshot->profiler()->heap_object_map()->heap())),
18661908
snapshot_(snapshot),
18671909
names_(snapshot_->profiler()->names()),
1910+
heap_object_map_(snapshot_->profiler()->heap_object_map()),
18681911
embedder_graph_entries_allocator_(
18691912
new EmbedderGraphEntriesAllocator(snapshot)) {}
18701913

18711914
HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
18721915
EmbedderGraphImpl::Node* node) {
18731916
EmbedderGraphImpl::Node* wrapper = node->WrapperNode();
1917+
NativeObject native_object = node->GetNativeObject();
18741918
if (wrapper) {
18751919
node = wrapper;
18761920
}
@@ -1882,8 +1926,16 @@ HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
18821926
static_cast<EmbedderGraphImpl::V8NodeImpl*>(node);
18831927
Object object = v8_node->GetObject();
18841928
if (object.IsSmi()) return nullptr;
1885-
return generator_->FindEntry(
1929+
HeapEntry* entry = generator_->FindEntry(
18861930
reinterpret_cast<void*>(Object::cast(object).ptr()));
1931+
if (native_object) {
1932+
HeapObject heap_object = HeapObject::cast(object);
1933+
heap_object_map_->AddMergedNativeEntry(native_object,
1934+
heap_object.address());
1935+
DCHECK_EQ(entry->id(),
1936+
heap_object_map_->FindMergedNativeEntry(native_object));
1937+
}
1938+
return entry;
18871939
}
18881940
}
18891941

src/profiler/heap-snapshot-generator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ class HeapObjectsMap {
249249
SnapshotObjectId FindOrAddEntry(Address addr,
250250
unsigned int size,
251251
bool accessed = true);
252+
SnapshotObjectId FindMergedNativeEntry(NativeObject addr);
253+
void AddMergedNativeEntry(NativeObject addr, Address canonical_addr);
252254
bool MoveObject(Address from, Address to, int size);
253255
void UpdateObjectSize(Address addr, int size);
254256
SnapshotObjectId last_assigned_id() const {
@@ -285,6 +287,8 @@ class HeapObjectsMap {
285287
base::HashMap entries_map_;
286288
std::vector<EntryInfo> entries_;
287289
std::vector<TimeInterval> time_intervals_;
290+
// Map from NativeObject to EntryInfo index in entries_.
291+
std::unordered_map<NativeObject, size_t> merged_native_entries_map_;
288292
Heap* heap_;
289293

290294
DISALLOW_COPY_AND_ASSIGN(HeapObjectsMap);
@@ -453,6 +457,7 @@ class NativeObjectsExplorer {
453457
Isolate* isolate_;
454458
HeapSnapshot* snapshot_;
455459
StringsStorage* names_;
460+
HeapObjectsMap* heap_object_map_;
456461
std::unique_ptr<HeapEntriesAllocator> embedder_graph_entries_allocator_;
457462
// Used during references extraction.
458463
HeapSnapshotGenerator* generator_ = nullptr;

test/cctest/test-heap-profiler.cc

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "src/profiler/heap-snapshot-generator-inl.h"
4747
#include "test/cctest/cctest.h"
4848
#include "test/cctest/collector.h"
49+
#include "test/cctest/heap/heap-utils.h"
4950

5051
using i::AllocationTraceNode;
5152
using i::AllocationTraceTree;
@@ -1693,6 +1694,154 @@ TEST(HeapSnapshotRetainedObjectInfo) {
16931694
CHECK_EQ(native_group_ccc, GetChildByName(n_CCC, "ccc-group"));
16941695
}
16951696

1697+
namespace {
1698+
1699+
class EmbedderGraphBuilderForNativeSnapshotObjectId final {
1700+
public:
1701+
class RegularNode : public v8::EmbedderGraph::Node {
1702+
public:
1703+
RegularNode(v8::NativeObject native_object, const char* name, size_t size,
1704+
Node* wrapper_node)
1705+
: name_(name),
1706+
size_(size),
1707+
native_object_(native_object),
1708+
wrapper_node_(wrapper_node) {}
1709+
// v8::EmbedderGraph::Node
1710+
const char* Name() override { return name_; }
1711+
size_t SizeInBytes() override { return size_; }
1712+
Node* WrapperNode() override { return wrapper_node_; }
1713+
v8::NativeObject GetNativeObject() override {
1714+
return native_object_ ? native_object_ : this;
1715+
}
1716+
1717+
private:
1718+
const char* name_;
1719+
size_t size_;
1720+
v8::NativeObject native_object_;
1721+
Node* wrapper_node_;
1722+
};
1723+
1724+
class RootNode : public RegularNode {
1725+
public:
1726+
explicit RootNode(const char* name)
1727+
: RegularNode(nullptr, name, 0, nullptr) {}
1728+
// v8::EmbedderGraph::EmbedderNode
1729+
bool IsRootNode() override { return true; }
1730+
};
1731+
1732+
struct BuildParameter {
1733+
v8::Persistent<v8::String>* wrapper;
1734+
void* native1;
1735+
void* native2;
1736+
};
1737+
1738+
static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph,
1739+
void* data) {
1740+
BuildParameter* parameter = reinterpret_cast<BuildParameter*>(data);
1741+
v8::Local<v8::String> local_str =
1742+
v8::Local<v8::String>::New(isolate, *(parameter->wrapper));
1743+
auto* v8_node = graph->V8Node(local_str);
1744+
CHECK(!v8_node->IsEmbedderNode());
1745+
auto* root_node =
1746+
graph->AddNode(std::unique_ptr<RootNode>(new RootNode("root")));
1747+
auto* non_merged_node = graph->AddNode(std::unique_ptr<RegularNode>(
1748+
new RegularNode(parameter->native1, "non-merged", 0, nullptr)));
1749+
auto* merged_node = graph->AddNode(std::unique_ptr<RegularNode>(
1750+
new RegularNode(parameter->native2, "merged", 0, v8_node)));
1751+
graph->AddEdge(root_node, non_merged_node);
1752+
graph->AddEdge(root_node, merged_node);
1753+
}
1754+
};
1755+
1756+
} // namespace
1757+
1758+
TEST(NativeSnapshotObjectId) {
1759+
LocalContext env;
1760+
v8::Isolate* isolate = env->GetIsolate();
1761+
v8::HandleScope scope(isolate);
1762+
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1763+
1764+
v8::Persistent<v8::String> wrapper(isolate, v8_str("wrapper"));
1765+
int native1;
1766+
int native2;
1767+
1768+
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildParameter parameter{
1769+
&wrapper, &native1, &native2};
1770+
heap_profiler->AddBuildEmbedderGraphCallback(
1771+
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildEmbedderGraph,
1772+
&parameter);
1773+
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1774+
CHECK(ValidateSnapshot(snapshot));
1775+
1776+
v8::SnapshotObjectId non_merged_id = heap_profiler->GetObjectId(&native1);
1777+
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, non_merged_id);
1778+
v8::SnapshotObjectId merged_id = heap_profiler->GetObjectId(&native2);
1779+
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, merged_id);
1780+
CHECK_NE(non_merged_id, merged_id);
1781+
const v8::HeapGraphNode* non_merged_node =
1782+
snapshot->GetNodeById(non_merged_id);
1783+
CHECK_NOT_NULL(non_merged_node);
1784+
const v8::HeapGraphNode* merged_node = snapshot->GetNodeById(merged_id);
1785+
CHECK_NOT_NULL(merged_node);
1786+
1787+
heap_profiler->ClearObjectIds();
1788+
CHECK_EQ(v8::HeapProfiler::kUnknownObjectId,
1789+
heap_profiler->GetObjectId(&native1));
1790+
CHECK_EQ(v8::HeapProfiler::kUnknownObjectId,
1791+
heap_profiler->GetObjectId(&native2));
1792+
}
1793+
1794+
TEST(NativeSnapshotObjectIdMoving) {
1795+
// Required to allow moving specific objects.
1796+
i::FLAG_manual_evacuation_candidates_selection = true;
1797+
1798+
LocalContext env;
1799+
v8::Isolate* isolate = env->GetIsolate();
1800+
v8::HandleScope scope(isolate);
1801+
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1802+
heap_profiler->StartTrackingHeapObjects(true);
1803+
1804+
v8::Persistent<v8::String> wrapper(isolate, v8_str("wrapper"));
1805+
int native1;
1806+
int native2;
1807+
1808+
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildParameter parameter{
1809+
&wrapper, &native1, &native2};
1810+
heap_profiler->AddBuildEmbedderGraphCallback(
1811+
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildEmbedderGraph,
1812+
&parameter);
1813+
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1814+
CHECK(ValidateSnapshot(snapshot));
1815+
1816+
v8::SnapshotObjectId non_merged_id = heap_profiler->GetObjectId(&native1);
1817+
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, non_merged_id);
1818+
v8::SnapshotObjectId merged_id = heap_profiler->GetObjectId(&native2);
1819+
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, merged_id);
1820+
CHECK_NE(non_merged_id, merged_id);
1821+
const v8::HeapGraphNode* non_merged_node =
1822+
snapshot->GetNodeById(non_merged_id);
1823+
CHECK_NOT_NULL(non_merged_node);
1824+
const v8::HeapGraphNode* merged_node = snapshot->GetNodeById(merged_id);
1825+
CHECK_NOT_NULL(merged_node);
1826+
1827+
{
1828+
v8::HandleScope scope(isolate);
1829+
auto local = v8::Local<v8::String>::New(isolate, wrapper);
1830+
i::Handle<i::String> internal = i::Handle<i::String>::cast(
1831+
v8::Utils::OpenHandle(*v8::Local<v8::String>::Cast(local)));
1832+
i::heap::ForceEvacuationCandidate(i::Page::FromHeapObject(*internal));
1833+
}
1834+
CcTest::CollectAllGarbage();
1835+
1836+
non_merged_id = heap_profiler->GetObjectId(&native1);
1837+
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, non_merged_id);
1838+
merged_id = heap_profiler->GetObjectId(&native2);
1839+
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, merged_id);
1840+
CHECK_NE(non_merged_id, merged_id);
1841+
1842+
heap_profiler->StopTrackingHeapObjects();
1843+
}
1844+
16961845
TEST(DeleteAllHeapSnapshots) {
16971846
LocalContext env;
16981847
v8::HandleScope scope(env->GetIsolate());

0 commit comments

Comments
 (0)