1*7c3d14c8STreehugger Robot //=-- lsan_common.cc ------------------------------------------------------===//
2*7c3d14c8STreehugger Robot //
3*7c3d14c8STreehugger Robot // The LLVM Compiler Infrastructure
4*7c3d14c8STreehugger Robot //
5*7c3d14c8STreehugger Robot // This file is distributed under the University of Illinois Open Source
6*7c3d14c8STreehugger Robot // License. See LICENSE.TXT for details.
7*7c3d14c8STreehugger Robot //
8*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
9*7c3d14c8STreehugger Robot //
10*7c3d14c8STreehugger Robot // This file is a part of LeakSanitizer.
11*7c3d14c8STreehugger Robot // Implementation of common leak checking functionality.
12*7c3d14c8STreehugger Robot //
13*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
14*7c3d14c8STreehugger Robot
15*7c3d14c8STreehugger Robot #include "lsan_common.h"
16*7c3d14c8STreehugger Robot
17*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_common.h"
18*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_flags.h"
19*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_flag_parser.h"
20*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_placement_new.h"
21*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_procmaps.h"
22*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_stackdepot.h"
23*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_stacktrace.h"
24*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_suppressions.h"
25*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_report_decorator.h"
26*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_tls_get_addr.h"
27*7c3d14c8STreehugger Robot
28*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
29*7c3d14c8STreehugger Robot namespace __lsan {
30*7c3d14c8STreehugger Robot
31*7c3d14c8STreehugger Robot // This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
32*7c3d14c8STreehugger Robot // also to protect the global list of root regions.
33*7c3d14c8STreehugger Robot BlockingMutex global_mutex(LINKER_INITIALIZED);
34*7c3d14c8STreehugger Robot
35*7c3d14c8STreehugger Robot THREADLOCAL int disable_counter;
DisabledInThisThread()36*7c3d14c8STreehugger Robot bool DisabledInThisThread() { return disable_counter > 0; }
DisableInThisThread()37*7c3d14c8STreehugger Robot void DisableInThisThread() { disable_counter++; }
EnableInThisThread()38*7c3d14c8STreehugger Robot void EnableInThisThread() {
39*7c3d14c8STreehugger Robot if (!disable_counter && common_flags()->detect_leaks) {
40*7c3d14c8STreehugger Robot Report("Unmatched call to __lsan_enable().\n");
41*7c3d14c8STreehugger Robot Die();
42*7c3d14c8STreehugger Robot }
43*7c3d14c8STreehugger Robot disable_counter--;
44*7c3d14c8STreehugger Robot }
45*7c3d14c8STreehugger Robot
46*7c3d14c8STreehugger Robot Flags lsan_flags;
47*7c3d14c8STreehugger Robot
SetDefaults()48*7c3d14c8STreehugger Robot void Flags::SetDefaults() {
49*7c3d14c8STreehugger Robot #define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
50*7c3d14c8STreehugger Robot #include "lsan_flags.inc"
51*7c3d14c8STreehugger Robot #undef LSAN_FLAG
52*7c3d14c8STreehugger Robot }
53*7c3d14c8STreehugger Robot
RegisterLsanFlags(FlagParser * parser,Flags * f)54*7c3d14c8STreehugger Robot void RegisterLsanFlags(FlagParser *parser, Flags *f) {
55*7c3d14c8STreehugger Robot #define LSAN_FLAG(Type, Name, DefaultValue, Description) \
56*7c3d14c8STreehugger Robot RegisterFlag(parser, #Name, Description, &f->Name);
57*7c3d14c8STreehugger Robot #include "lsan_flags.inc"
58*7c3d14c8STreehugger Robot #undef LSAN_FLAG
59*7c3d14c8STreehugger Robot }
60*7c3d14c8STreehugger Robot
61*7c3d14c8STreehugger Robot #define LOG_POINTERS(...) \
62*7c3d14c8STreehugger Robot do { \
63*7c3d14c8STreehugger Robot if (flags()->log_pointers) Report(__VA_ARGS__); \
64*7c3d14c8STreehugger Robot } while (0);
65*7c3d14c8STreehugger Robot
66*7c3d14c8STreehugger Robot #define LOG_THREADS(...) \
67*7c3d14c8STreehugger Robot do { \
68*7c3d14c8STreehugger Robot if (flags()->log_threads) Report(__VA_ARGS__); \
69*7c3d14c8STreehugger Robot } while (0);
70*7c3d14c8STreehugger Robot
71*7c3d14c8STreehugger Robot ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
72*7c3d14c8STreehugger Robot static SuppressionContext *suppression_ctx = nullptr;
73*7c3d14c8STreehugger Robot static const char kSuppressionLeak[] = "leak";
74*7c3d14c8STreehugger Robot static const char *kSuppressionTypes[] = { kSuppressionLeak };
75*7c3d14c8STreehugger Robot
InitializeSuppressions()76*7c3d14c8STreehugger Robot void InitializeSuppressions() {
77*7c3d14c8STreehugger Robot CHECK_EQ(nullptr, suppression_ctx);
78*7c3d14c8STreehugger Robot suppression_ctx = new (suppression_placeholder) // NOLINT
79*7c3d14c8STreehugger Robot SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
80*7c3d14c8STreehugger Robot suppression_ctx->ParseFromFile(flags()->suppressions);
81*7c3d14c8STreehugger Robot if (&__lsan_default_suppressions)
82*7c3d14c8STreehugger Robot suppression_ctx->Parse(__lsan_default_suppressions());
83*7c3d14c8STreehugger Robot }
84*7c3d14c8STreehugger Robot
GetSuppressionContext()85*7c3d14c8STreehugger Robot static SuppressionContext *GetSuppressionContext() {
86*7c3d14c8STreehugger Robot CHECK(suppression_ctx);
87*7c3d14c8STreehugger Robot return suppression_ctx;
88*7c3d14c8STreehugger Robot }
89*7c3d14c8STreehugger Robot
90*7c3d14c8STreehugger Robot struct RootRegion {
91*7c3d14c8STreehugger Robot const void *begin;
92*7c3d14c8STreehugger Robot uptr size;
93*7c3d14c8STreehugger Robot };
94*7c3d14c8STreehugger Robot
95*7c3d14c8STreehugger Robot InternalMmapVector<RootRegion> *root_regions;
96*7c3d14c8STreehugger Robot
InitializeRootRegions()97*7c3d14c8STreehugger Robot void InitializeRootRegions() {
98*7c3d14c8STreehugger Robot CHECK(!root_regions);
99*7c3d14c8STreehugger Robot ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
100*7c3d14c8STreehugger Robot root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
101*7c3d14c8STreehugger Robot }
102*7c3d14c8STreehugger Robot
InitCommonLsan()103*7c3d14c8STreehugger Robot void InitCommonLsan() {
104*7c3d14c8STreehugger Robot InitializeRootRegions();
105*7c3d14c8STreehugger Robot if (common_flags()->detect_leaks) {
106*7c3d14c8STreehugger Robot // Initialization which can fail or print warnings should only be done if
107*7c3d14c8STreehugger Robot // LSan is actually enabled.
108*7c3d14c8STreehugger Robot InitializeSuppressions();
109*7c3d14c8STreehugger Robot InitializePlatformSpecificModules();
110*7c3d14c8STreehugger Robot }
111*7c3d14c8STreehugger Robot }
112*7c3d14c8STreehugger Robot
113*7c3d14c8STreehugger Robot class Decorator: public __sanitizer::SanitizerCommonDecorator {
114*7c3d14c8STreehugger Robot public:
Decorator()115*7c3d14c8STreehugger Robot Decorator() : SanitizerCommonDecorator() { }
Error()116*7c3d14c8STreehugger Robot const char *Error() { return Red(); }
Leak()117*7c3d14c8STreehugger Robot const char *Leak() { return Blue(); }
End()118*7c3d14c8STreehugger Robot const char *End() { return Default(); }
119*7c3d14c8STreehugger Robot };
120*7c3d14c8STreehugger Robot
CanBeAHeapPointer(uptr p)121*7c3d14c8STreehugger Robot static inline bool CanBeAHeapPointer(uptr p) {
122*7c3d14c8STreehugger Robot // Since our heap is located in mmap-ed memory, we can assume a sensible lower
123*7c3d14c8STreehugger Robot // bound on heap addresses.
124*7c3d14c8STreehugger Robot const uptr kMinAddress = 4 * 4096;
125*7c3d14c8STreehugger Robot if (p < kMinAddress) return false;
126*7c3d14c8STreehugger Robot #if defined(__x86_64__)
127*7c3d14c8STreehugger Robot // Accept only canonical form user-space addresses.
128*7c3d14c8STreehugger Robot return ((p >> 47) == 0);
129*7c3d14c8STreehugger Robot #elif defined(__mips64)
130*7c3d14c8STreehugger Robot return ((p >> 40) == 0);
131*7c3d14c8STreehugger Robot #elif defined(__aarch64__)
132*7c3d14c8STreehugger Robot unsigned runtimeVMA =
133*7c3d14c8STreehugger Robot (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
134*7c3d14c8STreehugger Robot return ((p >> runtimeVMA) == 0);
135*7c3d14c8STreehugger Robot #else
136*7c3d14c8STreehugger Robot return true;
137*7c3d14c8STreehugger Robot #endif
138*7c3d14c8STreehugger Robot }
139*7c3d14c8STreehugger Robot
140*7c3d14c8STreehugger Robot // Scans the memory range, looking for byte patterns that point into allocator
141*7c3d14c8STreehugger Robot // chunks. Marks those chunks with |tag| and adds them to |frontier|.
142*7c3d14c8STreehugger Robot // There are two usage modes for this function: finding reachable chunks
143*7c3d14c8STreehugger Robot // (|tag| = kReachable) and finding indirectly leaked chunks
144*7c3d14c8STreehugger Robot // (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
145*7c3d14c8STreehugger Robot // so |frontier| = 0.
ScanRangeForPointers(uptr begin,uptr end,Frontier * frontier,const char * region_type,ChunkTag tag)146*7c3d14c8STreehugger Robot void ScanRangeForPointers(uptr begin, uptr end,
147*7c3d14c8STreehugger Robot Frontier *frontier,
148*7c3d14c8STreehugger Robot const char *region_type, ChunkTag tag) {
149*7c3d14c8STreehugger Robot CHECK(tag == kReachable || tag == kIndirectlyLeaked);
150*7c3d14c8STreehugger Robot const uptr alignment = flags()->pointer_alignment();
151*7c3d14c8STreehugger Robot LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
152*7c3d14c8STreehugger Robot uptr pp = begin;
153*7c3d14c8STreehugger Robot if (pp % alignment)
154*7c3d14c8STreehugger Robot pp = pp + alignment - pp % alignment;
155*7c3d14c8STreehugger Robot for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT
156*7c3d14c8STreehugger Robot void *p = *reinterpret_cast<void **>(pp);
157*7c3d14c8STreehugger Robot if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue;
158*7c3d14c8STreehugger Robot uptr chunk = PointsIntoChunk(p);
159*7c3d14c8STreehugger Robot if (!chunk) continue;
160*7c3d14c8STreehugger Robot // Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
161*7c3d14c8STreehugger Robot if (chunk == begin) continue;
162*7c3d14c8STreehugger Robot LsanMetadata m(chunk);
163*7c3d14c8STreehugger Robot if (m.tag() == kReachable || m.tag() == kIgnored) continue;
164*7c3d14c8STreehugger Robot
165*7c3d14c8STreehugger Robot // Do this check relatively late so we can log only the interesting cases.
166*7c3d14c8STreehugger Robot if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
167*7c3d14c8STreehugger Robot LOG_POINTERS(
168*7c3d14c8STreehugger Robot "%p is poisoned: ignoring %p pointing into chunk %p-%p of size "
169*7c3d14c8STreehugger Robot "%zu.\n",
170*7c3d14c8STreehugger Robot pp, p, chunk, chunk + m.requested_size(), m.requested_size());
171*7c3d14c8STreehugger Robot continue;
172*7c3d14c8STreehugger Robot }
173*7c3d14c8STreehugger Robot
174*7c3d14c8STreehugger Robot m.set_tag(tag);
175*7c3d14c8STreehugger Robot LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
176*7c3d14c8STreehugger Robot chunk, chunk + m.requested_size(), m.requested_size());
177*7c3d14c8STreehugger Robot if (frontier)
178*7c3d14c8STreehugger Robot frontier->push_back(chunk);
179*7c3d14c8STreehugger Robot }
180*7c3d14c8STreehugger Robot }
181*7c3d14c8STreehugger Robot
ForEachExtraStackRangeCb(uptr begin,uptr end,void * arg)182*7c3d14c8STreehugger Robot void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
183*7c3d14c8STreehugger Robot Frontier *frontier = reinterpret_cast<Frontier *>(arg);
184*7c3d14c8STreehugger Robot ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
185*7c3d14c8STreehugger Robot }
186*7c3d14c8STreehugger Robot
187*7c3d14c8STreehugger Robot // Scans thread data (stacks and TLS) for heap pointers.
ProcessThreads(SuspendedThreadsList const & suspended_threads,Frontier * frontier)188*7c3d14c8STreehugger Robot static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
189*7c3d14c8STreehugger Robot Frontier *frontier) {
190*7c3d14c8STreehugger Robot InternalScopedBuffer<uptr> registers(SuspendedThreadsList::RegisterCount());
191*7c3d14c8STreehugger Robot uptr registers_begin = reinterpret_cast<uptr>(registers.data());
192*7c3d14c8STreehugger Robot uptr registers_end = registers_begin + registers.size();
193*7c3d14c8STreehugger Robot for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
194*7c3d14c8STreehugger Robot uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
195*7c3d14c8STreehugger Robot LOG_THREADS("Processing thread %d.\n", os_id);
196*7c3d14c8STreehugger Robot uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
197*7c3d14c8STreehugger Robot DTLS *dtls;
198*7c3d14c8STreehugger Robot bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
199*7c3d14c8STreehugger Robot &tls_begin, &tls_end,
200*7c3d14c8STreehugger Robot &cache_begin, &cache_end, &dtls);
201*7c3d14c8STreehugger Robot if (!thread_found) {
202*7c3d14c8STreehugger Robot // If a thread can't be found in the thread registry, it's probably in the
203*7c3d14c8STreehugger Robot // process of destruction. Log this event and move on.
204*7c3d14c8STreehugger Robot LOG_THREADS("Thread %d not found in registry.\n", os_id);
205*7c3d14c8STreehugger Robot continue;
206*7c3d14c8STreehugger Robot }
207*7c3d14c8STreehugger Robot uptr sp;
208*7c3d14c8STreehugger Robot bool have_registers =
209*7c3d14c8STreehugger Robot (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0);
210*7c3d14c8STreehugger Robot if (!have_registers) {
211*7c3d14c8STreehugger Robot Report("Unable to get registers from thread %d.\n");
212*7c3d14c8STreehugger Robot // If unable to get SP, consider the entire stack to be reachable.
213*7c3d14c8STreehugger Robot sp = stack_begin;
214*7c3d14c8STreehugger Robot }
215*7c3d14c8STreehugger Robot
216*7c3d14c8STreehugger Robot if (flags()->use_registers && have_registers)
217*7c3d14c8STreehugger Robot ScanRangeForPointers(registers_begin, registers_end, frontier,
218*7c3d14c8STreehugger Robot "REGISTERS", kReachable);
219*7c3d14c8STreehugger Robot
220*7c3d14c8STreehugger Robot if (flags()->use_stacks) {
221*7c3d14c8STreehugger Robot LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
222*7c3d14c8STreehugger Robot if (sp < stack_begin || sp >= stack_end) {
223*7c3d14c8STreehugger Robot // SP is outside the recorded stack range (e.g. the thread is running a
224*7c3d14c8STreehugger Robot // signal handler on alternate stack, or swapcontext was used).
225*7c3d14c8STreehugger Robot // Again, consider the entire stack range to be reachable.
226*7c3d14c8STreehugger Robot LOG_THREADS("WARNING: stack pointer not in stack range.\n");
227*7c3d14c8STreehugger Robot uptr page_size = GetPageSizeCached();
228*7c3d14c8STreehugger Robot int skipped = 0;
229*7c3d14c8STreehugger Robot while (stack_begin < stack_end &&
230*7c3d14c8STreehugger Robot !IsAccessibleMemoryRange(stack_begin, 1)) {
231*7c3d14c8STreehugger Robot skipped++;
232*7c3d14c8STreehugger Robot stack_begin += page_size;
233*7c3d14c8STreehugger Robot }
234*7c3d14c8STreehugger Robot LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n",
235*7c3d14c8STreehugger Robot skipped, stack_begin, stack_end);
236*7c3d14c8STreehugger Robot } else {
237*7c3d14c8STreehugger Robot // Shrink the stack range to ignore out-of-scope values.
238*7c3d14c8STreehugger Robot stack_begin = sp;
239*7c3d14c8STreehugger Robot }
240*7c3d14c8STreehugger Robot ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
241*7c3d14c8STreehugger Robot kReachable);
242*7c3d14c8STreehugger Robot ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
243*7c3d14c8STreehugger Robot }
244*7c3d14c8STreehugger Robot
245*7c3d14c8STreehugger Robot if (flags()->use_tls) {
246*7c3d14c8STreehugger Robot LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
247*7c3d14c8STreehugger Robot if (cache_begin == cache_end) {
248*7c3d14c8STreehugger Robot ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
249*7c3d14c8STreehugger Robot } else {
250*7c3d14c8STreehugger Robot // Because LSan should not be loaded with dlopen(), we can assume
251*7c3d14c8STreehugger Robot // that allocator cache will be part of static TLS image.
252*7c3d14c8STreehugger Robot CHECK_LE(tls_begin, cache_begin);
253*7c3d14c8STreehugger Robot CHECK_GE(tls_end, cache_end);
254*7c3d14c8STreehugger Robot if (tls_begin < cache_begin)
255*7c3d14c8STreehugger Robot ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
256*7c3d14c8STreehugger Robot kReachable);
257*7c3d14c8STreehugger Robot if (tls_end > cache_end)
258*7c3d14c8STreehugger Robot ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
259*7c3d14c8STreehugger Robot }
260*7c3d14c8STreehugger Robot if (dtls) {
261*7c3d14c8STreehugger Robot for (uptr j = 0; j < dtls->dtv_size; ++j) {
262*7c3d14c8STreehugger Robot uptr dtls_beg = dtls->dtv[j].beg;
263*7c3d14c8STreehugger Robot uptr dtls_end = dtls_beg + dtls->dtv[j].size;
264*7c3d14c8STreehugger Robot if (dtls_beg < dtls_end) {
265*7c3d14c8STreehugger Robot LOG_THREADS("DTLS %zu at %p-%p.\n", j, dtls_beg, dtls_end);
266*7c3d14c8STreehugger Robot ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS",
267*7c3d14c8STreehugger Robot kReachable);
268*7c3d14c8STreehugger Robot }
269*7c3d14c8STreehugger Robot }
270*7c3d14c8STreehugger Robot }
271*7c3d14c8STreehugger Robot }
272*7c3d14c8STreehugger Robot }
273*7c3d14c8STreehugger Robot }
274*7c3d14c8STreehugger Robot
ProcessRootRegion(Frontier * frontier,uptr root_begin,uptr root_end)275*7c3d14c8STreehugger Robot static void ProcessRootRegion(Frontier *frontier, uptr root_begin,
276*7c3d14c8STreehugger Robot uptr root_end) {
277*7c3d14c8STreehugger Robot MemoryMappingLayout proc_maps(/*cache_enabled*/true);
278*7c3d14c8STreehugger Robot uptr begin, end, prot;
279*7c3d14c8STreehugger Robot while (proc_maps.Next(&begin, &end,
280*7c3d14c8STreehugger Robot /*offset*/ nullptr, /*filename*/ nullptr,
281*7c3d14c8STreehugger Robot /*filename_size*/ 0, &prot)) {
282*7c3d14c8STreehugger Robot uptr intersection_begin = Max(root_begin, begin);
283*7c3d14c8STreehugger Robot uptr intersection_end = Min(end, root_end);
284*7c3d14c8STreehugger Robot if (intersection_begin >= intersection_end) continue;
285*7c3d14c8STreehugger Robot bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
286*7c3d14c8STreehugger Robot LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
287*7c3d14c8STreehugger Robot root_begin, root_end, begin, end,
288*7c3d14c8STreehugger Robot is_readable ? "readable" : "unreadable");
289*7c3d14c8STreehugger Robot if (is_readable)
290*7c3d14c8STreehugger Robot ScanRangeForPointers(intersection_begin, intersection_end, frontier,
291*7c3d14c8STreehugger Robot "ROOT", kReachable);
292*7c3d14c8STreehugger Robot }
293*7c3d14c8STreehugger Robot }
294*7c3d14c8STreehugger Robot
295*7c3d14c8STreehugger Robot // Scans root regions for heap pointers.
ProcessRootRegions(Frontier * frontier)296*7c3d14c8STreehugger Robot static void ProcessRootRegions(Frontier *frontier) {
297*7c3d14c8STreehugger Robot if (!flags()->use_root_regions) return;
298*7c3d14c8STreehugger Robot CHECK(root_regions);
299*7c3d14c8STreehugger Robot for (uptr i = 0; i < root_regions->size(); i++) {
300*7c3d14c8STreehugger Robot RootRegion region = (*root_regions)[i];
301*7c3d14c8STreehugger Robot uptr begin_addr = reinterpret_cast<uptr>(region.begin);
302*7c3d14c8STreehugger Robot ProcessRootRegion(frontier, begin_addr, begin_addr + region.size);
303*7c3d14c8STreehugger Robot }
304*7c3d14c8STreehugger Robot }
305*7c3d14c8STreehugger Robot
FloodFillTag(Frontier * frontier,ChunkTag tag)306*7c3d14c8STreehugger Robot static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
307*7c3d14c8STreehugger Robot while (frontier->size()) {
308*7c3d14c8STreehugger Robot uptr next_chunk = frontier->back();
309*7c3d14c8STreehugger Robot frontier->pop_back();
310*7c3d14c8STreehugger Robot LsanMetadata m(next_chunk);
311*7c3d14c8STreehugger Robot ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier,
312*7c3d14c8STreehugger Robot "HEAP", tag);
313*7c3d14c8STreehugger Robot }
314*7c3d14c8STreehugger Robot }
315*7c3d14c8STreehugger Robot
316*7c3d14c8STreehugger Robot // ForEachChunk callback. If the chunk is marked as leaked, marks all chunks
317*7c3d14c8STreehugger Robot // which are reachable from it as indirectly leaked.
MarkIndirectlyLeakedCb(uptr chunk,void * arg)318*7c3d14c8STreehugger Robot static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
319*7c3d14c8STreehugger Robot chunk = GetUserBegin(chunk);
320*7c3d14c8STreehugger Robot LsanMetadata m(chunk);
321*7c3d14c8STreehugger Robot if (m.allocated() && m.tag() != kReachable) {
322*7c3d14c8STreehugger Robot ScanRangeForPointers(chunk, chunk + m.requested_size(),
323*7c3d14c8STreehugger Robot /* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
324*7c3d14c8STreehugger Robot }
325*7c3d14c8STreehugger Robot }
326*7c3d14c8STreehugger Robot
327*7c3d14c8STreehugger Robot // ForEachChunk callback. If chunk is marked as ignored, adds its address to
328*7c3d14c8STreehugger Robot // frontier.
CollectIgnoredCb(uptr chunk,void * arg)329*7c3d14c8STreehugger Robot static void CollectIgnoredCb(uptr chunk, void *arg) {
330*7c3d14c8STreehugger Robot CHECK(arg);
331*7c3d14c8STreehugger Robot chunk = GetUserBegin(chunk);
332*7c3d14c8STreehugger Robot LsanMetadata m(chunk);
333*7c3d14c8STreehugger Robot if (m.allocated() && m.tag() == kIgnored) {
334*7c3d14c8STreehugger Robot LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
335*7c3d14c8STreehugger Robot chunk, chunk + m.requested_size(), m.requested_size());
336*7c3d14c8STreehugger Robot reinterpret_cast<Frontier *>(arg)->push_back(chunk);
337*7c3d14c8STreehugger Robot }
338*7c3d14c8STreehugger Robot }
339*7c3d14c8STreehugger Robot
340*7c3d14c8STreehugger Robot // Sets the appropriate tag on each chunk.
ClassifyAllChunks(SuspendedThreadsList const & suspended_threads)341*7c3d14c8STreehugger Robot static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
342*7c3d14c8STreehugger Robot // Holds the flood fill frontier.
343*7c3d14c8STreehugger Robot Frontier frontier(1);
344*7c3d14c8STreehugger Robot
345*7c3d14c8STreehugger Robot ForEachChunk(CollectIgnoredCb, &frontier);
346*7c3d14c8STreehugger Robot ProcessGlobalRegions(&frontier);
347*7c3d14c8STreehugger Robot ProcessThreads(suspended_threads, &frontier);
348*7c3d14c8STreehugger Robot ProcessRootRegions(&frontier);
349*7c3d14c8STreehugger Robot FloodFillTag(&frontier, kReachable);
350*7c3d14c8STreehugger Robot
351*7c3d14c8STreehugger Robot // The check here is relatively expensive, so we do this in a separate flood
352*7c3d14c8STreehugger Robot // fill. That way we can skip the check for chunks that are reachable
353*7c3d14c8STreehugger Robot // otherwise.
354*7c3d14c8STreehugger Robot LOG_POINTERS("Processing platform-specific allocations.\n");
355*7c3d14c8STreehugger Robot CHECK_EQ(0, frontier.size());
356*7c3d14c8STreehugger Robot ProcessPlatformSpecificAllocations(&frontier);
357*7c3d14c8STreehugger Robot FloodFillTag(&frontier, kReachable);
358*7c3d14c8STreehugger Robot
359*7c3d14c8STreehugger Robot // Iterate over leaked chunks and mark those that are reachable from other
360*7c3d14c8STreehugger Robot // leaked chunks.
361*7c3d14c8STreehugger Robot LOG_POINTERS("Scanning leaked chunks.\n");
362*7c3d14c8STreehugger Robot ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
363*7c3d14c8STreehugger Robot }
364*7c3d14c8STreehugger Robot
365*7c3d14c8STreehugger Robot // ForEachChunk callback. Resets the tags to pre-leak-check state.
ResetTagsCb(uptr chunk,void * arg)366*7c3d14c8STreehugger Robot static void ResetTagsCb(uptr chunk, void *arg) {
367*7c3d14c8STreehugger Robot (void)arg;
368*7c3d14c8STreehugger Robot chunk = GetUserBegin(chunk);
369*7c3d14c8STreehugger Robot LsanMetadata m(chunk);
370*7c3d14c8STreehugger Robot if (m.allocated() && m.tag() != kIgnored)
371*7c3d14c8STreehugger Robot m.set_tag(kDirectlyLeaked);
372*7c3d14c8STreehugger Robot }
373*7c3d14c8STreehugger Robot
PrintStackTraceById(u32 stack_trace_id)374*7c3d14c8STreehugger Robot static void PrintStackTraceById(u32 stack_trace_id) {
375*7c3d14c8STreehugger Robot CHECK(stack_trace_id);
376*7c3d14c8STreehugger Robot StackDepotGet(stack_trace_id).Print();
377*7c3d14c8STreehugger Robot }
378*7c3d14c8STreehugger Robot
379*7c3d14c8STreehugger Robot // ForEachChunk callback. Aggregates information about unreachable chunks into
380*7c3d14c8STreehugger Robot // a LeakReport.
CollectLeaksCb(uptr chunk,void * arg)381*7c3d14c8STreehugger Robot static void CollectLeaksCb(uptr chunk, void *arg) {
382*7c3d14c8STreehugger Robot CHECK(arg);
383*7c3d14c8STreehugger Robot LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg);
384*7c3d14c8STreehugger Robot chunk = GetUserBegin(chunk);
385*7c3d14c8STreehugger Robot LsanMetadata m(chunk);
386*7c3d14c8STreehugger Robot if (!m.allocated()) return;
387*7c3d14c8STreehugger Robot if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
388*7c3d14c8STreehugger Robot u32 resolution = flags()->resolution;
389*7c3d14c8STreehugger Robot u32 stack_trace_id = 0;
390*7c3d14c8STreehugger Robot if (resolution > 0) {
391*7c3d14c8STreehugger Robot StackTrace stack = StackDepotGet(m.stack_trace_id());
392*7c3d14c8STreehugger Robot stack.size = Min(stack.size, resolution);
393*7c3d14c8STreehugger Robot stack_trace_id = StackDepotPut(stack);
394*7c3d14c8STreehugger Robot } else {
395*7c3d14c8STreehugger Robot stack_trace_id = m.stack_trace_id();
396*7c3d14c8STreehugger Robot }
397*7c3d14c8STreehugger Robot leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(),
398*7c3d14c8STreehugger Robot m.tag());
399*7c3d14c8STreehugger Robot }
400*7c3d14c8STreehugger Robot }
401*7c3d14c8STreehugger Robot
PrintMatchedSuppressions()402*7c3d14c8STreehugger Robot static void PrintMatchedSuppressions() {
403*7c3d14c8STreehugger Robot InternalMmapVector<Suppression *> matched(1);
404*7c3d14c8STreehugger Robot GetSuppressionContext()->GetMatched(&matched);
405*7c3d14c8STreehugger Robot if (!matched.size())
406*7c3d14c8STreehugger Robot return;
407*7c3d14c8STreehugger Robot const char *line = "-----------------------------------------------------";
408*7c3d14c8STreehugger Robot Printf("%s\n", line);
409*7c3d14c8STreehugger Robot Printf("Suppressions used:\n");
410*7c3d14c8STreehugger Robot Printf(" count bytes template\n");
411*7c3d14c8STreehugger Robot for (uptr i = 0; i < matched.size(); i++)
412*7c3d14c8STreehugger Robot Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed(
413*7c3d14c8STreehugger Robot &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ);
414*7c3d14c8STreehugger Robot Printf("%s\n\n", line);
415*7c3d14c8STreehugger Robot }
416*7c3d14c8STreehugger Robot
417*7c3d14c8STreehugger Robot struct CheckForLeaksParam {
418*7c3d14c8STreehugger Robot bool success;
419*7c3d14c8STreehugger Robot LeakReport leak_report;
420*7c3d14c8STreehugger Robot };
421*7c3d14c8STreehugger Robot
CheckForLeaksCallback(const SuspendedThreadsList & suspended_threads,void * arg)422*7c3d14c8STreehugger Robot static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
423*7c3d14c8STreehugger Robot void *arg) {
424*7c3d14c8STreehugger Robot CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
425*7c3d14c8STreehugger Robot CHECK(param);
426*7c3d14c8STreehugger Robot CHECK(!param->success);
427*7c3d14c8STreehugger Robot ClassifyAllChunks(suspended_threads);
428*7c3d14c8STreehugger Robot ForEachChunk(CollectLeaksCb, ¶m->leak_report);
429*7c3d14c8STreehugger Robot // Clean up for subsequent leak checks. This assumes we did not overwrite any
430*7c3d14c8STreehugger Robot // kIgnored tags.
431*7c3d14c8STreehugger Robot ForEachChunk(ResetTagsCb, nullptr);
432*7c3d14c8STreehugger Robot param->success = true;
433*7c3d14c8STreehugger Robot }
434*7c3d14c8STreehugger Robot
CheckForLeaks()435*7c3d14c8STreehugger Robot static bool CheckForLeaks() {
436*7c3d14c8STreehugger Robot if (&__lsan_is_turned_off && __lsan_is_turned_off())
437*7c3d14c8STreehugger Robot return false;
438*7c3d14c8STreehugger Robot EnsureMainThreadIDIsCorrect();
439*7c3d14c8STreehugger Robot CheckForLeaksParam param;
440*7c3d14c8STreehugger Robot param.success = false;
441*7c3d14c8STreehugger Robot LockThreadRegistry();
442*7c3d14c8STreehugger Robot LockAllocator();
443*7c3d14c8STreehugger Robot DoStopTheWorld(CheckForLeaksCallback, ¶m);
444*7c3d14c8STreehugger Robot UnlockAllocator();
445*7c3d14c8STreehugger Robot UnlockThreadRegistry();
446*7c3d14c8STreehugger Robot
447*7c3d14c8STreehugger Robot if (!param.success) {
448*7c3d14c8STreehugger Robot Report("LeakSanitizer has encountered a fatal error.\n");
449*7c3d14c8STreehugger Robot Report(
450*7c3d14c8STreehugger Robot "HINT: For debugging, try setting environment variable "
451*7c3d14c8STreehugger Robot "LSAN_OPTIONS=verbosity=1:log_threads=1\n");
452*7c3d14c8STreehugger Robot Die();
453*7c3d14c8STreehugger Robot }
454*7c3d14c8STreehugger Robot param.leak_report.ApplySuppressions();
455*7c3d14c8STreehugger Robot uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
456*7c3d14c8STreehugger Robot if (unsuppressed_count > 0) {
457*7c3d14c8STreehugger Robot Decorator d;
458*7c3d14c8STreehugger Robot Printf("\n"
459*7c3d14c8STreehugger Robot "================================================================="
460*7c3d14c8STreehugger Robot "\n");
461*7c3d14c8STreehugger Robot Printf("%s", d.Error());
462*7c3d14c8STreehugger Robot Report("ERROR: LeakSanitizer: detected memory leaks\n");
463*7c3d14c8STreehugger Robot Printf("%s", d.End());
464*7c3d14c8STreehugger Robot param.leak_report.ReportTopLeaks(flags()->max_leaks);
465*7c3d14c8STreehugger Robot }
466*7c3d14c8STreehugger Robot if (common_flags()->print_suppressions)
467*7c3d14c8STreehugger Robot PrintMatchedSuppressions();
468*7c3d14c8STreehugger Robot if (unsuppressed_count > 0) {
469*7c3d14c8STreehugger Robot param.leak_report.PrintSummary();
470*7c3d14c8STreehugger Robot return true;
471*7c3d14c8STreehugger Robot }
472*7c3d14c8STreehugger Robot return false;
473*7c3d14c8STreehugger Robot }
474*7c3d14c8STreehugger Robot
DoLeakCheck()475*7c3d14c8STreehugger Robot void DoLeakCheck() {
476*7c3d14c8STreehugger Robot BlockingMutexLock l(&global_mutex);
477*7c3d14c8STreehugger Robot static bool already_done;
478*7c3d14c8STreehugger Robot if (already_done) return;
479*7c3d14c8STreehugger Robot already_done = true;
480*7c3d14c8STreehugger Robot bool have_leaks = CheckForLeaks();
481*7c3d14c8STreehugger Robot if (!have_leaks) {
482*7c3d14c8STreehugger Robot return;
483*7c3d14c8STreehugger Robot }
484*7c3d14c8STreehugger Robot if (common_flags()->exitcode) {
485*7c3d14c8STreehugger Robot Die();
486*7c3d14c8STreehugger Robot }
487*7c3d14c8STreehugger Robot }
488*7c3d14c8STreehugger Robot
DoRecoverableLeakCheck()489*7c3d14c8STreehugger Robot static int DoRecoverableLeakCheck() {
490*7c3d14c8STreehugger Robot BlockingMutexLock l(&global_mutex);
491*7c3d14c8STreehugger Robot bool have_leaks = CheckForLeaks();
492*7c3d14c8STreehugger Robot return have_leaks ? 1 : 0;
493*7c3d14c8STreehugger Robot }
494*7c3d14c8STreehugger Robot
GetSuppressionForAddr(uptr addr)495*7c3d14c8STreehugger Robot static Suppression *GetSuppressionForAddr(uptr addr) {
496*7c3d14c8STreehugger Robot Suppression *s = nullptr;
497*7c3d14c8STreehugger Robot
498*7c3d14c8STreehugger Robot // Suppress by module name.
499*7c3d14c8STreehugger Robot SuppressionContext *suppressions = GetSuppressionContext();
500*7c3d14c8STreehugger Robot if (const char *module_name =
501*7c3d14c8STreehugger Robot Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
502*7c3d14c8STreehugger Robot if (suppressions->Match(module_name, kSuppressionLeak, &s))
503*7c3d14c8STreehugger Robot return s;
504*7c3d14c8STreehugger Robot
505*7c3d14c8STreehugger Robot // Suppress by file or function name.
506*7c3d14c8STreehugger Robot SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
507*7c3d14c8STreehugger Robot for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
508*7c3d14c8STreehugger Robot if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) ||
509*7c3d14c8STreehugger Robot suppressions->Match(cur->info.file, kSuppressionLeak, &s)) {
510*7c3d14c8STreehugger Robot break;
511*7c3d14c8STreehugger Robot }
512*7c3d14c8STreehugger Robot }
513*7c3d14c8STreehugger Robot frames->ClearAll();
514*7c3d14c8STreehugger Robot return s;
515*7c3d14c8STreehugger Robot }
516*7c3d14c8STreehugger Robot
GetSuppressionForStack(u32 stack_trace_id)517*7c3d14c8STreehugger Robot static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
518*7c3d14c8STreehugger Robot StackTrace stack = StackDepotGet(stack_trace_id);
519*7c3d14c8STreehugger Robot for (uptr i = 0; i < stack.size; i++) {
520*7c3d14c8STreehugger Robot Suppression *s = GetSuppressionForAddr(
521*7c3d14c8STreehugger Robot StackTrace::GetPreviousInstructionPc(stack.trace[i]));
522*7c3d14c8STreehugger Robot if (s) return s;
523*7c3d14c8STreehugger Robot }
524*7c3d14c8STreehugger Robot return nullptr;
525*7c3d14c8STreehugger Robot }
526*7c3d14c8STreehugger Robot
527*7c3d14c8STreehugger Robot ///// LeakReport implementation. /////
528*7c3d14c8STreehugger Robot
529*7c3d14c8STreehugger Robot // A hard limit on the number of distinct leaks, to avoid quadratic complexity
530*7c3d14c8STreehugger Robot // in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks
531*7c3d14c8STreehugger Robot // in real-world applications.
532*7c3d14c8STreehugger Robot // FIXME: Get rid of this limit by changing the implementation of LeakReport to
533*7c3d14c8STreehugger Robot // use a hash table.
534*7c3d14c8STreehugger Robot const uptr kMaxLeaksConsidered = 5000;
535*7c3d14c8STreehugger Robot
AddLeakedChunk(uptr chunk,u32 stack_trace_id,uptr leaked_size,ChunkTag tag)536*7c3d14c8STreehugger Robot void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
537*7c3d14c8STreehugger Robot uptr leaked_size, ChunkTag tag) {
538*7c3d14c8STreehugger Robot CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
539*7c3d14c8STreehugger Robot bool is_directly_leaked = (tag == kDirectlyLeaked);
540*7c3d14c8STreehugger Robot uptr i;
541*7c3d14c8STreehugger Robot for (i = 0; i < leaks_.size(); i++) {
542*7c3d14c8STreehugger Robot if (leaks_[i].stack_trace_id == stack_trace_id &&
543*7c3d14c8STreehugger Robot leaks_[i].is_directly_leaked == is_directly_leaked) {
544*7c3d14c8STreehugger Robot leaks_[i].hit_count++;
545*7c3d14c8STreehugger Robot leaks_[i].total_size += leaked_size;
546*7c3d14c8STreehugger Robot break;
547*7c3d14c8STreehugger Robot }
548*7c3d14c8STreehugger Robot }
549*7c3d14c8STreehugger Robot if (i == leaks_.size()) {
550*7c3d14c8STreehugger Robot if (leaks_.size() == kMaxLeaksConsidered) return;
551*7c3d14c8STreehugger Robot Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id,
552*7c3d14c8STreehugger Robot is_directly_leaked, /* is_suppressed */ false };
553*7c3d14c8STreehugger Robot leaks_.push_back(leak);
554*7c3d14c8STreehugger Robot }
555*7c3d14c8STreehugger Robot if (flags()->report_objects) {
556*7c3d14c8STreehugger Robot LeakedObject obj = {leaks_[i].id, chunk, leaked_size};
557*7c3d14c8STreehugger Robot leaked_objects_.push_back(obj);
558*7c3d14c8STreehugger Robot }
559*7c3d14c8STreehugger Robot }
560*7c3d14c8STreehugger Robot
LeakComparator(const Leak & leak1,const Leak & leak2)561*7c3d14c8STreehugger Robot static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
562*7c3d14c8STreehugger Robot if (leak1.is_directly_leaked == leak2.is_directly_leaked)
563*7c3d14c8STreehugger Robot return leak1.total_size > leak2.total_size;
564*7c3d14c8STreehugger Robot else
565*7c3d14c8STreehugger Robot return leak1.is_directly_leaked;
566*7c3d14c8STreehugger Robot }
567*7c3d14c8STreehugger Robot
ReportTopLeaks(uptr num_leaks_to_report)568*7c3d14c8STreehugger Robot void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) {
569*7c3d14c8STreehugger Robot CHECK(leaks_.size() <= kMaxLeaksConsidered);
570*7c3d14c8STreehugger Robot Printf("\n");
571*7c3d14c8STreehugger Robot if (leaks_.size() == kMaxLeaksConsidered)
572*7c3d14c8STreehugger Robot Printf("Too many leaks! Only the first %zu leaks encountered will be "
573*7c3d14c8STreehugger Robot "reported.\n",
574*7c3d14c8STreehugger Robot kMaxLeaksConsidered);
575*7c3d14c8STreehugger Robot
576*7c3d14c8STreehugger Robot uptr unsuppressed_count = UnsuppressedLeakCount();
577*7c3d14c8STreehugger Robot if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count)
578*7c3d14c8STreehugger Robot Printf("The %zu top leak(s):\n", num_leaks_to_report);
579*7c3d14c8STreehugger Robot InternalSort(&leaks_, leaks_.size(), LeakComparator);
580*7c3d14c8STreehugger Robot uptr leaks_reported = 0;
581*7c3d14c8STreehugger Robot for (uptr i = 0; i < leaks_.size(); i++) {
582*7c3d14c8STreehugger Robot if (leaks_[i].is_suppressed) continue;
583*7c3d14c8STreehugger Robot PrintReportForLeak(i);
584*7c3d14c8STreehugger Robot leaks_reported++;
585*7c3d14c8STreehugger Robot if (leaks_reported == num_leaks_to_report) break;
586*7c3d14c8STreehugger Robot }
587*7c3d14c8STreehugger Robot if (leaks_reported < unsuppressed_count) {
588*7c3d14c8STreehugger Robot uptr remaining = unsuppressed_count - leaks_reported;
589*7c3d14c8STreehugger Robot Printf("Omitting %zu more leak(s).\n", remaining);
590*7c3d14c8STreehugger Robot }
591*7c3d14c8STreehugger Robot }
592*7c3d14c8STreehugger Robot
PrintReportForLeak(uptr index)593*7c3d14c8STreehugger Robot void LeakReport::PrintReportForLeak(uptr index) {
594*7c3d14c8STreehugger Robot Decorator d;
595*7c3d14c8STreehugger Robot Printf("%s", d.Leak());
596*7c3d14c8STreehugger Robot Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
597*7c3d14c8STreehugger Robot leaks_[index].is_directly_leaked ? "Direct" : "Indirect",
598*7c3d14c8STreehugger Robot leaks_[index].total_size, leaks_[index].hit_count);
599*7c3d14c8STreehugger Robot Printf("%s", d.End());
600*7c3d14c8STreehugger Robot
601*7c3d14c8STreehugger Robot PrintStackTraceById(leaks_[index].stack_trace_id);
602*7c3d14c8STreehugger Robot
603*7c3d14c8STreehugger Robot if (flags()->report_objects) {
604*7c3d14c8STreehugger Robot Printf("Objects leaked above:\n");
605*7c3d14c8STreehugger Robot PrintLeakedObjectsForLeak(index);
606*7c3d14c8STreehugger Robot Printf("\n");
607*7c3d14c8STreehugger Robot }
608*7c3d14c8STreehugger Robot }
609*7c3d14c8STreehugger Robot
PrintLeakedObjectsForLeak(uptr index)610*7c3d14c8STreehugger Robot void LeakReport::PrintLeakedObjectsForLeak(uptr index) {
611*7c3d14c8STreehugger Robot u32 leak_id = leaks_[index].id;
612*7c3d14c8STreehugger Robot for (uptr j = 0; j < leaked_objects_.size(); j++) {
613*7c3d14c8STreehugger Robot if (leaked_objects_[j].leak_id == leak_id)
614*7c3d14c8STreehugger Robot Printf("%p (%zu bytes)\n", leaked_objects_[j].addr,
615*7c3d14c8STreehugger Robot leaked_objects_[j].size);
616*7c3d14c8STreehugger Robot }
617*7c3d14c8STreehugger Robot }
618*7c3d14c8STreehugger Robot
PrintSummary()619*7c3d14c8STreehugger Robot void LeakReport::PrintSummary() {
620*7c3d14c8STreehugger Robot CHECK(leaks_.size() <= kMaxLeaksConsidered);
621*7c3d14c8STreehugger Robot uptr bytes = 0, allocations = 0;
622*7c3d14c8STreehugger Robot for (uptr i = 0; i < leaks_.size(); i++) {
623*7c3d14c8STreehugger Robot if (leaks_[i].is_suppressed) continue;
624*7c3d14c8STreehugger Robot bytes += leaks_[i].total_size;
625*7c3d14c8STreehugger Robot allocations += leaks_[i].hit_count;
626*7c3d14c8STreehugger Robot }
627*7c3d14c8STreehugger Robot InternalScopedString summary(kMaxSummaryLength);
628*7c3d14c8STreehugger Robot summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes,
629*7c3d14c8STreehugger Robot allocations);
630*7c3d14c8STreehugger Robot ReportErrorSummary(summary.data());
631*7c3d14c8STreehugger Robot }
632*7c3d14c8STreehugger Robot
ApplySuppressions()633*7c3d14c8STreehugger Robot void LeakReport::ApplySuppressions() {
634*7c3d14c8STreehugger Robot for (uptr i = 0; i < leaks_.size(); i++) {
635*7c3d14c8STreehugger Robot Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
636*7c3d14c8STreehugger Robot if (s) {
637*7c3d14c8STreehugger Robot s->weight += leaks_[i].total_size;
638*7c3d14c8STreehugger Robot atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
639*7c3d14c8STreehugger Robot leaks_[i].hit_count);
640*7c3d14c8STreehugger Robot leaks_[i].is_suppressed = true;
641*7c3d14c8STreehugger Robot }
642*7c3d14c8STreehugger Robot }
643*7c3d14c8STreehugger Robot }
644*7c3d14c8STreehugger Robot
UnsuppressedLeakCount()645*7c3d14c8STreehugger Robot uptr LeakReport::UnsuppressedLeakCount() {
646*7c3d14c8STreehugger Robot uptr result = 0;
647*7c3d14c8STreehugger Robot for (uptr i = 0; i < leaks_.size(); i++)
648*7c3d14c8STreehugger Robot if (!leaks_[i].is_suppressed) result++;
649*7c3d14c8STreehugger Robot return result;
650*7c3d14c8STreehugger Robot }
651*7c3d14c8STreehugger Robot
652*7c3d14c8STreehugger Robot } // namespace __lsan
653*7c3d14c8STreehugger Robot #else // CAN_SANITIZE_LEAKS
654*7c3d14c8STreehugger Robot namespace __lsan {
InitCommonLsan()655*7c3d14c8STreehugger Robot void InitCommonLsan() { }
DoLeakCheck()656*7c3d14c8STreehugger Robot void DoLeakCheck() { }
DisableInThisThread()657*7c3d14c8STreehugger Robot void DisableInThisThread() { }
EnableInThisThread()658*7c3d14c8STreehugger Robot void EnableInThisThread() { }
659*7c3d14c8STreehugger Robot }
660*7c3d14c8STreehugger Robot #endif // CAN_SANITIZE_LEAKS
661*7c3d14c8STreehugger Robot
662*7c3d14c8STreehugger Robot using namespace __lsan; // NOLINT
663*7c3d14c8STreehugger Robot
664*7c3d14c8STreehugger Robot extern "C" {
665*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_ignore_object(const void * p)666*7c3d14c8STreehugger Robot void __lsan_ignore_object(const void *p) {
667*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
668*7c3d14c8STreehugger Robot if (!common_flags()->detect_leaks)
669*7c3d14c8STreehugger Robot return;
670*7c3d14c8STreehugger Robot // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not
671*7c3d14c8STreehugger Robot // locked.
672*7c3d14c8STreehugger Robot BlockingMutexLock l(&global_mutex);
673*7c3d14c8STreehugger Robot IgnoreObjectResult res = IgnoreObjectLocked(p);
674*7c3d14c8STreehugger Robot if (res == kIgnoreObjectInvalid)
675*7c3d14c8STreehugger Robot VReport(1, "__lsan_ignore_object(): no heap object found at %p", p);
676*7c3d14c8STreehugger Robot if (res == kIgnoreObjectAlreadyIgnored)
677*7c3d14c8STreehugger Robot VReport(1, "__lsan_ignore_object(): "
678*7c3d14c8STreehugger Robot "heap object at %p is already being ignored\n", p);
679*7c3d14c8STreehugger Robot if (res == kIgnoreObjectSuccess)
680*7c3d14c8STreehugger Robot VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p);
681*7c3d14c8STreehugger Robot #endif // CAN_SANITIZE_LEAKS
682*7c3d14c8STreehugger Robot }
683*7c3d14c8STreehugger Robot
684*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_register_root_region(const void * begin,uptr size)685*7c3d14c8STreehugger Robot void __lsan_register_root_region(const void *begin, uptr size) {
686*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
687*7c3d14c8STreehugger Robot BlockingMutexLock l(&global_mutex);
688*7c3d14c8STreehugger Robot CHECK(root_regions);
689*7c3d14c8STreehugger Robot RootRegion region = {begin, size};
690*7c3d14c8STreehugger Robot root_regions->push_back(region);
691*7c3d14c8STreehugger Robot VReport(1, "Registered root region at %p of size %llu\n", begin, size);
692*7c3d14c8STreehugger Robot #endif // CAN_SANITIZE_LEAKS
693*7c3d14c8STreehugger Robot }
694*7c3d14c8STreehugger Robot
695*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_unregister_root_region(const void * begin,uptr size)696*7c3d14c8STreehugger Robot void __lsan_unregister_root_region(const void *begin, uptr size) {
697*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
698*7c3d14c8STreehugger Robot BlockingMutexLock l(&global_mutex);
699*7c3d14c8STreehugger Robot CHECK(root_regions);
700*7c3d14c8STreehugger Robot bool removed = false;
701*7c3d14c8STreehugger Robot for (uptr i = 0; i < root_regions->size(); i++) {
702*7c3d14c8STreehugger Robot RootRegion region = (*root_regions)[i];
703*7c3d14c8STreehugger Robot if (region.begin == begin && region.size == size) {
704*7c3d14c8STreehugger Robot removed = true;
705*7c3d14c8STreehugger Robot uptr last_index = root_regions->size() - 1;
706*7c3d14c8STreehugger Robot (*root_regions)[i] = (*root_regions)[last_index];
707*7c3d14c8STreehugger Robot root_regions->pop_back();
708*7c3d14c8STreehugger Robot VReport(1, "Unregistered root region at %p of size %llu\n", begin, size);
709*7c3d14c8STreehugger Robot break;
710*7c3d14c8STreehugger Robot }
711*7c3d14c8STreehugger Robot }
712*7c3d14c8STreehugger Robot if (!removed) {
713*7c3d14c8STreehugger Robot Report(
714*7c3d14c8STreehugger Robot "__lsan_unregister_root_region(): region at %p of size %llu has not "
715*7c3d14c8STreehugger Robot "been registered.\n",
716*7c3d14c8STreehugger Robot begin, size);
717*7c3d14c8STreehugger Robot Die();
718*7c3d14c8STreehugger Robot }
719*7c3d14c8STreehugger Robot #endif // CAN_SANITIZE_LEAKS
720*7c3d14c8STreehugger Robot }
721*7c3d14c8STreehugger Robot
722*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_disable()723*7c3d14c8STreehugger Robot void __lsan_disable() {
724*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
725*7c3d14c8STreehugger Robot __lsan::DisableInThisThread();
726*7c3d14c8STreehugger Robot #endif
727*7c3d14c8STreehugger Robot }
728*7c3d14c8STreehugger Robot
729*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_enable()730*7c3d14c8STreehugger Robot void __lsan_enable() {
731*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
732*7c3d14c8STreehugger Robot __lsan::EnableInThisThread();
733*7c3d14c8STreehugger Robot #endif
734*7c3d14c8STreehugger Robot }
735*7c3d14c8STreehugger Robot
736*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_do_leak_check()737*7c3d14c8STreehugger Robot void __lsan_do_leak_check() {
738*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
739*7c3d14c8STreehugger Robot if (common_flags()->detect_leaks)
740*7c3d14c8STreehugger Robot __lsan::DoLeakCheck();
741*7c3d14c8STreehugger Robot #endif // CAN_SANITIZE_LEAKS
742*7c3d14c8STreehugger Robot }
743*7c3d14c8STreehugger Robot
744*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE
__lsan_do_recoverable_leak_check()745*7c3d14c8STreehugger Robot int __lsan_do_recoverable_leak_check() {
746*7c3d14c8STreehugger Robot #if CAN_SANITIZE_LEAKS
747*7c3d14c8STreehugger Robot if (common_flags()->detect_leaks)
748*7c3d14c8STreehugger Robot return __lsan::DoRecoverableLeakCheck();
749*7c3d14c8STreehugger Robot #endif // CAN_SANITIZE_LEAKS
750*7c3d14c8STreehugger Robot return 0;
751*7c3d14c8STreehugger Robot }
752*7c3d14c8STreehugger Robot
753*7c3d14c8STreehugger Robot #if !SANITIZER_SUPPORTS_WEAK_HOOKS
754*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
__lsan_is_turned_off()755*7c3d14c8STreehugger Robot int __lsan_is_turned_off() {
756*7c3d14c8STreehugger Robot return 0;
757*7c3d14c8STreehugger Robot }
758*7c3d14c8STreehugger Robot #endif
759*7c3d14c8STreehugger Robot } // extern "C"
760