[compiler-rt] r235728 - [lsan] Add an interface function for on-demand leak checking.

Alexey Samsonov vonosmas at gmail.com
Fri Apr 24 12:49:24 PDT 2015


On Fri, Apr 24, 2015 at 12:29 PM, David Blaikie <dblaikie at gmail.com> wrote:

> I'm seeing a couple of these warnings on Ubuntu:
>
> ld: warning: Cannot export local symbol '__lsan_do_recoverable_leak_check'
>

Thanks for pointing it out! Please check if r235761 fixes this.


>
> On Fri, Apr 24, 2015 at 9:53 AM, Sergey Matveev <earthdok at google.com>
> wrote:
> > Author: smatveev
> > Date: Fri Apr 24 11:53:15 2015
> > New Revision: 235728
> >
> > URL: http://llvm.org/viewvc/llvm-project?rev=235728&view=rev
> > Log:
> > [lsan] Add an interface function for on-demand leak checking.
> >
> > Summary:
> > Add an interface function which can be used to periodically trigger
> > leak detection in a long-running process.
> >
> > NB: The meaning of the kIgnored tag has been changed to allow easy
> clean-up
> > between subsequent leak checks. Previously, this tag was applied to
> explicitly
> > ignored (i.e. with __lsan_disable() or __lsan_ignore_object()) chunks
> *and* any
> > chunks only reachable from those. With this change, it's only applied to
> > explicitly ignored chunks.
> >
> > Reviewers: samsonov
> >
> > Reviewed By: samsonov
> >
> > Subscribers: llvm-commits
> >
> > Differential Revision: http://reviews.llvm.org/D9159
> >
> > Added:
> >     compiler-rt/trunk/test/lsan/TestCases/recoverable_leak_check.cc
> > Modified:
> >     compiler-rt/trunk/include/sanitizer/lsan_interface.h
> >     compiler-rt/trunk/lib/lsan/lsan_common.cc
> >     compiler-rt/trunk/lib/lsan/lsan_common_linux.cc
> >
> > Modified: compiler-rt/trunk/include/sanitizer/lsan_interface.h
> > URL:
> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/include/sanitizer/lsan_interface.h?rev=235728&r1=235727&r2=235728&view=diff
> >
> ==============================================================================
> > --- compiler-rt/trunk/include/sanitizer/lsan_interface.h (original)
> > +++ compiler-rt/trunk/include/sanitizer/lsan_interface.h Fri Apr 24
> 11:53:15 2015
> > @@ -41,14 +41,25 @@ extern "C" {
> >    void __lsan_register_root_region(const void *p, size_t size);
> >    void __lsan_unregister_root_region(const void *p, size_t size);
> >
> > -  // Calling this function makes LSan enter the leak checking phase
> immediately.
> > -  // Use this if normal end-of-process leak checking happens too late
> (e.g. if
> > -  // you have intentional memory leaks in your shutdown code). Calling
> this
> > -  // function overrides end-of-process leak checking; it must be called
> at
> > -  // most once per process. This function will terminate the process if
> there
> > -  // are memory leaks and the exit_code flag is non-zero.
> > +  // Check for leaks now. This function behaves identically to the
> default
> > +  // end-of-process leak check. In particular, it will terminate the
> process if
> > +  // leaks are found and the exit_code flag is non-zero.
> > +  // Subsequent calls to this function will have no effect and
> end-of-process
> > +  // leak check will not run. Effectively, end-of-process leak check is
> moved to
> > +  // the time of first invocation of this function.
> > +  // By calling this function early during process shutdown, you can
> instruct
> > +  // LSan to ignore shutdown-only leaks which happen later on.
> >    void __lsan_do_leak_check();
> >
> > +  // Check for leaks now. Returns zero if no leaks have been found or
> if leak
> > +  // detection is disabled, non-zero otherwise.
> > +  // This function may be called repeatedly, e.g. to periodically check
> a
> > +  // long-running process. It prints a leak report if appropriate, but
> does not
> > +  // terminate the process. It does not affect the behavior of
> > +  // __lsan_do_leak_check() or the end-of-process leak check, and is not
> > +  // affected by them.
> > +  int __lsan_do_recoverable_leak_check();
> > +
> >    // The user may optionally provide this function to disallow leak
> checking
> >    // for the program it is linked into (if the return value is
> non-zero). This
> >    // function must be defined as returning a constant value; any
> behavior beyond
> >
> > Modified: compiler-rt/trunk/lib/lsan/lsan_common.cc
> > URL:
> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.cc?rev=235728&r1=235727&r2=235728&view=diff
> >
> ==============================================================================
> > --- compiler-rt/trunk/lib/lsan/lsan_common.cc (original)
> > +++ compiler-rt/trunk/lib/lsan/lsan_common.cc Fri Apr 24 11:53:15 2015
> > @@ -126,13 +126,14 @@ static inline bool CanBeAHeapPointer(upt
> >
> >  // Scans the memory range, looking for byte patterns that point into
> allocator
> >  // chunks. Marks those chunks with |tag| and adds them to |frontier|.
> > -// There are two usage modes for this function: finding reachable or
> ignored
> > -// chunks (|tag| = kReachable or kIgnored) and finding indirectly
> leaked chunks
> > +// There are two usage modes for this function: finding reachable chunks
> > +// (|tag| = kReachable) and finding indirectly leaked chunks
> >  // (|tag| = kIndirectlyLeaked). In the second case, there's no flood
> fill,
> >  // so |frontier| = 0.
> >  void ScanRangeForPointers(uptr begin, uptr end,
> >                            Frontier *frontier,
> >                            const char *region_type, ChunkTag tag) {
> > +  CHECK(tag == kReachable || tag == kIndirectlyLeaked);
> >    const uptr alignment = flags()->pointer_alignment();
> >    LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
> >    uptr pp = begin;
> > @@ -146,9 +147,7 @@ void ScanRangeForPointers(uptr begin, up
> >      // Pointers to self don't count. This matters when tag ==
> kIndirectlyLeaked.
> >      if (chunk == begin) continue;
> >      LsanMetadata m(chunk);
> > -    // Reachable beats ignored beats leaked.
> > -    if (m.tag() == kReachable) continue;
> > -    if (m.tag() == kIgnored && tag != kReachable) continue;
> > +    if (m.tag() == kReachable || m.tag() == kIgnored) continue;
> >
> >      // Do this check relatively late so we can log only the interesting
> cases.
> >      if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
> > @@ -287,7 +286,7 @@ static void MarkIndirectlyLeakedCb(uptr
> >    LsanMetadata m(chunk);
> >    if (m.allocated() && m.tag() != kReachable) {
> >      ScanRangeForPointers(chunk, chunk + m.requested_size(),
> > -                         /* frontier */ 0, "HEAP", kIndirectlyLeaked);
> > +                         /* frontier */ nullptr, "HEAP",
> kIndirectlyLeaked);
> >    }
> >  }
> >
> > @@ -297,8 +296,11 @@ static void CollectIgnoredCb(uptr chunk,
> >    CHECK(arg);
> >    chunk = GetUserBegin(chunk);
> >    LsanMetadata m(chunk);
> > -  if (m.allocated() && m.tag() == kIgnored)
> > +  if (m.allocated() && m.tag() == kIgnored) {
> > +    LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
> > +                 chunk, chunk + m.requested_size(), m.requested_size());
> >      reinterpret_cast<Frontier *>(arg)->push_back(chunk);
> > +  }
> >  }
> >
> >  // Sets the appropriate tag on each chunk.
> > @@ -306,26 +308,33 @@ static void ClassifyAllChunks(SuspendedT
> >    // Holds the flood fill frontier.
> >    Frontier frontier(1);
> >
> > +  ForEachChunk(CollectIgnoredCb, &frontier);
> >    ProcessGlobalRegions(&frontier);
> >    ProcessThreads(suspended_threads, &frontier);
> >    ProcessRootRegions(&frontier);
> >    FloodFillTag(&frontier, kReachable);
> > +
> >    // The check here is relatively expensive, so we do this in a
> separate flood
> >    // fill. That way we can skip the check for chunks that are reachable
> >    // otherwise.
> >    LOG_POINTERS("Processing platform-specific allocations.\n");
> > +  CHECK_EQ(0, frontier.size());
> >    ProcessPlatformSpecificAllocations(&frontier);
> >    FloodFillTag(&frontier, kReachable);
> >
> > -  LOG_POINTERS("Scanning ignored chunks.\n");
> > -  CHECK_EQ(0, frontier.size());
> > -  ForEachChunk(CollectIgnoredCb, &frontier);
> > -  FloodFillTag(&frontier, kIgnored);
> > -
> >    // Iterate over leaked chunks and mark those that are reachable from
> other
> >    // leaked chunks.
> >    LOG_POINTERS("Scanning leaked chunks.\n");
> > -  ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */);
> > +  ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
> > +}
> > +
> > +// ForEachChunk callback. Resets the tags to pre-leak-check state.
> > +static void ResetTagsCb(uptr chunk, void *arg) {
> > +  (void)arg;
> > +  chunk = GetUserBegin(chunk);
> > +  LsanMetadata m(chunk);
> > +  if (m.allocated() && m.tag() != kIgnored)
> > +    m.set_tag(kDirectlyLeaked);
> >  }
> >
> >  static void PrintStackTraceById(u32 stack_trace_id) {
> > @@ -371,35 +380,33 @@ static void PrintMatchedSuppressions() {
> >    Printf("%s\n\n", line);
> >  }
> >
> > -struct DoLeakCheckParam {
> > +struct CheckForLeaksParam {
> >    bool success;
> >    LeakReport leak_report;
> >  };
> >
> > -static void DoLeakCheckCallback(const SuspendedThreadsList
> &suspended_threads,
> > -                                void *arg) {
> > -  DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg);
> > +static void CheckForLeaksCallback(const SuspendedThreadsList
> &suspended_threads,
> > +                                  void *arg) {
> > +  CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam
> *>(arg);
> >    CHECK(param);
> >    CHECK(!param->success);
> >    ClassifyAllChunks(suspended_threads);
> >    ForEachChunk(CollectLeaksCb, &param->leak_report);
> > +  // Clean up for subsequent leak checks. This assumes we did not
> overwrite any
> > +  // kIgnored tags.
> > +  ForEachChunk(ResetTagsCb, nullptr);
> >    param->success = true;
> >  }
> >
> > -void DoLeakCheck() {
> > -  EnsureMainThreadIDIsCorrect();
> > -  BlockingMutexLock l(&global_mutex);
> > -  static bool already_done;
> > -  if (already_done) return;
> > -  already_done = true;
> > +static bool CheckForLeaks() {
> >    if (&__lsan_is_turned_off && __lsan_is_turned_off())
> > -      return;
> > -
> > -  DoLeakCheckParam param;
> > +      return false;
> > +  EnsureMainThreadIDIsCorrect();
> > +  CheckForLeaksParam param;
> >    param.success = false;
> >    LockThreadRegistry();
> >    LockAllocator();
> > -  DoStopTheWorld(DoLeakCheckCallback, &param);
> > +  DoStopTheWorld(CheckForLeaksCallback, &param);
> >    UnlockAllocator();
> >    UnlockThreadRegistry();
> >
> > @@ -423,14 +430,33 @@ void DoLeakCheck() {
> >      PrintMatchedSuppressions();
> >    if (unsuppressed_count > 0) {
> >      param.leak_report.PrintSummary();
> > -    if (flags()->exitcode) {
> > -      if (common_flags()->coverage)
> > -        __sanitizer_cov_dump();
> > -      internal__exit(flags()->exitcode);
> > -    }
> > +    return true;
> > +  }
> > +  return false;
> > +}
> > +
> > +void DoLeakCheck() {
> > +  BlockingMutexLock l(&global_mutex);
> > +  static bool already_done;
> > +  if (already_done) return;
> > +  already_done = true;
> > +  bool have_leaks = CheckForLeaks();
> > +  if (!have_leaks) {
> > +    return;
> > +  }
> > +  if (flags()->exitcode) {
> > +    if (common_flags()->coverage)
> > +      __sanitizer_cov_dump();
> > +    internal__exit(flags()->exitcode);
> >    }
> >  }
> >
> > +static int DoRecoverableLeakCheck() {
> > +  BlockingMutexLock l(&global_mutex);
> > +  bool have_leaks = CheckForLeaks();
> > +  return have_leaks ? 1 : 0;
> > +}
> > +
> >  static Suppression *GetSuppressionForAddr(uptr addr) {
> >    Suppression *s = nullptr;
> >
> > @@ -676,6 +702,14 @@ void __lsan_do_leak_check() {
> >  #endif  // CAN_SANITIZE_LEAKS
> >  }
> >
> > +int __lsan_do_recoverable_leak_check() {
> > +#if CAN_SANITIZE_LEAKS
> > +  if (common_flags()->detect_leaks)
> > +    return __lsan::DoRecoverableLeakCheck();
> > +#endif  // CAN_SANITIZE_LEAKS
> > +  return 0;
> > +}
> > +
> >  #if !SANITIZER_SUPPORTS_WEAK_HOOKS
> >  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
> >  int __lsan_is_turned_off() {
> >
> > Modified: compiler-rt/trunk/lib/lsan/lsan_common_linux.cc
> > URL:
> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common_linux.cc?rev=235728&r1=235727&r2=235728&view=diff
> >
> ==============================================================================
> > --- compiler-rt/trunk/lib/lsan/lsan_common_linux.cc (original)
> > +++ compiler-rt/trunk/lib/lsan/lsan_common_linux.cc Fri Apr 24 11:53:15
> 2015
> > @@ -110,7 +110,7 @@ static void ProcessPlatformSpecificAlloc
> >        reinterpret_cast<ProcessPlatformAllocParam *>(arg);
> >    chunk = GetUserBegin(chunk);
> >    LsanMetadata m(chunk);
> > -  if (m.allocated() && m.tag() != kReachable) {
> > +  if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
> >      u32 stack_id = m.stack_trace_id();
> >      uptr caller_pc = 0;
> >      if (stack_id > 0)
> >
> > Added: compiler-rt/trunk/test/lsan/TestCases/recoverable_leak_check.cc
> > URL:
> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/lsan/TestCases/recoverable_leak_check.cc?rev=235728&view=auto
> >
> ==============================================================================
> > --- compiler-rt/trunk/test/lsan/TestCases/recoverable_leak_check.cc
> (added)
> > +++ compiler-rt/trunk/test/lsan/TestCases/recoverable_leak_check.cc Fri
> Apr 24 11:53:15 2015
> > @@ -0,0 +1,32 @@
> > +// Test for on-demand leak checking.
> > +// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
> > +// RUN: %clangxx_lsan %s -o %t
> > +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s
> > +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s
> > +
> > +#include <assert.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <sanitizer/lsan_interface.h>
> > +
> > +void *p;
> > +
> > +int main(int argc, char *argv[]) {
> > +  p = malloc(23);
> > +
> > +  assert(__lsan_do_recoverable_leak_check() == 0);
> > +
> > +  fprintf(stderr, "Test alloc: %p.\n", malloc(1337));
> > +// CHECK: Test alloc:
> > +
> > +  assert(__lsan_do_recoverable_leak_check() == 1);
> > +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte
> > +
> > +  // Test that we correctly reset chunk tags.
> > +  p = 0;
> > +  assert(__lsan_do_recoverable_leak_check() == 1);
> > +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1360 byte
> > +
> > +  _exit(0);
> > +}
> >
> >
> > _______________________________________________
> > llvm-commits mailing list
> > llvm-commits at cs.uiuc.edu
> > http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>



-- 
Alexey Samsonov
vonosmas at gmail.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150424/c8495f94/attachment.html>


More information about the llvm-commits mailing list