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