Commit 731fd3f5 authored by Corentin Pescheloche's avatar Corentin Pescheloche Committed by V8 LUCI CQ

[cpu-profiler] Add method to estimate mem size of ProfilerCodeObserver

This patchset introduces instrumentation of the memory usage of the
datatructures maintained by the CPU profiler.
It captures:
* The total size of the strings held in StringsStorage for CodeEntries
* Estimated size held by CodeMap's entries.

The target is to surface that metric through telemetry to get better
visibility into the memory profile of CpuProfiler.

For now, STL containers overhead is ignored as it is implementation
specific.

Change-Id: I8c6a0cd4f14348fe8832dec1f24861befc67d700
Bug: chromium:1241491
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3101580
Auto-Submit: Corentin Pescheloche <cpescheloche@fb.com>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76438}
parent 7c25cfcf
...@@ -361,6 +361,16 @@ void ProfilerCodeObserver::CodeEventHandler( ...@@ -361,6 +361,16 @@ void ProfilerCodeObserver::CodeEventHandler(
CodeEventHandlerInternal(evt_rec); CodeEventHandlerInternal(evt_rec);
} }
size_t ProfilerCodeObserver::GetEstimatedMemoryUsage() const {
// To avoid race condition in codemap,
// for now limit computation in kEagerLogging mode
if (!processor_) {
return sizeof(*this) + code_map_.GetEstimatedMemoryUsage() +
code_entries_.strings().GetStringSize();
}
return 0;
}
void ProfilerCodeObserver::CodeEventHandlerInternal( void ProfilerCodeObserver::CodeEventHandlerInternal(
const CodeEventsContainer& evt_rec) { const CodeEventsContainer& evt_rec) {
CodeEventsContainer record = evt_rec; CodeEventsContainer record = evt_rec;
......
...@@ -268,6 +268,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver { ...@@ -268,6 +268,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver {
CodeEntryStorage* code_entries() { return &code_entries_; } CodeEntryStorage* code_entries() { return &code_entries_; }
CodeMap* code_map() { return &code_map_; } CodeMap* code_map() { return &code_map_; }
WeakCodeRegistry* weak_code_registry() { return &weak_code_registry_; } WeakCodeRegistry* weak_code_registry() { return &weak_code_registry_; }
size_t GetEstimatedMemoryUsage() const;
void ClearCodeMap(); void ClearCodeMap();
......
...@@ -64,6 +64,11 @@ int SourcePositionTable::GetInliningId(int pc_offset) const { ...@@ -64,6 +64,11 @@ int SourcePositionTable::GetInliningId(int pc_offset) const {
return it->inlining_id; return it->inlining_id;
} }
size_t SourcePositionTable::Size() const {
return sizeof(*this) + pc_offsets_to_lines_.capacity() *
sizeof(decltype(pc_offsets_to_lines_)::value_type);
}
void SourcePositionTable::print() const { void SourcePositionTable::print() const {
base::OS::Print(" - source position table at %p\n", this); base::OS::Print(" - source position table at %p\n", this);
for (const SourcePositionTuple& pos_info : pc_offsets_to_lines_) { for (const SourcePositionTuple& pos_info : pc_offsets_to_lines_) {
...@@ -207,6 +212,37 @@ void CodeEntry::FillFunctionInfo(SharedFunctionInfo shared) { ...@@ -207,6 +212,37 @@ void CodeEntry::FillFunctionInfo(SharedFunctionInfo shared) {
} }
} }
size_t CodeEntry::EstimatedSize() const {
size_t estimated_size = 0;
if (rare_data_) {
estimated_size += sizeof(rare_data_.get());
for (const auto& inline_entry : rare_data_->inline_entries_) {
estimated_size += inline_entry->EstimatedSize();
}
estimated_size += rare_data_->inline_entries_.size() *
sizeof(decltype(rare_data_->inline_entries_)::value_type);
for (const auto& inline_stack_pair : rare_data_->inline_stacks_) {
estimated_size += inline_stack_pair.second.size() *
sizeof(decltype(inline_stack_pair.second)::value_type);
}
estimated_size +=
rare_data_->inline_stacks_.size() *
(sizeof(decltype(rare_data_->inline_stacks_)::key_type) +
sizeof(decltype(rare_data_->inline_stacks_)::value_type));
estimated_size +=
rare_data_->deopt_inlined_frames_.capacity() *
sizeof(decltype(rare_data_->deopt_inlined_frames_)::value_type);
}
if (line_info_) {
estimated_size += line_info_.get()->Size();
}
return sizeof(*this) + estimated_size;
}
CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() { CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() {
DCHECK(has_deopt_info()); DCHECK(has_deopt_info());
...@@ -423,9 +459,7 @@ class DeleteNodesCallback { ...@@ -423,9 +459,7 @@ class DeleteNodesCallback {
public: public:
void BeforeTraversingChild(ProfileNode*, ProfileNode*) { } void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
void AfterAllChildrenTraversed(ProfileNode* node) { void AfterAllChildrenTraversed(ProfileNode* node) { delete node; }
delete node;
}
void AfterChildTraversed(ProfileNode*, ProfileNode*) { } void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
}; };
...@@ -845,6 +879,15 @@ void CodeMap::Print() { ...@@ -845,6 +879,15 @@ void CodeMap::Print() {
} }
} }
size_t CodeMap::GetEstimatedMemoryUsage() const {
size_t map_size = 0;
for (const auto& pair : code_map_) {
map_size += sizeof(pair.first) + sizeof(pair.second) +
pair.second.entry->EstimatedSize();
}
return sizeof(*this) + map_size;
}
CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate) CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
: profiler_(nullptr), current_profiles_semaphore_(1) {} : profiler_(nullptr), current_profiles_semaphore_(1) {}
......
...@@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE SourcePositionTable : public Malloced { ...@@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE SourcePositionTable : public Malloced {
int GetSourceLineNumber(int pc_offset) const; int GetSourceLineNumber(int pc_offset) const;
int GetInliningId(int pc_offset) const; int GetInliningId(int pc_offset) const;
size_t Size() const;
void print() const; void print() const;
private: private:
...@@ -98,6 +99,7 @@ class CodeEntry { ...@@ -98,6 +99,7 @@ class CodeEntry {
void set_deopt_info(const char* deopt_reason, int deopt_id, void set_deopt_info(const char* deopt_reason, int deopt_id,
std::vector<CpuProfileDeoptFrame> inlined_frames); std::vector<CpuProfileDeoptFrame> inlined_frames);
size_t EstimatedSize() const;
CpuProfileDeoptInfo GetDeoptInfo(); CpuProfileDeoptInfo GetDeoptInfo();
bool has_deopt_info() const { bool has_deopt_info() const {
return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId; return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId;
...@@ -491,6 +493,8 @@ class V8_EXPORT_PRIVATE CodeMap { ...@@ -491,6 +493,8 @@ class V8_EXPORT_PRIVATE CodeMap {
void Print(); void Print();
size_t size() const { return code_map_.size(); } size_t size() const { return code_map_.size(); }
size_t GetEstimatedMemoryUsage() const;
CodeEntryStorage& code_entries() { return code_entries_; } CodeEntryStorage& code_entries() { return code_entries_; }
void Clear(); void Clear();
......
...@@ -36,6 +36,7 @@ const char* StringsStorage::GetCopy(const char* src) { ...@@ -36,6 +36,7 @@ const char* StringsStorage::GetCopy(const char* src) {
base::StrNCpy(dst, src, len); base::StrNCpy(dst, src, len);
dst[len] = '\0'; dst[len] = '\0';
entry->key = dst.begin(); entry->key = dst.begin();
string_size_ += len;
} }
entry->value = entry->value =
reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) + 1); reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) + 1);
...@@ -56,6 +57,7 @@ const char* StringsStorage::AddOrDisposeString(char* str, int len) { ...@@ -56,6 +57,7 @@ const char* StringsStorage::AddOrDisposeString(char* str, int len) {
if (entry->value == nullptr) { if (entry->value == nullptr) {
// New entry added. // New entry added.
entry->key = str; entry->key = str;
string_size_ += len;
} else { } else {
DeleteArray(str); DeleteArray(str);
} }
...@@ -156,6 +158,7 @@ bool StringsStorage::Release(const char* str) { ...@@ -156,6 +158,7 @@ bool StringsStorage::Release(const char* str) {
reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) - 1); reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) - 1);
if (entry->value == 0) { if (entry->value == 0) {
string_size_ -= len;
names_.Remove(const_cast<char*>(str), hash); names_.Remove(const_cast<char*>(str), hash);
DeleteArray(str); DeleteArray(str);
} }
...@@ -166,6 +169,11 @@ size_t StringsStorage::GetStringCountForTesting() const { ...@@ -166,6 +169,11 @@ size_t StringsStorage::GetStringCountForTesting() const {
return names_.occupancy(); return names_.occupancy();
} }
size_t StringsStorage::GetStringSize() {
base::MutexGuard guard(&mutex_);
return string_size_;
}
base::HashMap::Entry* StringsStorage::GetEntry(const char* str, int len) { base::HashMap::Entry* StringsStorage::GetEntry(const char* str, int len) {
uint32_t hash = ComputeStringHash(str, len); uint32_t hash = ComputeStringHash(str, len);
return names_.LookupOrInsert(const_cast<char*>(str), hash); return names_.LookupOrInsert(const_cast<char*>(str), hash);
......
...@@ -47,6 +47,9 @@ class V8_EXPORT_PRIVATE StringsStorage { ...@@ -47,6 +47,9 @@ class V8_EXPORT_PRIVATE StringsStorage {
// Returns the number of strings in the store. // Returns the number of strings in the store.
size_t GetStringCountForTesting() const; size_t GetStringCountForTesting() const;
// Returns the size of strings in the store
size_t GetStringSize();
// Returns true if the strings table is empty. // Returns true if the strings table is empty.
bool empty() const { return names_.occupancy() == 0; } bool empty() const { return names_.occupancy() == 0; }
...@@ -62,6 +65,7 @@ class V8_EXPORT_PRIVATE StringsStorage { ...@@ -62,6 +65,7 @@ class V8_EXPORT_PRIVATE StringsStorage {
base::CustomMatcherHashMap names_; base::CustomMatcherHashMap names_;
base::Mutex mutex_; base::Mutex mutex_;
size_t string_size_ = 0;
}; };
} // namespace internal } // namespace internal
......
...@@ -4319,10 +4319,17 @@ TEST(ClearUnusedWithEagerLogging) { ...@@ -4319,10 +4319,17 @@ TEST(ClearUnusedWithEagerLogging) {
i::Isolate* isolate = CcTest::i_isolate(); i::Isolate* isolate = CcTest::i_isolate();
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
CpuProfiler profiler(isolate, kDebugNaming, kEagerLogging); CodeEntryStorage storage;
CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
ProfilerCodeObserver* code_observer =
new ProfilerCodeObserver(isolate, storage);
CpuProfiler profiler(isolate, kDebugNaming, kEagerLogging, profiles, nullptr,
nullptr, code_observer);
CodeMap* code_map = profiler.code_map_for_test(); CodeMap* code_map = profiler.code_map_for_test();
size_t initial_size = code_map->size(); size_t initial_size = code_map->size();
size_t profiler_size = code_observer->GetEstimatedMemoryUsage();
{ {
// Create and run a new script and function, generating 2 code objects. // Create and run a new script and function, generating 2 code objects.
...@@ -4334,6 +4341,7 @@ TEST(ClearUnusedWithEagerLogging) { ...@@ -4334,6 +4341,7 @@ TEST(ClearUnusedWithEagerLogging) {
"function some_func() {}" "function some_func() {}"
"some_func();"); "some_func();");
CHECK_GT(code_map->size(), initial_size); CHECK_GT(code_map->size(), initial_size);
CHECK_GT(code_observer->GetEstimatedMemoryUsage(), profiler_size);
} }
// Clear the compilation cache so that there are no more references to the // Clear the compilation cache so that there are no more references to the
...@@ -4344,6 +4352,33 @@ TEST(ClearUnusedWithEagerLogging) { ...@@ -4344,6 +4352,33 @@ TEST(ClearUnusedWithEagerLogging) {
// Verify that the CodeMap's size is unchanged post-GC. // Verify that the CodeMap's size is unchanged post-GC.
CHECK_EQ(code_map->size(), initial_size); CHECK_EQ(code_map->size(), initial_size);
CHECK_EQ(code_observer->GetEstimatedMemoryUsage(), profiler_size);
}
// Ensure that ProfilerCodeObserver doesn't compute estimated size when race
// condition potential
TEST(SkipEstimatedSizeWhenActiveProfiling) {
ManualGCScope manual_gc;
TestSetup test_setup;
i::Isolate* isolate = CcTest::i_isolate();
i::HandleScope scope(isolate);
CodeEntryStorage storage;
CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
ProfilerCodeObserver* code_observer =
new ProfilerCodeObserver(isolate, storage);
CpuProfiler profiler(isolate, kDebugNaming, kEagerLogging, profiles, nullptr,
nullptr, code_observer);
CHECK_GT(code_observer->GetEstimatedMemoryUsage(), 0);
profiler.StartProfiling("");
CHECK_EQ(code_observer->GetEstimatedMemoryUsage(), 0);
profiler.StopProfiling("");
CHECK_GT(code_observer->GetEstimatedMemoryUsage(), 0);
} }
} // namespace test_cpu_profiler } // namespace test_cpu_profiler
......
...@@ -113,17 +113,23 @@ TEST_F(StringsStorageWithIsolate, Refcounting) { ...@@ -113,17 +113,23 @@ TEST_F(StringsStorageWithIsolate, Refcounting) {
const char* a = storage.GetCopy("12"); const char* a = storage.GetCopy("12");
CHECK_EQ(storage.GetStringCountForTesting(), 1); CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
const char* b = storage.GetCopy("12"); const char* b = storage.GetCopy("12");
CHECK_EQ(storage.GetStringCountForTesting(), 1); CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
// Ensure that we deduplicate the string. // Ensure that we deduplicate the string.
CHECK_EQ(a, b); CHECK_EQ(a, b);
CHECK(storage.Release(a)); CHECK(storage.Release(a));
CHECK_EQ(storage.GetStringCountForTesting(), 1); CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
CHECK(storage.Release(b)); CHECK(storage.Release(b));
CHECK_EQ(storage.GetStringCountForTesting(), 0); CHECK_EQ(storage.GetStringCountForTesting(), 0);
CHECK_EQ(0, storage.GetStringSize());
#if !DEBUG #if !DEBUG
CHECK(!storage.Release("12")); CHECK(!storage.Release("12"));
#endif // !DEBUG #endif // !DEBUG
...@@ -131,16 +137,21 @@ TEST_F(StringsStorageWithIsolate, Refcounting) { ...@@ -131,16 +137,21 @@ TEST_F(StringsStorageWithIsolate, Refcounting) {
// Verify that other constructors refcount as intended. // Verify that other constructors refcount as intended.
const char* c = storage.GetFormatted("%d", 12); const char* c = storage.GetFormatted("%d", 12);
CHECK_EQ(storage.GetStringCountForTesting(), 1); CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
const char* d = storage.GetName(12); const char* d = storage.GetName(12);
CHECK_EQ(storage.GetStringCountForTesting(), 1); CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
CHECK_EQ(c, d); CHECK_EQ(c, d);
CHECK(storage.Release(c)); CHECK(storage.Release(c));
CHECK_EQ(storage.GetStringCountForTesting(), 1); CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
CHECK(storage.Release(d)); CHECK(storage.Release(d));
CHECK_EQ(storage.GetStringCountForTesting(), 0); CHECK_EQ(storage.GetStringCountForTesting(), 0);
CHECK_EQ(0, storage.GetStringSize());
CHECK(!storage.Release("12")); CHECK(!storage.Release("12"));
} }
......
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