| // 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 <string.h> |
| |
| #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::DEFAULT) { |
| // 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::BACKGROUND) { |
| NOTREACHED(); |
| return; |
| } |
| entries_.emplace_back(name, units, value); |
| } |
| |
| void MemoryAllocatorDump::AsValueInto(TracedValue* value) const { |
| std::string string_conversion_buffer; |
| 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: |
| SStringPrintf(&string_conversion_buffer, "%" PRIx64, |
| entry.value_uint64); |
| value->SetString("type", kTypeScalar); |
| value->SetString("units", entry.units); |
| value->SetString("value", string_conversion_buffer); |
| 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() & WEAK) { |
| 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(); |
| return false; |
| } |
| |
| void PrintTo(const MemoryAllocatorDump::Entry& entry, std::ostream* out) { |
| switch (entry.entry_type) { |
| case MemoryAllocatorDump::Entry::EntryType::kUint64: |
| *out << "<Entry(\"" << entry.name << "\", \"" << entry.units << "\", " |
| << entry.value_uint64 << ")>"; |
| return; |
| case MemoryAllocatorDump::Entry::EntryType::kString: |
| *out << "<Entry(\"" << entry.name << "\", \"" << entry.units << "\", \"" |
| << entry.value_string << "\")>"; |
| return; |
| } |
| NOTREACHED(); |
| } |
| |
| } // namespace trace_event |
| } // namespace base |