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