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(
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(
const CodeEventsContainer& evt_rec) {
CodeEventsContainer record = evt_rec;
......
......@@ -268,6 +268,7 @@ class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver {
CodeEntryStorage* code_entries() { return &code_entries_; }
CodeMap* code_map() { return &code_map_; }
WeakCodeRegistry* weak_code_registry() { return &weak_code_registry_; }
size_t GetEstimatedMemoryUsage() const;
void ClearCodeMap();
......
......@@ -64,6 +64,11 @@ int SourcePositionTable::GetInliningId(int pc_offset) const {
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 {
base::OS::Print(" - source position table at %p\n", this);
for (const SourcePositionTuple& pos_info : pc_offsets_to_lines_) {
......@@ -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() {
DCHECK(has_deopt_info());
......@@ -423,9 +459,7 @@ class DeleteNodesCallback {
public:
void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
void AfterAllChildrenTraversed(ProfileNode* node) {
delete node;
}
void AfterAllChildrenTraversed(ProfileNode* node) { delete node; }
void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
};
......@@ -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)
: profiler_(nullptr), current_profiles_semaphore_(1) {}
......
......@@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE SourcePositionTable : public Malloced {
int GetSourceLineNumber(int pc_offset) const;
int GetInliningId(int pc_offset) const;
size_t Size() const;
void print() const;
private:
......@@ -98,6 +99,7 @@ class CodeEntry {
void set_deopt_info(const char* deopt_reason, int deopt_id,
std::vector<CpuProfileDeoptFrame> inlined_frames);
size_t EstimatedSize() const;
CpuProfileDeoptInfo GetDeoptInfo();
bool has_deopt_info() const {
return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId;
......@@ -491,6 +493,8 @@ class V8_EXPORT_PRIVATE CodeMap {
void Print();
size_t size() const { return code_map_.size(); }
size_t GetEstimatedMemoryUsage() const;
CodeEntryStorage& code_entries() { return code_entries_; }
void Clear();
......
......@@ -36,6 +36,7 @@ const char* StringsStorage::GetCopy(const char* src) {
base::StrNCpy(dst, src, len);
dst[len] = '\0';
entry->key = dst.begin();
string_size_ += len;
}
entry->value =
reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) + 1);
......@@ -56,6 +57,7 @@ const char* StringsStorage::AddOrDisposeString(char* str, int len) {
if (entry->value == nullptr) {
// New entry added.
entry->key = str;
string_size_ += len;
} else {
DeleteArray(str);
}
......@@ -156,6 +158,7 @@ bool StringsStorage::Release(const char* str) {
reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) - 1);
if (entry->value == 0) {
string_size_ -= len;
names_.Remove(const_cast<char*>(str), hash);
DeleteArray(str);
}
......@@ -166,6 +169,11 @@ size_t StringsStorage::GetStringCountForTesting() const {
return names_.occupancy();
}
size_t StringsStorage::GetStringSize() {
base::MutexGuard guard(&mutex_);
return string_size_;
}
base::HashMap::Entry* StringsStorage::GetEntry(const char* str, int len) {
uint32_t hash = ComputeStringHash(str, len);
return names_.LookupOrInsert(const_cast<char*>(str), hash);
......
......@@ -47,6 +47,9 @@ class V8_EXPORT_PRIVATE StringsStorage {
// Returns the number of strings in the store.
size_t GetStringCountForTesting() const;
// Returns the size of strings in the store
size_t GetStringSize();
// Returns true if the strings table is empty.
bool empty() const { return names_.occupancy() == 0; }
......@@ -62,6 +65,7 @@ class V8_EXPORT_PRIVATE StringsStorage {
base::CustomMatcherHashMap names_;
base::Mutex mutex_;
size_t string_size_ = 0;
};
} // namespace internal
......
......@@ -4319,10 +4319,17 @@ TEST(ClearUnusedWithEagerLogging) {
i::Isolate* isolate = CcTest::i_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();
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.
......@@ -4334,6 +4341,7 @@ TEST(ClearUnusedWithEagerLogging) {
"function some_func() {}"
"some_func();");
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
......@@ -4344,6 +4352,33 @@ TEST(ClearUnusedWithEagerLogging) {
// Verify that the CodeMap's size is unchanged post-GC.
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
......
......@@ -113,17 +113,23 @@ TEST_F(StringsStorageWithIsolate, Refcounting) {
const char* a = storage.GetCopy("12");
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
const char* b = storage.GetCopy("12");
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
// Ensure that we deduplicate the string.
CHECK_EQ(a, b);
CHECK(storage.Release(a));
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
CHECK(storage.Release(b));
CHECK_EQ(storage.GetStringCountForTesting(), 0);
CHECK_EQ(0, storage.GetStringSize());
#if !DEBUG
CHECK(!storage.Release("12"));
#endif // !DEBUG
......@@ -131,16 +137,21 @@ TEST_F(StringsStorageWithIsolate, Refcounting) {
// Verify that other constructors refcount as intended.
const char* c = storage.GetFormatted("%d", 12);
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
const char* d = storage.GetName(12);
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
CHECK_EQ(c, d);
CHECK(storage.Release(c));
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(2, storage.GetStringSize());
CHECK(storage.Release(d));
CHECK_EQ(storage.GetStringCountForTesting(), 0);
CHECK_EQ(0, storage.GetStringSize());
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