1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/memory/shared_memory_tracker.h"
6
7 #include "base/check.h"
8 #include "base/notreached.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/trace_event/base_tracing.h"
11 #include "base/tracing_buildflags.h"
12
13 #if BUILDFLAG(ENABLE_BASE_TRACING)
14 #include <optional>
15
16 #include "base/trace_event/memory_dump_manager.h" // no-presubmit-check
17 #include "base/trace_event/process_memory_dump.h" // no-presubmit-check
18 #endif // BUILDFLAG(ENABLE_BASE_TRACING)
19
20 namespace base {
21
22 const char SharedMemoryTracker::kDumpRootName[] = "shared_memory";
23
24 // static
GetInstance()25 SharedMemoryTracker* SharedMemoryTracker::GetInstance() {
26 static SharedMemoryTracker* instance = new SharedMemoryTracker;
27 return instance;
28 }
29
30 // static
GetDumpNameForTracing(const UnguessableToken & id)31 std::string SharedMemoryTracker::GetDumpNameForTracing(
32 const UnguessableToken& id) {
33 DCHECK(!id.is_empty());
34 return std::string(kDumpRootName) + "/" + id.ToString();
35 }
36
37 // static
38 trace_event::MemoryAllocatorDumpGuid
GetGlobalDumpIdForTracing(const UnguessableToken & id)39 SharedMemoryTracker::GetGlobalDumpIdForTracing(const UnguessableToken& id) {
40 std::string dump_name = GetDumpNameForTracing(id);
41 return trace_event::MemoryAllocatorDumpGuid(dump_name);
42 }
43
44 const trace_event::MemoryAllocatorDump*
GetOrCreateSharedMemoryDump(const SharedMemoryMapping & shared_memory,trace_event::ProcessMemoryDump * pmd)45 SharedMemoryTracker::GetOrCreateSharedMemoryDump(
46 const SharedMemoryMapping& shared_memory,
47 trace_event::ProcessMemoryDump* pmd) {
48 return GetOrCreateSharedMemoryDumpInternal(shared_memory.raw_memory_ptr(),
49 shared_memory.mapped_size(),
50 shared_memory.guid(), pmd);
51 }
52
IncrementMemoryUsage(const SharedMemoryMapping & mapping)53 void SharedMemoryTracker::IncrementMemoryUsage(
54 const SharedMemoryMapping& mapping) {
55 AutoLock hold(usages_lock_);
56 DCHECK(usages_.find(mapping.raw_memory_ptr()) == usages_.end());
57 usages_.emplace(mapping.raw_memory_ptr(),
58 UsageInfo(mapping.mapped_size(), mapping.guid()));
59 }
60
DecrementMemoryUsage(const SharedMemoryMapping & mapping)61 void SharedMemoryTracker::DecrementMemoryUsage(
62 const SharedMemoryMapping& mapping) {
63 AutoLock hold(usages_lock_);
64 const auto it = usages_.find(mapping.raw_memory_ptr());
65 // TODO(pbos): When removing this NotFatalUntil, use erase(it) below. We can't
66 // do that now because if this CHECK is actually failing there'd be a memory
67 // bug.
68 CHECK(it != usages_.end(), base::NotFatalUntil::M125);
69 usages_.erase(mapping.raw_memory_ptr());
70 }
71
SharedMemoryTracker()72 SharedMemoryTracker::SharedMemoryTracker() {
73 #if BUILDFLAG(ENABLE_BASE_TRACING)
74 trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
75 this, "SharedMemoryTracker", nullptr);
76 #endif // BUILDFLAG(ENABLE_BASE_TRACING)
77 }
78
79 SharedMemoryTracker::~SharedMemoryTracker() = default;
80
OnMemoryDump(const trace_event::MemoryDumpArgs & args,trace_event::ProcessMemoryDump * pmd)81 bool SharedMemoryTracker::OnMemoryDump(const trace_event::MemoryDumpArgs& args,
82 trace_event::ProcessMemoryDump* pmd) {
83 AutoLock hold(usages_lock_);
84 for (const auto& usage : usages_) {
85 const trace_event::MemoryAllocatorDump* dump =
86 GetOrCreateSharedMemoryDumpInternal(
87 usage.first, usage.second.mapped_size, usage.second.mapped_id, pmd);
88 DCHECK(dump);
89 }
90 return true;
91 }
92
93 // static
94 const trace_event::MemoryAllocatorDump*
GetOrCreateSharedMemoryDumpInternal(void * mapped_memory,size_t mapped_size,const UnguessableToken & mapped_id,trace_event::ProcessMemoryDump * pmd)95 SharedMemoryTracker::GetOrCreateSharedMemoryDumpInternal(
96 void* mapped_memory,
97 size_t mapped_size,
98 const UnguessableToken& mapped_id,
99 trace_event::ProcessMemoryDump* pmd) {
100 #if BUILDFLAG(ENABLE_BASE_TRACING)
101 const std::string dump_name = GetDumpNameForTracing(mapped_id);
102 trace_event::MemoryAllocatorDump* local_dump =
103 pmd->GetAllocatorDump(dump_name);
104 if (local_dump)
105 return local_dump;
106
107 size_t virtual_size = mapped_size;
108 // If resident size is not available, a virtual size is used as fallback.
109 size_t size = virtual_size;
110 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
111 std::optional<size_t> resident_size =
112 trace_event::ProcessMemoryDump::CountResidentBytesInSharedMemory(
113 mapped_memory, mapped_size);
114 if (resident_size.has_value())
115 size = resident_size.value();
116 #endif
117
118 local_dump = pmd->CreateAllocatorDump(dump_name);
119 local_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
120 trace_event::MemoryAllocatorDump::kUnitsBytes, size);
121 local_dump->AddScalar("virtual_size",
122 trace_event::MemoryAllocatorDump::kUnitsBytes,
123 virtual_size);
124 auto global_dump_guid = GetGlobalDumpIdForTracing(mapped_id);
125 trace_event::MemoryAllocatorDump* global_dump =
126 pmd->CreateSharedGlobalAllocatorDump(global_dump_guid);
127 global_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
128 trace_event::MemoryAllocatorDump::kUnitsBytes, size);
129
130 // The edges will be overriden by the clients with correct importance.
131 pmd->AddOverridableOwnershipEdge(local_dump->guid(), global_dump->guid(),
132 0 /* importance */);
133 return local_dump;
134 #else // BUILDFLAG(ENABLE_BASE_TRACING)
135 NOTREACHED();
136 return nullptr;
137 #endif // BUILDFLAG(ENABLE_BASE_TRACING)
138 }
139
140 } // namespace
141