1// Copyright 2015 The Chromium Authors. All rights reserved.
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/trace_event_memory_overhead.h"
6
7#include <algorithm>
8
9#include "base/bits.h"
10#include "base/memory/ref_counted_memory.h"
11#include "base/strings/stringprintf.h"
12#include "base/trace_event/memory_allocator_dump.h"
13#include "base/trace_event/process_memory_dump.h"
14#include "base/values.h"
15
16namespace base {
17namespace trace_event {
18
19TraceEventMemoryOverhead::TraceEventMemoryOverhead() {
20}
21
22TraceEventMemoryOverhead::~TraceEventMemoryOverhead() {
23}
24
25void TraceEventMemoryOverhead::AddOrCreateInternal(
26    const char* object_type,
27    size_t count,
28    size_t allocated_size_in_bytes,
29    size_t resident_size_in_bytes) {
30  auto it = allocated_objects_.find(object_type);
31  if (it == allocated_objects_.end()) {
32    allocated_objects_.insert(std::make_pair(
33        object_type,
34        ObjectCountAndSize(
35            {count, allocated_size_in_bytes, resident_size_in_bytes})));
36    return;
37  }
38  it->second.count += count;
39  it->second.allocated_size_in_bytes += allocated_size_in_bytes;
40  it->second.resident_size_in_bytes += resident_size_in_bytes;
41}
42
43void TraceEventMemoryOverhead::Add(const char* object_type,
44                                   size_t allocated_size_in_bytes) {
45  Add(object_type, allocated_size_in_bytes, allocated_size_in_bytes);
46}
47
48void TraceEventMemoryOverhead::Add(const char* object_type,
49                                   size_t allocated_size_in_bytes,
50                                   size_t resident_size_in_bytes) {
51  AddOrCreateInternal(object_type, 1, allocated_size_in_bytes,
52                      resident_size_in_bytes);
53}
54
55void TraceEventMemoryOverhead::AddString(const std::string& str) {
56  // The number below are empirical and mainly based on profiling of real-world
57  // std::string implementations:
58  //  - even short string end up malloc()-inc at least 32 bytes.
59  //  - longer strings seem to malloc() multiples of 16 bytes.
60  const size_t capacity = bits::Align(str.capacity(), 16);
61  Add("std::string", sizeof(std::string) + std::max<size_t>(capacity, 32u));
62}
63
64void TraceEventMemoryOverhead::AddRefCountedString(
65    const RefCountedString& str) {
66  Add("RefCountedString", sizeof(RefCountedString));
67  AddString(str.data());
68}
69
70void TraceEventMemoryOverhead::AddValue(const Value& value) {
71  switch (value.GetType()) {
72    case Value::TYPE_NULL:
73    case Value::TYPE_BOOLEAN:
74    case Value::TYPE_INTEGER:
75    case Value::TYPE_DOUBLE:
76      Add("FundamentalValue", sizeof(Value));
77      break;
78
79    case Value::TYPE_STRING: {
80      const StringValue* string_value = nullptr;
81      value.GetAsString(&string_value);
82      Add("StringValue", sizeof(StringValue));
83      AddString(string_value->GetString());
84    } break;
85
86    case Value::TYPE_BINARY: {
87      const BinaryValue* binary_value = nullptr;
88      value.GetAsBinary(&binary_value);
89      Add("BinaryValue", sizeof(BinaryValue) + binary_value->GetSize());
90    } break;
91
92    case Value::TYPE_DICTIONARY: {
93      const DictionaryValue* dictionary_value = nullptr;
94      value.GetAsDictionary(&dictionary_value);
95      Add("DictionaryValue", sizeof(DictionaryValue));
96      for (DictionaryValue::Iterator it(*dictionary_value); !it.IsAtEnd();
97           it.Advance()) {
98        AddString(it.key());
99        AddValue(it.value());
100      }
101    } break;
102
103    case Value::TYPE_LIST: {
104      const ListValue* list_value = nullptr;
105      value.GetAsList(&list_value);
106      Add("ListValue", sizeof(ListValue));
107      for (const Value* v : *list_value)
108        AddValue(*v);
109    } break;
110
111    default:
112      NOTREACHED();
113  }
114}
115
116void TraceEventMemoryOverhead::AddSelf() {
117  size_t estimated_size = sizeof(*this);
118  // If the SmallMap did overflow its static capacity, its elements will be
119  // allocated on the heap and have to be accounted separately.
120  if (allocated_objects_.UsingFullMap())
121    estimated_size += sizeof(map_type::value_type) * allocated_objects_.size();
122  Add("TraceEventMemoryOverhead", estimated_size);
123}
124
125size_t TraceEventMemoryOverhead::GetCount(const char* object_type) const {
126  const auto& it = allocated_objects_.find(object_type);
127  if (it == allocated_objects_.end())
128    return 0u;
129  return it->second.count;
130}
131
132void TraceEventMemoryOverhead::Update(const TraceEventMemoryOverhead& other) {
133  for (const auto& it : other.allocated_objects_) {
134    AddOrCreateInternal(it.first, it.second.count,
135                        it.second.allocated_size_in_bytes,
136                        it.second.resident_size_in_bytes);
137  }
138}
139
140void TraceEventMemoryOverhead::DumpInto(const char* base_name,
141                                        ProcessMemoryDump* pmd) const {
142  for (const auto& it : allocated_objects_) {
143    std::string dump_name = StringPrintf("%s/%s", base_name, it.first);
144    MemoryAllocatorDump* mad = pmd->CreateAllocatorDump(dump_name);
145    mad->AddScalar(MemoryAllocatorDump::kNameSize,
146                   MemoryAllocatorDump::kUnitsBytes,
147                   it.second.allocated_size_in_bytes);
148    mad->AddScalar("resident_size", MemoryAllocatorDump::kUnitsBytes,
149                   it.second.resident_size_in_bytes);
150    mad->AddScalar(MemoryAllocatorDump::kNameObjectCount,
151                   MemoryAllocatorDump::kUnitsObjects, it.second.count);
152  }
153}
154
155}  // namespace trace_event
156}  // namespace base
157