1 // Copyright 2015 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/trace_event/memory_allocator_dump.h"
6
7 #include <string.h>
8
9 #include "base/format_macros.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/notreached.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/trace_event/memory_dump_manager.h"
14 #include "base/trace_event/memory_dump_provider.h"
15 #include "base/trace_event/process_memory_dump.h"
16 #include "base/trace_event/traced_value.h"
17 #include "base/values.h"
18 #include "third_party/perfetto/protos/perfetto/trace/memory_graph.pbzero.h"
19 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
20
21 namespace base {
22 namespace trace_event {
23
24 const char MemoryAllocatorDump::kNameSize[] = "size";
25 const char MemoryAllocatorDump::kNameObjectCount[] = "object_count";
26 const char MemoryAllocatorDump::kTypeScalar[] = "scalar";
27 const char MemoryAllocatorDump::kTypeString[] = "string";
28 const char MemoryAllocatorDump::kUnitsBytes[] = "bytes";
29 const char MemoryAllocatorDump::kUnitsObjects[] = "objects";
30
MemoryAllocatorDump(const std::string & absolute_name,MemoryDumpLevelOfDetail level_of_detail,const MemoryAllocatorDumpGuid & guid)31 MemoryAllocatorDump::MemoryAllocatorDump(
32 const std::string& absolute_name,
33 MemoryDumpLevelOfDetail level_of_detail,
34 const MemoryAllocatorDumpGuid& guid)
35 : absolute_name_(absolute_name),
36 guid_(guid),
37 level_of_detail_(level_of_detail),
38 flags_(Flags::kDefault) {
39 // The |absolute_name| cannot be empty.
40 DCHECK(!absolute_name.empty());
41
42 // The |absolute_name| can contain slash separator, but not leading or
43 // trailing ones.
44 DCHECK(absolute_name[0] != '/' && *absolute_name.rbegin() != '/');
45 }
46
47 MemoryAllocatorDump::~MemoryAllocatorDump() = default;
48
AddScalar(const char * name,const char * units,uint64_t value)49 void MemoryAllocatorDump::AddScalar(const char* name,
50 const char* units,
51 uint64_t value) {
52 entries_.emplace_back(name, units, value);
53 }
54
AddString(const char * name,const char * units,const std::string & value)55 void MemoryAllocatorDump::AddString(const char* name,
56 const char* units,
57 const std::string& value) {
58 // String attributes are disabled in background mode.
59 if (level_of_detail_ == MemoryDumpLevelOfDetail::kBackground) {
60 NOTREACHED();
61 }
62 entries_.emplace_back(name, units, value);
63 }
64
AsValueInto(TracedValue * value) const65 void MemoryAllocatorDump::AsValueInto(TracedValue* value) const {
66 value->BeginDictionaryWithCopiedName(absolute_name_);
67 value->SetString("guid", guid_.ToString());
68 value->BeginDictionary("attrs");
69
70 for (const Entry& entry : entries_) {
71 value->BeginDictionaryWithCopiedName(entry.name);
72 switch (entry.entry_type) {
73 case Entry::kUint64:
74 value->SetString("type", kTypeScalar);
75 value->SetString("units", entry.units);
76 value->SetString("value", StringPrintf("%" PRIx64, entry.value_uint64));
77 break;
78 case Entry::kString:
79 value->SetString("type", kTypeString);
80 value->SetString("units", entry.units);
81 value->SetString("value", entry.value_string);
82 break;
83 }
84 value->EndDictionary();
85 }
86 value->EndDictionary(); // "attrs": { ... }
87 if (flags_)
88 value->SetInteger("flags", flags_);
89 value->EndDictionary(); // "allocator_name/heap_subheap": { ... }
90 }
91
AsProtoInto(perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::MemoryNode * memory_node) const92 void MemoryAllocatorDump::AsProtoInto(
93 perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::
94 MemoryNode* memory_node) const {
95 memory_node->set_id(guid_.ToUint64());
96 memory_node->set_absolute_name(absolute_name_);
97 if (flags() & kWeak) {
98 memory_node->set_weak(true);
99 }
100
101 for (const Entry& entry : entries_) {
102 if (entry.name == "size") {
103 DCHECK_EQ(entry.entry_type, Entry::EntryType::kUint64);
104 DCHECK_EQ(entry.units, kUnitsBytes);
105 memory_node->set_size_bytes(entry.value_uint64);
106 continue;
107 }
108
109 perfetto::protos::pbzero::MemoryTrackerSnapshot_ProcessSnapshot::
110 MemoryNode::MemoryNodeEntry* proto_memory_node_entry =
111 memory_node->add_entries();
112
113 proto_memory_node_entry->set_name(entry.name);
114 switch (entry.entry_type) {
115 case Entry::EntryType::kUint64:
116 proto_memory_node_entry->set_value_uint64(entry.value_uint64);
117 break;
118 case Entry::EntryType::kString:
119 proto_memory_node_entry->set_value_string(entry.value_string);
120 break;
121 }
122 if (entry.units == kUnitsBytes) {
123 proto_memory_node_entry->set_units(
124 perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::
125 MemoryNode::MemoryNodeEntry::BYTES);
126 } else if (entry.units == kUnitsObjects) {
127 proto_memory_node_entry->set_units(
128 perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::
129 MemoryNode::MemoryNodeEntry::COUNT);
130 } else {
131 proto_memory_node_entry->set_units(
132 perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::
133 MemoryNode::MemoryNodeEntry::UNSPECIFIED);
134 }
135 }
136 }
137
GetSizeInternal() const138 uint64_t MemoryAllocatorDump::GetSizeInternal() const {
139 if (cached_size_.has_value())
140 return *cached_size_;
141 for (const auto& entry : entries_) {
142 if (entry.entry_type == Entry::kUint64 && entry.units == kUnitsBytes &&
143 strcmp(entry.name.c_str(), kNameSize) == 0) {
144 cached_size_ = entry.value_uint64;
145 return entry.value_uint64;
146 }
147 }
148 return 0;
149 }
150
Entry()151 MemoryAllocatorDump::Entry::Entry() : entry_type(kString), value_uint64() {}
152 MemoryAllocatorDump::Entry::Entry(MemoryAllocatorDump::Entry&&) noexcept =
153 default;
154 MemoryAllocatorDump::Entry& MemoryAllocatorDump::Entry::operator=(
155 MemoryAllocatorDump::Entry&&) = default;
Entry(std::string name,std::string units,uint64_t value)156 MemoryAllocatorDump::Entry::Entry(std::string name,
157 std::string units,
158 uint64_t value)
159 : name(name), units(units), entry_type(kUint64), value_uint64(value) {}
Entry(std::string name,std::string units,std::string value)160 MemoryAllocatorDump::Entry::Entry(std::string name,
161 std::string units,
162 std::string value)
163 : name(name), units(units), entry_type(kString), value_string(value) {}
164
operator ==(const Entry & rhs) const165 bool MemoryAllocatorDump::Entry::operator==(const Entry& rhs) const {
166 if (!(name == rhs.name && units == rhs.units && entry_type == rhs.entry_type))
167 return false;
168 switch (entry_type) {
169 case EntryType::kUint64:
170 return value_uint64 == rhs.value_uint64;
171 case EntryType::kString:
172 return value_string == rhs.value_string;
173 }
174 NOTREACHED();
175 }
176
PrintTo(const MemoryAllocatorDump::Entry & entry,std::ostream * out)177 void PrintTo(const MemoryAllocatorDump::Entry& entry, std::ostream* out) {
178 switch (entry.entry_type) {
179 case MemoryAllocatorDump::Entry::EntryType::kUint64:
180 *out << "<Entry(\"" << entry.name << "\", \"" << entry.units << "\", "
181 << entry.value_uint64 << ")>";
182 return;
183 case MemoryAllocatorDump::Entry::EntryType::kString:
184 *out << "<Entry(\"" << entry.name << "\", \"" << entry.units << "\", \""
185 << entry.value_string << "\")>";
186 return;
187 }
188 NOTREACHED();
189 }
190
191 } // namespace trace_event
192 } // namespace base
193