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

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

Powered by Google App Engine
This is Rietveld 408576698