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