blob: 815e78bd1ef759e6866082b866fe12d93224f551 [file] [log] [blame]
Sergey Matveevb5483be2013-05-20 11:06:50 +00001//=-- lsan_common.cc ------------------------------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of LeakSanitizer.
11// Implementation of common leak checking functionality.
12//
13//===----------------------------------------------------------------------===//
14
15#include "lsan_common.h"
16
17#include "sanitizer_common/sanitizer_common.h"
18#include "sanitizer_common/sanitizer_flags.h"
19#include "sanitizer_common/sanitizer_stackdepot.h"
20#include "sanitizer_common/sanitizer_stacktrace.h"
21#include "sanitizer_common/sanitizer_stoptheworld.h"
22
Sergey Matveev62074452013-05-21 14:12:11 +000023#if CAN_SANITIZE_LEAKS
Sergey Matveev17f57052013-05-21 15:35:34 +000024namespace __lsan {
25
Sergey Matveevb5483be2013-05-20 11:06:50 +000026Flags lsan_flags;
27
28static void InitializeFlags() {
29 Flags *f = flags();
30 // Default values.
31 f->sources = kSourceAllAligned;
32 f->report_blocks = false;
33 f->resolution = 0;
34 f->max_leaks = 0;
Sergey Matveevbc880f32013-05-24 13:16:02 +000035 f->exitcode = 23;
Sergey Matveevb5483be2013-05-20 11:06:50 +000036 f->log_pointers = false;
37 f->log_threads = false;
38
39 const char *options = GetEnv("LSAN_OPTIONS");
40 if (options) {
41 bool aligned = true;
42 ParseFlag(options, &aligned, "aligned");
43 if (!aligned) f->sources |= kSourceUnaligned;
44 ParseFlag(options, &f->report_blocks, "report_blocks");
45 ParseFlag(options, &f->resolution, "resolution");
46 CHECK_GE(&f->resolution, 0);
47 ParseFlag(options, &f->max_leaks, "max_leaks");
48 CHECK_GE(&f->max_leaks, 0);
49 ParseFlag(options, &f->log_pointers, "log_pointers");
50 ParseFlag(options, &f->log_threads, "log_threads");
Sergey Matveevbc880f32013-05-24 13:16:02 +000051 ParseFlag(options, &f->exitcode, "exitcode");
Sergey Matveevb5483be2013-05-20 11:06:50 +000052 }
53}
54
55void InitCommonLsan() {
56 InitializeFlags();
57 InitializePlatformSpecificModules();
58}
59
60static inline bool CanBeAHeapPointer(uptr p) {
61 // Since our heap is located in mmap-ed memory, we can assume a sensible lower
62 // boundary on heap addresses.
63 const uptr kMinAddress = 4 * 4096;
64 if (p < kMinAddress) return false;
65#ifdef __x86_64__
66 // Accept only canonical form user-space addresses.
67 return ((p >> 47) == 0);
68#else
69 return true;
70#endif
71}
72
73// Scan the memory range, looking for byte patterns that point into allocator
74// chunks. Mark those chunks with tag and add them to the frontier.
75// There are two usage modes for this function: finding non-leaked chunks
76// (tag = kReachable) and finding indirectly leaked chunks
77// (tag = kIndirectlyLeaked). In the second case, there's no flood fill,
78// so frontier = 0.
79void ScanRangeForPointers(uptr begin, uptr end, InternalVector<uptr> *frontier,
80 const char *region_type, ChunkTag tag) {
81 const uptr alignment = flags()->pointer_alignment();
82 if (flags()->log_pointers)
83 Report("Scanning %s range %p-%p.\n", region_type, begin, end);
84 uptr pp = begin;
85 if (pp % alignment)
86 pp = pp + alignment - pp % alignment;
87 for (; pp + sizeof(uptr) <= end; pp += alignment) {
88 void *p = *reinterpret_cast<void**>(pp);
89 if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue;
90 // FIXME: PointsIntoChunk is SLOW because GetBlockBegin() in
91 // LargeMmapAllocator involves a lock and a linear search.
92 void *chunk = PointsIntoChunk(p);
93 if (!chunk) continue;
94 LsanMetadata m(chunk);
95 if (m.tag() == kReachable) continue;
96 m.set_tag(tag);
97 if (flags()->log_pointers)
98 Report("%p: found %p pointing into chunk %p-%p of size %llu.\n", pp, p,
99 chunk, reinterpret_cast<uptr>(chunk) + m.requested_size(),
100 m.requested_size());
101 if (frontier)
102 frontier->push_back(reinterpret_cast<uptr>(chunk));
103 }
104}
105
106// Scan thread data (stacks and TLS) for heap pointers.
107static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
108 InternalVector<uptr> *frontier) {
109 InternalScopedBuffer<uptr> registers(SuspendedThreadsList::RegisterCount());
110 uptr registers_begin = reinterpret_cast<uptr>(registers.data());
111 uptr registers_end = registers_begin + registers.size();
112 for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
113 uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
114 if (flags()->log_threads) Report("Processing thread %d.\n", os_id);
115 uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
116 bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
117 &tls_begin, &tls_end,
118 &cache_begin, &cache_end);
119 if (!thread_found) {
120 // If a thread can't be found in the thread registry, it's probably in the
121 // process of destruction. Log this event and move on.
122 if (flags()->log_threads)
123 Report("Thread %d not found in registry.\n", os_id);
124 continue;
125 }
126 uptr sp;
127 bool have_registers =
128 (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0);
129 if (!have_registers) {
130 Report("Unable to get registers from thread %d.\n");
131 // If unable to get SP, consider the entire stack to be reachable.
132 sp = stack_begin;
133 }
134
135 if (flags()->use_registers() && have_registers)
136 ScanRangeForPointers(registers_begin, registers_end, frontier,
137 "REGISTERS", kReachable);
138
139 if (flags()->use_stacks()) {
140 if (flags()->log_threads)
141 Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp);
142 if (sp < stack_begin || sp >= stack_end) {
143 // SP is outside the recorded stack range (e.g. the thread is running a
144 // signal handler on alternate stack). Again, consider the entire stack
145 // range to be reachable.
146 if (flags()->log_threads)
147 Report("WARNING: stack_pointer not in stack_range.\n");
148 } else {
149 // Shrink the stack range to ignore out-of-scope values.
150 stack_begin = sp;
151 }
152 ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
153 kReachable);
154 }
155
156 if (flags()->use_tls()) {
157 if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end);
158 // Because LSan should not be loaded with dlopen(), we can assume
159 // that allocator cache will be part of static TLS image.
160 CHECK_LE(tls_begin, cache_begin);
161 CHECK_GE(tls_end, cache_end);
162 if (tls_begin < cache_begin)
163 ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
164 kReachable);
165 if (tls_end > cache_end)
166 ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
167 }
168 }
169}
170
171static void FloodFillReachable(InternalVector<uptr> *frontier) {
172 while (frontier->size()) {
173 uptr next_chunk = frontier->back();
174 frontier->pop_back();
175 LsanMetadata m(reinterpret_cast<void *>(next_chunk));
176 ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier,
177 "HEAP", kReachable);
178 }
179}
180
181// Mark leaked chunks which are reachable from other leaked chunks.
182void MarkIndirectlyLeakedCb::operator()(void *p) const {
Sergey Matveevbcfd8382013-05-20 13:08:23 +0000183 p = GetUserBegin(p);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000184 LsanMetadata m(p);
185 if (m.allocated() && m.tag() != kReachable) {
186 ScanRangeForPointers(reinterpret_cast<uptr>(p),
187 reinterpret_cast<uptr>(p) + m.requested_size(),
188 /* frontier */ 0, "HEAP", kIndirectlyLeaked);
189 }
190}
191
192// Set the appropriate tag on each chunk.
193static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
194 // Holds the flood fill frontier.
195 InternalVector<uptr> frontier(GetPageSizeCached());
196
197 if (flags()->use_globals())
198 ProcessGlobalRegions(&frontier);
199 ProcessThreads(suspended_threads, &frontier);
200 FloodFillReachable(&frontier);
201 ProcessPlatformSpecificAllocations(&frontier);
202 FloodFillReachable(&frontier);
203
204 // Now all reachable chunks are marked. Iterate over leaked chunks and mark
205 // those that are reachable from other leaked chunks.
206 if (flags()->log_pointers)
207 Report("Now scanning leaked blocks for pointers.\n");
208 ForEachChunk(MarkIndirectlyLeakedCb());
209}
210
211void ClearTagCb::operator()(void *p) const {
Sergey Matveevbcfd8382013-05-20 13:08:23 +0000212 p = GetUserBegin(p);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000213 LsanMetadata m(p);
214 m.set_tag(kDirectlyLeaked);
215}
216
217static void PrintStackTraceById(u32 stack_trace_id) {
218 CHECK(stack_trace_id);
219 uptr size = 0;
220 const uptr *trace = StackDepotGet(stack_trace_id, &size);
221 StackTrace::PrintStack(trace, size, common_flags()->symbolize,
222 common_flags()->strip_path_prefix, 0);
223}
224
225static void LockAndSuspendThreads(StopTheWorldCallback callback, void *arg) {
226 LockThreadRegistry();
227 LockAllocator();
228 StopTheWorld(callback, arg);
229 // Allocator must be unlocked by the callback.
230 UnlockThreadRegistry();
231}
232
233///// Normal leak checking. /////
234
235void CollectLeaksCb::operator()(void *p) const {
Sergey Matveevbcfd8382013-05-20 13:08:23 +0000236 p = GetUserBegin(p);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000237 LsanMetadata m(p);
238 if (!m.allocated()) return;
239 if (m.tag() != kReachable) {
240 uptr resolution = flags()->resolution;
241 if (resolution > 0) {
242 uptr size = 0;
243 const uptr *trace = StackDepotGet(m.stack_trace_id(), &size);
244 size = Min(size, resolution);
245 leak_report_->Add(StackDepotPut(trace, size), m.requested_size(),
246 m.tag());
247 } else {
248 leak_report_->Add(m.stack_trace_id(), m.requested_size(), m.tag());
249 }
250 }
251}
252
253static void CollectLeaks(LeakReport *leak_report) {
254 ForEachChunk(CollectLeaksCb(leak_report));
255}
256
257void PrintLeakedCb::operator()(void *p) const {
Sergey Matveevbcfd8382013-05-20 13:08:23 +0000258 p = GetUserBegin(p);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000259 LsanMetadata m(p);
260 if (!m.allocated()) return;
261 if (m.tag() != kReachable) {
262 CHECK(m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked);
263 Printf("%s leaked %llu byte block at %p\n",
264 m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly",
265 m.requested_size(), p);
266 }
267}
268
269static void PrintLeaked() {
Sergey Matveevc7715a22013-05-24 14:49:13 +0000270 Printf("Reporting individual blocks:\n");
271 Printf("============================\n");
Sergey Matveevb5483be2013-05-20 11:06:50 +0000272 ForEachChunk(PrintLeakedCb());
Sergey Matveevc7715a22013-05-24 14:49:13 +0000273 Printf("\n");
Sergey Matveevb5483be2013-05-20 11:06:50 +0000274}
275
Sergey Matveevbc880f32013-05-24 13:16:02 +0000276enum LeakCheckResult {
277 kFatalError,
278 kLeaksFound,
279 kNoLeaks
280};
281
Sergey Matveevb5483be2013-05-20 11:06:50 +0000282static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
283 void *arg) {
Sergey Matveevbc880f32013-05-24 13:16:02 +0000284 LeakCheckResult *result = reinterpret_cast<LeakCheckResult *>(arg);
285 CHECK_EQ(*result, kFatalError);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000286 // Allocator must not be locked when we call GetRegionBegin().
287 UnlockAllocator();
Sergey Matveevb5483be2013-05-20 11:06:50 +0000288 ClassifyAllChunks(suspended_threads);
289 LeakReport leak_report;
290 CollectLeaks(&leak_report);
Sergey Matveevbc880f32013-05-24 13:16:02 +0000291 if (leak_report.IsEmpty()) {
292 *result = kNoLeaks;
293 return;
Sergey Matveevb5483be2013-05-20 11:06:50 +0000294 }
Sergey Matveevc7715a22013-05-24 14:49:13 +0000295 Printf("\n");
296 Printf("=================================================================\n");
297 Report("ERROR: LeakSanitizer: detected leaks.\n");
Sergey Matveevbc880f32013-05-24 13:16:02 +0000298 leak_report.PrintLargest(flags()->max_leaks);
299 if (flags()->report_blocks)
300 PrintLeaked();
Sergey Matveevc7715a22013-05-24 14:49:13 +0000301 leak_report.PrintSummary();
302 Printf("\n");
Sergey Matveevb5483be2013-05-20 11:06:50 +0000303 ForEachChunk(ClearTagCb());
Sergey Matveevbc880f32013-05-24 13:16:02 +0000304 *result = kLeaksFound;
Sergey Matveevb5483be2013-05-20 11:06:50 +0000305}
306
307void DoLeakCheck() {
Sergey Matveevbc880f32013-05-24 13:16:02 +0000308 LeakCheckResult result = kFatalError;
309 LockAndSuspendThreads(DoLeakCheckCallback, &result);
310 if (result == kFatalError) {
311 Report("LeakSanitizer has encountered a fatal error.\n");
312 Die();
313 } else if (result == kLeaksFound) {
314 if (flags()->exitcode)
315 internal__exit(flags()->exitcode);
316 }
Sergey Matveevb5483be2013-05-20 11:06:50 +0000317}
318
319///// Reporting of leaked blocks' addresses (for testing). /////
320
321void ReportLeakedCb::operator()(void *p) const {
Sergey Matveevbcfd8382013-05-20 13:08:23 +0000322 p = GetUserBegin(p);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000323 LsanMetadata m(p);
324 if (m.allocated() && m.tag() != kReachable)
325 leaked_->push_back(p);
326}
327
328struct ReportLeakedParam {
329 InternalVector<void *> *leaked;
330 uptr sources;
331 bool success;
332};
333
334static void ReportLeakedCallback(const SuspendedThreadsList &suspended_threads,
335 void *arg) {
336 // Allocator must not be locked when we call GetRegionBegin().
337 UnlockAllocator();
338 ReportLeakedParam *param = reinterpret_cast<ReportLeakedParam *>(arg);
339 flags()->sources = param->sources;
340 ClassifyAllChunks(suspended_threads);
341 ForEachChunk(ReportLeakedCb(param->leaked));
342 ForEachChunk(ClearTagCb());
343 param->success = true;
344}
345
346void ReportLeaked(InternalVector<void *> *leaked, uptr sources) {
347 CHECK_EQ(0, leaked->size());
348 ReportLeakedParam param;
349 param.leaked = leaked;
350 param.success = false;
351 param.sources = sources;
352 LockAndSuspendThreads(ReportLeakedCallback, &param);
353 CHECK(param.success);
354}
355
356///// LeakReport implementation. /////
357
358// A hard limit on the number of distinct leaks, to avoid quadratic complexity
359// in LeakReport::Add(). We don't expect to ever see this many leaks in
360// real-world applications.
361// FIXME: Get rid of this limit by changing the implementation of LeakReport to
362// use a hash table.
363const uptr kMaxLeaksConsidered = 1000;
364
365void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) {
366 CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
367 bool is_directly_leaked = (tag == kDirectlyLeaked);
368 for (uptr i = 0; i < leaks_.size(); i++)
369 if (leaks_[i].stack_trace_id == stack_trace_id &&
370 leaks_[i].is_directly_leaked == is_directly_leaked) {
371 leaks_[i].hit_count++;
372 leaks_[i].total_size += leaked_size;
373 return;
374 }
375 if (leaks_.size() == kMaxLeaksConsidered) return;
376 Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id,
377 is_directly_leaked };
378 leaks_.push_back(leak);
379}
380
381static bool IsLarger(const Leak &leak1, const Leak &leak2) {
382 return leak1.total_size > leak2.total_size;
383}
384
385void LeakReport::PrintLargest(uptr max_leaks) {
386 CHECK(leaks_.size() <= kMaxLeaksConsidered);
387 Printf("\n");
388 if (leaks_.size() == kMaxLeaksConsidered)
389 Printf("Too many leaks! Only the first %llu leaks encountered will be "
390 "reported.\n",
391 kMaxLeaksConsidered);
392 if (max_leaks > 0 && max_leaks < leaks_.size())
Sergey Matveev37dff382013-05-24 15:36:30 +0000393 Printf("The %llu largest leak(s):\n", max_leaks);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000394 InternalSort(&leaks_, leaks_.size(), IsLarger);
395 max_leaks = max_leaks > 0 ? Min(max_leaks, leaks_.size()) : leaks_.size();
396 for (uptr i = 0; i < max_leaks; i++) {
Sergey Matveev37dff382013-05-24 15:36:30 +0000397 Printf("%s leak of %llu byte(s) in %llu object(s) allocated from:\n",
Sergey Matveevb5483be2013-05-20 11:06:50 +0000398 leaks_[i].is_directly_leaked ? "Direct" : "Indirect",
Sergey Matveev37dff382013-05-24 15:36:30 +0000399 leaks_[i].total_size, leaks_[i].hit_count);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000400 PrintStackTraceById(leaks_[i].stack_trace_id);
Sergey Matveevc7715a22013-05-24 14:49:13 +0000401 Printf("\n");
Sergey Matveevb5483be2013-05-20 11:06:50 +0000402 }
403 if (max_leaks < leaks_.size()) {
404 uptr remaining = leaks_.size() - max_leaks;
Sergey Matveev37dff382013-05-24 15:36:30 +0000405 Printf("Omitting %llu more leak(s).\n", remaining);
Sergey Matveevb5483be2013-05-20 11:06:50 +0000406 }
407}
Sergey Matveev17f57052013-05-21 15:35:34 +0000408
Sergey Matveevc7715a22013-05-24 14:49:13 +0000409void LeakReport::PrintSummary() {
410 CHECK(leaks_.size() <= kMaxLeaksConsidered);
Sergey Matveev37dff382013-05-24 15:36:30 +0000411 uptr bytes = 0, allocations = 0;
Sergey Matveevc7715a22013-05-24 14:49:13 +0000412 for (uptr i = 0; i < leaks_.size(); i++) {
Sergey Matveev37dff382013-05-24 15:36:30 +0000413 bytes += leaks_[i].total_size;
414 allocations += leaks_[i].hit_count;
Sergey Matveevc7715a22013-05-24 14:49:13 +0000415 }
Sergey Matveev37dff382013-05-24 15:36:30 +0000416 Printf("SUMMARY: LeakSanitizer: %llu byte(s) leaked in %llu allocation(s).\n",
417 bytes, allocations);
Sergey Matveevc7715a22013-05-24 14:49:13 +0000418}
Sergey Matveevb5483be2013-05-20 11:06:50 +0000419} // namespace __lsan
Sergey Matveev17f57052013-05-21 15:35:34 +0000420#endif // CAN_SANITIZE_LEAKS