Commit a937635d authored by ssid's avatar ssid Committed by Commit bot

[tracing] Add method to create "weak" global dumps

The renderer process could add memory dumps that are known to be
non-existent in the browser. To resolve this issue, a notion of "weak"
global allocator dump is introduced so that the renderer could tell it
is unsure of the existence of the dump.

A weak dump will be removed in the UI unless at least one process
creates a non-weak global dump with the same guid. This would mark the
existence of the memory.

Sample use case: The browser creates a non-weak global dump and a
renderer process creates a weak global dump for discardable segments.
This represents the fact that the renderer doesn't know whether a
segment is or isn't present in memory.

BUG=570655

Review URL: https://codereview.chromium.org/1583483002

Cr-Commit-Position: refs/heads/master@{#370683}
parent 0325c0f7
......@@ -28,7 +28,8 @@ MemoryAllocatorDump::MemoryAllocatorDump(const std::string& absolute_name,
: absolute_name_(absolute_name),
process_memory_dump_(process_memory_dump),
attributes_(new TracedValue),
guid_(guid) {
guid_(guid),
flags_(Flags::DEFAULT) {
// The |absolute_name| cannot be empty.
DCHECK(!absolute_name.empty());
......@@ -90,6 +91,8 @@ void MemoryAllocatorDump::AsValueInto(TracedValue* value) const {
value->BeginDictionaryWithCopiedName(absolute_name_);
value->SetString("guid", guid_.ToString());
value->SetValue("attrs", *attributes_);
if (flags_)
value->SetInteger("flags", flags_);
value->EndDictionary(); // "allocator_name/heap_subheap": { ... }
}
......
......@@ -26,6 +26,13 @@ class TracedValue;
// Data model for user-land memory allocator dumps.
class BASE_EXPORT MemoryAllocatorDump {
public:
enum Flags {
DEFAULT = 0,
// A dump marked weak will be discarded by TraceViewer.
WEAK = 1 << 0,
};
// MemoryAllocatorDump is owned by ProcessMemoryDump.
MemoryAllocatorDump(const std::string& absolute_name,
ProcessMemoryDump* process_memory_dump,
......@@ -68,6 +75,11 @@ class BASE_EXPORT MemoryAllocatorDump {
return process_memory_dump_;
}
// Use enum Flags to set values.
void set_flags(int flags) { flags_ |= flags; }
void clear_flags(int flags) { flags_ &= ~flags; }
int flags() { return flags_; }
// |guid| is an optional global dump identifier, unique across all processes
// within the scope of a global dump. It is only required when using the
// graph APIs (see TODO_method_name) to express retention / suballocation or
......@@ -83,6 +95,7 @@ class BASE_EXPORT MemoryAllocatorDump {
ProcessMemoryDump* const process_memory_dump_; // Not owned (PMD owns this).
scoped_refptr<TracedValue> attributes_;
MemoryAllocatorDumpGuid guid_;
int flags_; // See enum Flags.
// A local buffer for Sprintf conversion on fastpath. Avoids allocating
// temporary strings on each AddScalar() call.
......
......@@ -135,10 +135,24 @@ MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump(
const MemoryAllocatorDumpGuid& guid) {
// A shared allocator dump can be shared within a process and the guid could
// have been created already.
MemoryAllocatorDump* allocator_dump = GetSharedGlobalAllocatorDump(guid);
return allocator_dump ? allocator_dump
: CreateAllocatorDump(
GetSharedGlobalAllocatorDumpName(guid), guid);
MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid);
if (mad) {
// The weak flag is cleared because this method should create a non-weak
// dump.
mad->clear_flags(MemoryAllocatorDump::Flags::WEAK);
return mad;
}
return CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid);
}
MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump(
const MemoryAllocatorDumpGuid& guid) {
MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid);
if (mad)
return mad;
mad = CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid);
mad->set_flags(MemoryAllocatorDump::Flags::WEAK);
return mad;
}
MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump(
......
......@@ -98,6 +98,15 @@ class BASE_EXPORT ProcessMemoryDump {
MemoryAllocatorDump* CreateSharedGlobalAllocatorDump(
const MemoryAllocatorDumpGuid& guid);
// Creates a shared MemoryAllocatorDump as CreateSharedGlobalAllocatorDump,
// but with a WEAK flag. A weak dump will be discarded unless a non-weak dump
// is created using CreateSharedGlobalAllocatorDump by at least one process.
// The WEAK flag does not apply if a non-weak dump with the same GUID already
// exists or is created later. All owners and children of the discarded dump
// will also be discarded transitively.
MemoryAllocatorDump* CreateWeakSharedGlobalAllocatorDump(
const MemoryAllocatorDumpGuid& guid);
// Looks up a shared MemoryAllocatorDump given its guid.
MemoryAllocatorDump* GetSharedGlobalAllocatorDump(
const MemoryAllocatorDumpGuid& guid) const;
......
......@@ -30,8 +30,10 @@ TEST(ProcessMemoryDumpTest, Clear) {
pmd1->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
MemoryAllocatorDumpGuid(4242));
MemoryAllocatorDumpGuid shared_mad_guid(1);
pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid);
MemoryAllocatorDumpGuid shared_mad_guid1(1);
MemoryAllocatorDumpGuid shared_mad_guid2(2);
pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid2);
pmd1->Clear();
ASSERT_TRUE(pmd1->allocator_dumps().empty());
......@@ -41,7 +43,8 @@ TEST(ProcessMemoryDumpTest, Clear) {
ASSERT_FALSE(pmd1->has_process_totals());
ASSERT_FALSE(pmd1->has_process_mmaps());
ASSERT_TRUE(pmd1->process_mmaps()->vm_regions().empty());
ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
// Check that calling AsValueInto() doesn't cause a crash.
scoped_refptr<TracedValue> traced_value(new TracedValue());
......@@ -50,12 +53,17 @@ TEST(ProcessMemoryDumpTest, Clear) {
// Check that the pmd can be reused and behaves as expected.
auto mad1 = pmd1->CreateAllocatorDump("mad1");
auto mad3 = pmd1->CreateAllocatorDump("mad3");
auto shared_mad = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid);
ASSERT_EQ(3u, pmd1->allocator_dumps().size());
auto shared_mad1 = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
auto shared_mad2 =
pmd1->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2);
ASSERT_EQ(4u, pmd1->allocator_dumps().size());
ASSERT_EQ(mad1, pmd1->GetAllocatorDump("mad1"));
ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
ASSERT_EQ(mad3, pmd1->GetAllocatorDump("mad3"));
ASSERT_EQ(shared_mad, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad2->flags());
traced_value = new TracedValue();
pmd1->AsValueInto(traced_value.get());
......@@ -76,8 +84,11 @@ TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) {
auto mad2_2 = pmd2->CreateAllocatorDump("pmd2/mad2");
pmd1->AddOwnershipEdge(mad2_1->guid(), mad2_2->guid());
MemoryAllocatorDumpGuid shared_mad_guid(1);
auto shared_mad = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid);
MemoryAllocatorDumpGuid shared_mad_guid1(1);
MemoryAllocatorDumpGuid shared_mad_guid2(2);
auto shared_mad1 = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
auto shared_mad2 =
pmd2->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2);
pmd1->TakeAllDumpsFrom(pmd2.get());
......@@ -98,13 +109,15 @@ TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) {
pmd2.reset();
// Now check that |pmd1| has been effectively merged.
ASSERT_EQ(5u, pmd1->allocator_dumps().size());
ASSERT_EQ(6u, pmd1->allocator_dumps().size());
ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad1"));
ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd2/mad1"));
ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
ASSERT_EQ(2u, pmd1->allocator_dumps_edges().size());
ASSERT_EQ(shared_mad, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
ASSERT_TRUE(MemoryAllocatorDump::Flags::WEAK & shared_mad2->flags());
// Check that calling AsValueInto() doesn't cause a crash.
traced_value = new TracedValue();
......@@ -157,6 +170,30 @@ TEST(ProcessMemoryDumpTest, Suballocations) {
pmd.reset();
}
TEST(ProcessMemoryDumpTest, GlobalAllocatorDumpTest) {
scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump(nullptr));
MemoryAllocatorDumpGuid shared_mad_guid(1);
auto shared_mad1 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
ASSERT_EQ(shared_mad_guid, shared_mad1->guid());
ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
auto shared_mad2 = pmd->GetSharedGlobalAllocatorDump(shared_mad_guid);
ASSERT_EQ(shared_mad1, shared_mad2);
ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
auto shared_mad3 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
ASSERT_EQ(shared_mad1, shared_mad3);
ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
auto shared_mad4 = pmd->CreateSharedGlobalAllocatorDump(shared_mad_guid);
ASSERT_EQ(shared_mad1, shared_mad4);
ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
auto shared_mad5 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
ASSERT_EQ(shared_mad1, shared_mad5);
ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
}
#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
TEST(ProcessMemoryDumpTest, CountResidentBytes) {
const size_t page_size = base::GetPageSize();
......
......@@ -418,12 +418,14 @@ void DiscardableSharedMemoryHeap::OnMemoryDump(
// to avoid double-counting segments when both browser and child process emit
// them. In the special case of single-process-mode, this will be the only
// dumper active and the single ownership edge will become a no-op in the UI.
// The global dump is created as a weak dump so that the segment is removed if
// the browser does not dump it (segment was purged).
const uint64_t tracing_process_id =
base::trace_event::MemoryDumpManager::GetInstance()
->GetTracingProcessId();
base::trace_event::MemoryAllocatorDumpGuid shared_segment_guid =
GetSegmentGUIDForTracing(tracing_process_id, segment_id);
pmd->CreateSharedGlobalAllocatorDump(shared_segment_guid);
pmd->CreateWeakSharedGlobalAllocatorDump(shared_segment_guid);
// The size is added to the global dump so that it gets propagated to both the
// dumps associated.
......
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