Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: components/metrics/leak_detector/leak_detector_impl.cc

Issue 986503002: components/metrics: Add runtime memory leak detector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Mac build fixes: const arg in comparator, rm const in func return type Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "leak_detector_impl.h"
6
7 #include <inttypes.h>
8 #include <stddef.h>
9
10 #include <algorithm>
11 #include <new>
12
13 #include "base/hash.h"
14 #include "base/process/process_handle.h"
15 #include "components/metrics/leak_detector/call_stack_table.h"
16 #include "components/metrics/leak_detector/custom_allocator.h"
17 #include "components/metrics/leak_detector/ranked_list.h"
18
19 namespace metrics {
20 namespace leak_detector {
21
22 namespace {
23
24 // Look for leaks in the the top N entries in each tier, where N is this value.
25 const int kRankedListSize = 16;
26
27 // Initial hash table size for |LeakDetectorImpl::address_map_|.
28 const int kAddressMapNumBuckets = 100003;
29
30 // Number of entries in the alloc size table. As sizes are aligned to 32-bits
31 // the max supported allocation size is (kNumSizeEntries * 4 - 1). Any larger
32 // sizes are ignored. This value is chosen high enough that such large sizes
33 // are rare if not nonexistent.
34 const int kNumSizeEntries = 2048;
35
36 using ValueType = LeakDetectorValueType;
37
38 // Functions to convert an allocation size to/from the array index used for
39 // |LeakDetectorImpl::size_entries_|.
40 size_t SizeToIndex(const size_t size) {
41 int result = static_cast<int>(size / sizeof(uint32_t));
42 if (result < kNumSizeEntries)
43 return result;
44 return 0;
45 }
46
47 size_t IndexToSize(size_t index) {
48 return sizeof(uint32_t) * index;
49 }
50
51 } // namespace
52
53 LeakDetectorImpl::LeakReport::LeakReport() : alloc_size_bytes_(0) {}
54
55 LeakDetectorImpl::LeakReport::~LeakReport() {}
56
57 bool LeakDetectorImpl::LeakReport::operator<(const LeakReport& other) const {
58 if (alloc_size_bytes_ != other.alloc_size_bytes_)
59 return alloc_size_bytes_ < other.alloc_size_bytes_;
60 for (size_t i = 0; i < call_stack_.size() && i < other.call_stack_.size();
61 ++i) {
62 if (call_stack_[i] != other.call_stack_[i])
63 return call_stack_[i] < other.call_stack_[i];
64 }
65 return call_stack_.size() < other.call_stack_.size();
66 }
67
68 LeakDetectorImpl::LeakDetectorImpl(uintptr_t mapping_addr,
69 size_t mapping_size,
70 int size_suspicion_threshold,
71 int call_stack_suspicion_threshold)
72 : num_allocs_(0),
73 num_frees_(0),
74 alloc_size_(0),
75 free_size_(0),
76 num_allocs_with_call_stack_(0),
77 num_stack_tables_(0),
78 address_map_(kAddressMapNumBuckets),
79 size_leak_analyzer_(kRankedListSize, size_suspicion_threshold),
80 size_entries_(kNumSizeEntries),
81 mapping_addr_(mapping_addr),
82 mapping_size_(mapping_size),
83 call_stack_suspicion_threshold_(call_stack_suspicion_threshold) {}
84
85 LeakDetectorImpl::~LeakDetectorImpl() {
86 // Free any call stack tables.
87 for (AllocSizeEntry& entry : size_entries_) {
88 CallStackTable* table = entry.stack_table;
89 if (!table)
90 continue;
91 table->~CallStackTable();
92 CustomAllocator::Free(table, sizeof(CallStackTable));
93 }
94 size_entries_.clear();
95 }
96
97 bool LeakDetectorImpl::ShouldGetStackTraceForSize(size_t size) const {
98 return size_entries_[SizeToIndex(size)].stack_table != nullptr;
99 }
100
101 void LeakDetectorImpl::RecordAlloc(const void* ptr,
102 size_t size,
103 int stack_depth,
104 const void* const stack[]) {
105 AllocInfo alloc_info;
106 alloc_info.size = size;
107
108 alloc_size_ += alloc_info.size;
109 ++num_allocs_;
110
111 AllocSizeEntry* entry = &size_entries_[SizeToIndex(size)];
112 ++entry->num_allocs;
113
114 if (entry->stack_table && stack_depth > 0) {
115 alloc_info.call_stack =
116 call_stack_manager_.GetCallStack(stack_depth, stack);
117 entry->stack_table->Add(alloc_info.call_stack);
118
119 ++num_allocs_with_call_stack_;
120 }
121
122 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
123 address_map_.insert(std::pair<uintptr_t, AllocInfo>(addr, alloc_info));
124 }
125
126 void LeakDetectorImpl::RecordFree(const void* ptr) {
127 // Look up address.
128 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
129 auto iter = address_map_.find(addr);
130 // TODO(sque): Catch and report double frees.
131 if (iter == address_map_.end())
132 return;
133
134 const AllocInfo& alloc_info = iter->second;
135
136 AllocSizeEntry* entry = &size_entries_[SizeToIndex(alloc_info.size)];
137 ++entry->num_frees;
138
139 const CallStack* call_stack = alloc_info.call_stack;
140 if (call_stack) {
141 if (entry->stack_table)
142 entry->stack_table->Remove(call_stack);
143 }
144 ++num_frees_;
145 free_size_ += alloc_info.size;
146
147 address_map_.erase(iter);
148 }
149
150 void LeakDetectorImpl::TestForLeaks(InternalVector<LeakReport>* reports) {
151 // Add net alloc counts for each size to a ranked list.
152 RankedList size_ranked_list(kRankedListSize);
153 for (size_t i = 0; i < size_entries_.size(); ++i) {
154 const AllocSizeEntry& entry = size_entries_[i];
155 ValueType size_value(IndexToSize(i));
156 size_ranked_list.Add(size_value, entry.num_allocs - entry.num_frees);
157 }
158 size_leak_analyzer_.AddSample(size_ranked_list.Pass());
159
160 // Get suspected leaks by size.
161 for (const ValueType& size_value : size_leak_analyzer_.suspected_leaks()) {
162 uint32_t size = size_value.size();
163 AllocSizeEntry* entry = &size_entries_[SizeToIndex(size)];
164 if (entry->stack_table)
165 continue;
166 entry->stack_table = new (CustomAllocator::Allocate(sizeof(CallStackTable)))
167 CallStackTable(call_stack_suspicion_threshold_);
168 ++num_stack_tables_;
169 }
170
171 // Check for leaks in each CallStackTable. It makes sense to this before
172 // checking the size allocations, because that could potentially create new
173 // CallStackTable. However, the overhead to check a new CallStackTable is
174 // small since this function is run very rarely. So handle the leak checks of
175 // Tier 2 here.
176 reports->clear();
177 for (size_t i = 0; i < size_entries_.size(); ++i) {
178 const AllocSizeEntry& entry = size_entries_[i];
179 CallStackTable* stack_table = entry.stack_table;
180 if (!stack_table || stack_table->empty())
181 continue;
182
183 size_t size = IndexToSize(i);
184
185 // Get suspected leaks by call stack.
186 stack_table->TestForLeaks();
187 const LeakAnalyzer& leak_analyzer = stack_table->leak_analyzer();
188 for (const ValueType& call_stack_value : leak_analyzer.suspected_leaks()) {
189 const CallStack* call_stack = call_stack_value.call_stack();
190
191 // Return reports by storing in |*reports|.
192 reports->resize(reports->size() + 1);
193 LeakReport* report = &reports->back();
194 report->alloc_size_bytes_ = size;
195 report->call_stack_.resize(call_stack->depth);
196 for (size_t j = 0; j < call_stack->depth; ++j) {
197 report->call_stack_[j] = GetOffset(call_stack->stack[j]);
198 }
199 }
200 }
201 }
202
203 size_t LeakDetectorImpl::AddressHash::operator()(uintptr_t addr) const {
204 return base::Hash(reinterpret_cast<const char*>(&addr), sizeof(addr));
205 }
206
207 uintptr_t LeakDetectorImpl::GetOffset(const void* ptr) const {
208 uintptr_t ptr_value = reinterpret_cast<uintptr_t>(ptr);
209 if (ptr_value >= mapping_addr_ && ptr_value < mapping_addr_ + mapping_size_)
210 return ptr_value - mapping_addr_;
211 return ptr_value;
212 }
213
214 } // namespace leak_detector
215 } // namespace metrics
OLDNEW
« no previous file with comments | « components/metrics/leak_detector/leak_detector_impl.h ('k') | components/metrics/leak_detector/leak_detector_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698