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

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

Powered by Google App Engine
This is Rietveld 408576698