// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/trace_event/memory_allocator_dump.h" #include #include "base/format_macros.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/strings/stringprintf.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/memory_dump_provider.h" #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/traced_value.h" #include "base/values.h" #include "third_party/perfetto/protos/perfetto/trace/memory_graph.pbzero.h" #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" namespace base { namespace trace_event { const char MemoryAllocatorDump::kNameSize[] = "size"; const char MemoryAllocatorDump::kNameObjectCount[] = "object_count"; const char MemoryAllocatorDump::kTypeScalar[] = "scalar"; const char MemoryAllocatorDump::kTypeString[] = "string"; const char MemoryAllocatorDump::kUnitsBytes[] = "bytes"; const char MemoryAllocatorDump::kUnitsObjects[] = "objects"; MemoryAllocatorDump::MemoryAllocatorDump( const std::string& absolute_name, MemoryDumpLevelOfDetail level_of_detail, const MemoryAllocatorDumpGuid& guid) : absolute_name_(absolute_name), guid_(guid), level_of_detail_(level_of_detail), flags_(Flags::kDefault) { // The |absolute_name| cannot be empty. DCHECK(!absolute_name.empty()); // The |absolute_name| can contain slash separator, but not leading or // trailing ones. DCHECK(absolute_name[0] != '/' && *absolute_name.rbegin() != '/'); } MemoryAllocatorDump::~MemoryAllocatorDump() = default; void MemoryAllocatorDump::AddScalar(const char* name, const char* units, uint64_t value) { entries_.emplace_back(name, units, value); } void MemoryAllocatorDump::AddString(const char* name, const char* units, const std::string& value) { // String attributes are disabled in background mode. if (level_of_detail_ == MemoryDumpLevelOfDetail::kBackground) { NOTREACHED(); } entries_.emplace_back(name, units, value); } void MemoryAllocatorDump::AsValueInto(TracedValue* value) const { value->BeginDictionaryWithCopiedName(absolute_name_); value->SetString("guid", guid_.ToString()); value->BeginDictionary("attrs"); for (const Entry& entry : entries_) { value->BeginDictionaryWithCopiedName(entry.name); switch (entry.entry_type) { case Entry::kUint64: value->SetString("type", kTypeScalar); value->SetString("units", entry.units); value->SetString("value", StringPrintf("%" PRIx64, entry.value_uint64)); break; case Entry::kString: value->SetString("type", kTypeString); value->SetString("units", entry.units); value->SetString("value", entry.value_string); break; } value->EndDictionary(); } value->EndDictionary(); // "attrs": { ... } if (flags_) value->SetInteger("flags", flags_); value->EndDictionary(); // "allocator_name/heap_subheap": { ... } } void MemoryAllocatorDump::AsProtoInto( perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot:: MemoryNode* memory_node) const { memory_node->set_id(guid_.ToUint64()); memory_node->set_absolute_name(absolute_name_); if (flags() & kWeak) { memory_node->set_weak(true); } for (const Entry& entry : entries_) { if (entry.name == "size") { DCHECK_EQ(entry.entry_type, Entry::EntryType::kUint64); DCHECK_EQ(entry.units, kUnitsBytes); memory_node->set_size_bytes(entry.value_uint64); continue; } perfetto::protos::pbzero::MemoryTrackerSnapshot_ProcessSnapshot:: MemoryNode::MemoryNodeEntry* proto_memory_node_entry = memory_node->add_entries(); proto_memory_node_entry->set_name(entry.name); switch (entry.entry_type) { case Entry::EntryType::kUint64: proto_memory_node_entry->set_value_uint64(entry.value_uint64); break; case Entry::EntryType::kString: proto_memory_node_entry->set_value_string(entry.value_string); break; } if (entry.units == kUnitsBytes) { proto_memory_node_entry->set_units( perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot:: MemoryNode::MemoryNodeEntry::BYTES); } else if (entry.units == kUnitsObjects) { proto_memory_node_entry->set_units( perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot:: MemoryNode::MemoryNodeEntry::COUNT); } else { proto_memory_node_entry->set_units( perfetto::protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot:: MemoryNode::MemoryNodeEntry::UNSPECIFIED); } } } uint64_t MemoryAllocatorDump::GetSizeInternal() const { if (cached_size_.has_value()) return *cached_size_; for (const auto& entry : entries_) { if (entry.entry_type == Entry::kUint64 && entry.units == kUnitsBytes && strcmp(entry.name.c_str(), kNameSize) == 0) { cached_size_ = entry.value_uint64; return entry.value_uint64; } } return 0; } MemoryAllocatorDump::Entry::Entry() : entry_type(kString), value_uint64() {} MemoryAllocatorDump::Entry::Entry(MemoryAllocatorDump::Entry&&) noexcept = default; MemoryAllocatorDump::Entry& MemoryAllocatorDump::Entry::operator=( MemoryAllocatorDump::Entry&&) = default; MemoryAllocatorDump::Entry::Entry(std::string name, std::string units, uint64_t value) : name(name), units(units), entry_type(kUint64), value_uint64(value) {} MemoryAllocatorDump::Entry::Entry(std::string name, std::string units, std::string value) : name(name), units(units), entry_type(kString), value_string(value) {} bool MemoryAllocatorDump::Entry::operator==(const Entry& rhs) const { if (!(name == rhs.name && units == rhs.units && entry_type == rhs.entry_type)) return false; switch (entry_type) { case EntryType::kUint64: return value_uint64 == rhs.value_uint64; case EntryType::kString: return value_string == rhs.value_string; } NOTREACHED(); } void PrintTo(const MemoryAllocatorDump::Entry& entry, std::ostream* out) { switch (entry.entry_type) { case MemoryAllocatorDump::Entry::EntryType::kUint64: *out << ""; return; case MemoryAllocatorDump::Entry::EntryType::kString: *out << ""; return; } NOTREACHED(); } } // namespace trace_event } // namespace base