Index: compiler-rt/lib/lsan/lsan_common_mac.cpp =================================================================== --- compiler-rt/lib/lsan/lsan_common_mac.cpp +++ compiler-rt/lib/lsan/lsan_common_mac.cpp @@ -17,22 +17,26 @@ #if CAN_SANITIZE_LEAKS && SANITIZER_APPLE -#include "sanitizer_common/sanitizer_allocator_internal.h" -#include "lsan_allocator.h" - -#include - #include +#include +#include -// Only introduced in Mac OS X 10.9. -#ifdef VM_MEMORY_OS_ALLOC_ONCE -static const int kSanitizerVmMemoryOsAllocOnce = VM_MEMORY_OS_ALLOC_ONCE; -#else -static const int kSanitizerVmMemoryOsAllocOnce = 73; -#endif - +#include "lsan_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" namespace __lsan { +enum class SeenRegion { + None = 0, + AllocOnce = 1 << 0, + LibDispatch = 1 << 1, + Foundation = 1 << 2, + All = AllocOnce | LibDispatch | Foundation +}; +struct RegionScanState { + int seen_regions = static_cast(SeenRegion::None); + bool in_libdispatch = false; +}; + typedef struct { int disable_counter; u32 current_thread_id; @@ -148,6 +152,7 @@ InternalMmapVectorNoCtor const *root_regions = GetRootRegions(); + RegionScanState scan_state; while (err == KERN_SUCCESS) { vm_size_t size = 0; unsigned depth = 1; @@ -157,17 +162,35 @@ (vm_region_info_t)&info, &count); uptr end_address = address + size; - - // libxpc stashes some pointers in the Kernel Alloc Once page, - // make sure not to report those as leaks. - if (info.user_tag == kSanitizerVmMemoryOsAllocOnce) { + if (info.user_tag == VM_MEMORY_OS_ALLOC_ONCE) { + // libxpc stashes some pointers in the Kernel Alloc Once page, + // make sure not to report those as leaks. + scan_state.seen_regions |= static_cast(SeenRegion::AllocOnce); + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); + } else if (info.user_tag == VM_MEMORY_FOUNDATION) { + // Objective-C block trampolines use the Foundation region. + scan_state.seen_regions = static_cast(SeenRegion::Foundation); + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); + } else if (info.user_tag == VM_MEMORY_LIBDISPATCH) { + // Dispatch continuations use the libdispatch region. Empirically, there + // can be more than one region with this tag, so we'll optimistically + // assume that they're continguous. Otherwise, we would need to scan every + // region to ensure we find them all. + scan_state.in_libdispatch = true; ScanRangeForPointers(address, end_address, frontier, "GLOBAL", kReachable); + } else if (scan_state.in_libdispatch) { + scan_state.seen_regions |= static_cast(SeenRegion::LibDispatch); + scan_state.in_libdispatch = false; + } - // Recursing over the full memory map is very slow, break out - // early if we don't need the full iteration. - if (!flags()->use_root_regions || !root_regions->size()) - break; + // Recursing over the full memory map is very slow, break out + // early if we don't need the full iteration. + if (scan_state.seen_regions == static_cast(SeenRegion::All) && + !(flags()->use_root_regions && root_regions->size() > 0)) { + break; } // This additional root region scan is required on Darwin in order to Index: compiler-rt/test/lsan/TestCases/Darwin/dispatch_continuations.mm =================================================================== --- /dev/null +++ compiler-rt/test/lsan/TestCases/Darwin/dispatch_continuations.mm @@ -0,0 +1,23 @@ +// Test that dispatch continuation memory region is scanned. +// RUN: %clangxx_lsan %s -o %t -framework Foundation +// RUN: %env_lsan_opts="report_objects=1" %run %t 2>&1 && echo "" | FileCheck %s + +#include +#include + +int main() { + dispatch_queue_t fake_rl_queue = dispatch_get_global_queue(2, 0); + dispatch_source_t timer = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, fake_rl_queue); + dispatch_source_set_event_handler(timer, ^{ + }); + dispatch_source_set_timer(timer, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, + 321); + dispatch_resume(timer); + __lsan_do_leak_check(); + dispatch_source_cancel(timer); + dispatch_release(timer); + return 0; +} + +// CHECK-NOT: LeakSanitizer: detected memory leaks Index: compiler-rt/test/lsan/TestCases/Darwin/trampoline.mm =================================================================== --- /dev/null +++ compiler-rt/test/lsan/TestCases/Darwin/trampoline.mm @@ -0,0 +1,18 @@ +// Test that the memory region that contains Objective-C block trampolines +// is scanned. +// FIXME: Find a way to reduce this without AppKit to remove Mac requirement. +// UNSUPPORTED: ios +// RUN: %clangxx_lsan %s -o %t -framework Cocoa -fno-objc-arc +// RUN: %env_lsan_opts="report_objects=1" %run %t 2>&1 && echo "" | FileCheck %s + +#import + +#include + +int main() { + NSView *view = + [[[NSView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)] autorelease]; + __lsan_do_leak_check(); + return 0; +} +// CHECK-NOT: LeakSanitizer: detected memory leaks