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

Side by Side Diff: third_party/tcmalloc/chromium/src/leak_detector.cc

Issue 986503002: components/metrics: Add runtime memory leak detector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix error in leak analyzer logic Created 5 years, 6 months 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 (c) 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 // ---
6 // Author: Simon Que
7
8 #include <unistd.h>
9 #include <link.h>
10
11 #include "base/basictypes.h"
12 #include "base/googleinit.h"
13 #include "base/logging.h"
14 #include "base/low_level_alloc.h"
15 #include "base/spinlock.h"
16 #include "call_stack_table.h"
17 #include <gperftools/malloc_extension.h>
18 #include <gperftools/malloc_hook.h>
19 #include "leak_detector_impl.h"
20 #include "tcmalloc_guard.h"
21
22 DEFINE_int32(sampling_factor,
23 EnvToInt("LEAK_DETECTOR_SAMPLING_FACTOR", 1),
24 "Used for sampling allocs and frees. Randomly samples "
25 "|sampling_factor|/256 of the pointers being allocated and "
26 "freed.");
27 DEFINE_int32(stack_depth,
28 EnvToInt("LEAK_DETECTOR_STACK_DEPTH", 4),
29 "The number of call stack levels to unwind when profiling "
30 "allocations by call stack.");
31 DEFINE_uint64(dump_interval_bytes,
32 EnvToInt("LEAK_DETECTOR_DUMP_INTERVAL_KB", 32768) * 1024,
33 "Dump allocation stats and check for memory leaks after this "
34 "many bytes have been allocated since the last dump/check. Does "
35 "not get affected by sampling.");
36 DEFINE_bool(dump_leak_analysis,
37 EnvToInt("LEAK_DETECTOR_VERBOSE", false),
38 "Dump all leak analysis data, not just suspected leak reports.");
39
40 DEFINE_int32(size_suspicion_threshold,
41 EnvToInt("LEAK_DETECTOR_SIZE_SUSPICION_THRESHOLD", 4),
42 "The number of times an allocation size must be suspected as a "
43 "leak before it gets reported.");
44 DEFINE_int32(call_stack_size_suspicion_threshold,
45 EnvToInt("LEAK_DETECTOR_CALL_STACK_SUSPICION_THRESHOLD", 4),
46 "The number of times a call stack for a particular allocation "
47 "size must be suspected as a leak before it gets reported.");
48
49 namespace leak_detector {
50
51 namespace {
52
53 // We strip out different number of stack frames in debug mode
54 // because less inlining happens in that case
55 #ifdef NDEBUG
56 static const int kStripFrames = 2;
57 #else
58 static const int kStripFrames = 3;
59 #endif
60
61 // Use a simple spinlock for locking. Don't use a mutex, which can call malloc
62 // and cause infinite recursion.
63 SpinLock heap_lock(SpinLock::LINKER_INITIALIZED);
64
65 // For storing the address range of the Chrome binary in memory.
66 struct MappingInfo {
67 uint64 addr;
68 uint64 size;
69 } chrome_mapping;
70
71 //----------------------------------------------------------------------
72 // Simple allocator for heap profiler's internal memory
73 //----------------------------------------------------------------------
74
75 LowLevelAlloc::Arena *leak_detector_memory;
76
77 void* ProfilerMalloc(size_t bytes) {
78 return LowLevelAlloc::AllocWithArena(bytes, leak_detector_memory);
79 }
80 void ProfilerFree(void* p) {
81 LowLevelAlloc::Free(p);
82 }
83
84 // Points to the active instance of the leak detector.
85 LeakDetectorImpl* leak_detector = NULL;
86
87 // Keep track of the total number of bytes allocated.
88 // TODO(sque): This might render LeakDetectorImpl::stats redundant.
89 uint64 total_alloc_size = 0;
90
91 // Keep track of the total alloc/free size when the last dump occurred.
92 uint64 last_alloc_dump_size = 0;
93 uint64 last_free_dump_size = 0;
94
95 // Dump allocation stats and check for leaks after |FLAGS_dump_interval_bytes|
96 // have been allocated since the last time this function was called.
97 inline void MaybeDumpStatsAndCheckForLeaks() {
98 if (total_alloc_size > last_alloc_dump_size + FLAGS_dump_interval_bytes) {
99 leak_detector->DumpStats();
100 last_alloc_dump_size = total_alloc_size;
101 leak_detector->TestForLeaks();
102 }
103 }
104
105 // Convert a pointer to a hash value.
106 inline unsigned int PointerToHash(const void* ptr) {
107 // The input data is the pointer address, not the location in memory pointed
108 // to by the pointer.
109 const uint64_t kMultiplier = 0x9ddfea08eb382d69ULL;
110 uint64_t value = reinterpret_cast<uint64_t>(ptr) * kMultiplier;
111 return value >> 56;
112 }
113
114 // Uses PointerToHash() to pseudorandomly sample |ptr|.
115 inline bool ShouldSample(const void* ptr) {
116 return PointerToHash(ptr) < FLAGS_sampling_factor;
117 }
118
119 //----------------------------------------------------------------------
120 // Allocation/deallocation hooks for MallocHook
121 //----------------------------------------------------------------------
122
123 void NewHook(const void* ptr, size_t size) {
124 {
125 SpinLockHolder l(&heap_lock);
126 total_alloc_size += size;
127 }
128
129 if (!ShouldSample(ptr) || !ptr || !leak_detector)
130 return;
131
132 // Take the stack trace outside the critical section.
133 void* stack[FLAGS_stack_depth];
134 int depth = 0;
135 if (leak_detector->ShouldGetStackTraceForSize(size)) {
136 depth = MallocHook::GetCallerStackTrace(
137 stack, FLAGS_stack_depth, kStripFrames + 1);
138 }
139
140 SpinLockHolder l(&heap_lock);
141 leak_detector->RecordAlloc(ptr, size, depth, stack);
142 MaybeDumpStatsAndCheckForLeaks();
143 }
144
145 void DeleteHook(const void* ptr) {
146 if (!ShouldSample(ptr) || !ptr || !leak_detector)
147 return;
148
149 SpinLockHolder l(&heap_lock);
150 leak_detector->RecordFree(ptr);
151 }
152
153 // Callback for dl_iterate_phdr() to find the Chrome binary mapping.
154 int IterateLoadedObjects(struct dl_phdr_info *info,
155 size_t /* size */,
156 void *data) {
157 uint64 base_addr = info->dlpi_addr;
158 RAW_LOG(0, "%d: name=%s addr=%lx (%d segments)", getpid(),
159 info->dlpi_name, base_addr, info->dlpi_phnum);
160 for (int i = 0; i < info->dlpi_phnum; i++) {
161 const ElfW(Phdr)& header = info->dlpi_phdr[i];
162 RAW_LOG(0, "%d: \t\t header %2d: type=%x, offset=%lx, vaddr=%lx, "
163 "paddr=%lx, filesz=%lx, memsz=%lx", getpid(), i,
164 header.p_type,
165 header.p_offset,
166 header.p_vaddr,
167 header.p_paddr,
168 header.p_filesz,
169 header.p_memsz);
170 if (header.p_type == SHT_PROGBITS && header.p_offset == 0 && data) {
171 MappingInfo* mapping = static_cast<MappingInfo*>(data);
172 mapping->addr = info->dlpi_addr + header.p_offset;
173 mapping->size = header.p_memsz;
174 RAW_LOG(0, "%d: Chrome mapped from %lx to %lx", getpid(), mapping->addr,
175 mapping->addr + mapping->size);
176 return 1;
177 }
178 }
179 return 0;
180 }
181
182 } // namespace
183
184 //----------------------------------------------------------------------
185 // Starting/stopping
186 //----------------------------------------------------------------------
187
188 void LeakDetectorStart() {
189 // If the sampling factor is too low, don't bother enabling the leak detector.
190 if (FLAGS_sampling_factor < 1) {
191 RAW_LOG(0, "%d: Not enabling leak detector because sampling_factor = %d",
192 getpid(), FLAGS_sampling_factor);
193 return;
194 }
195
196 // Locate the Chrome binary mapping before doing anything else.
197 dl_iterate_phdr(IterateLoadedObjects, &chrome_mapping);
198
199 SpinLockHolder l(&heap_lock);
200 if (leak_detector)
201 return;
202
203 RAW_LOG(0, "%d: Starting leak detector. Sampling factor: %d", getpid(),
204 FLAGS_sampling_factor);
205
206 // This should be done before the hooks are set up, since it should
207 // call new, and we want that to be accounted for correctly.
208 MallocExtension::Initialize();
209
210 leak_detector_memory =
211 LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
212 CallStackTable::Alloc::Init(ProfilerMalloc, ProfilerFree);
213
214 leak_detector = new(ProfilerMalloc(sizeof(LeakDetectorImpl)))
215 LeakDetectorImpl(ProfilerMalloc, ProfilerFree, chrome_mapping.addr,
216 chrome_mapping.size);
217
218 // We do not reset dump_count so if the user does a sequence of
219 // HeapProfilerStart/HeapProfileStop, we will get a continuous
220 // sequence of profiles.
221
222 // Now set the hooks that capture new/delete and malloc/free.
223 RAW_CHECK(MallocHook::SetNewHook(&NewHook) == NULL, "");
224 RAW_CHECK(MallocHook::SetDeleteHook(&DeleteHook) == NULL, "");
225 }
226
227 void LeakDetectorStop() {
228 SpinLockHolder l(&heap_lock);
229
230 if (!leak_detector)
231 return;
232
233 // Unset our new/delete hooks, checking they were set:
234 RAW_CHECK(MallocHook::SetNewHook(NULL) == &NewHook, "");
235 RAW_CHECK(MallocHook::SetDeleteHook(NULL) == &DeleteHook, "");
236
237 leak_detector->~LeakDetectorImpl();
238 ProfilerFree(leak_detector);
239 leak_detector = NULL;
240
241 CallStackTable::Alloc::Init(NULL, NULL);
242 if (!LowLevelAlloc::DeleteArena(leak_detector_memory)) {
243 RAW_LOG(FATAL, "Memory leak in LeakDetector.");
244 }
245
246 }
247
248 } // namespace leak_detector
249
250 #if defined(ENABLE_LEAK_DETECTOR)
251
252 extern "C" void LeakDetectorDummy() {}
253
254 // We want to make sure tcmalloc is up and running before starting the profiler
255 static const TCMallocGuard tcmalloc_initializer;
256 REGISTER_MODULE_INITIALIZER(leakdetector, leak_detector::LeakDetectorStart());
257 REGISTER_MODULE_DESTRUCTOR(leakdetector, leak_detector::LeakDetectorStop());
258
259 #endif // defined(ENABLE_LEAK_DETECTOR)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698