aboutsummaryrefslogtreecommitdiff
path: root/lib/lsan
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lsan')
-rw-r--r--lib/lsan/CMakeLists.txt27
-rw-r--r--lib/lsan/lit_tests/AsanConfig/lit.cfg32
-rw-r--r--lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in8
-rw-r--r--lib/lsan/lit_tests/CMakeLists.txt37
-rw-r--r--lib/lsan/lit_tests/LsanConfig/lit.cfg30
-rw-r--r--lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in8
-rw-r--r--lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc12
-rw-r--r--lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg4
-rw-r--r--lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc45
-rw-r--r--lib/lsan/lit_tests/TestCases/disabler.cc23
-rw-r--r--lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc38
-rw-r--r--lib/lsan/lit_tests/TestCases/do_leak_check_override.cc36
-rw-r--r--lib/lsan/lit_tests/TestCases/fork.cc24
-rw-r--r--lib/lsan/lit_tests/TestCases/fork_threaded.cc43
-rw-r--r--lib/lsan/lit_tests/TestCases/high_allocator_contention.cc48
-rw-r--r--lib/lsan/lit_tests/TestCases/ignore_object.cc30
-rw-r--r--lib/lsan/lit_tests/TestCases/ignore_object_errors.cc22
-rw-r--r--lib/lsan/lit_tests/TestCases/large_allocation_leak.cc18
-rw-r--r--lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc19
-rw-r--r--lib/lsan/lit_tests/TestCases/link_turned_off.cc24
-rw-r--r--lib/lsan/lit_tests/TestCases/pointer_to_self.cc18
-rw-r--r--lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c10
-rw-r--r--lib/lsan/lit_tests/TestCases/stale_stack_leak.cc42
-rw-r--r--lib/lsan/lit_tests/TestCases/suppressions_default.cc29
-rw-r--r--lib/lsan/lit_tests/TestCases/suppressions_file.cc29
-rw-r--r--lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp1
-rw-r--r--lib/lsan/lit_tests/TestCases/swapcontext.cc42
-rw-r--r--lib/lsan/lit_tests/TestCases/use_after_return.cc23
-rw-r--r--lib/lsan/lit_tests/TestCases/use_globals_initialized.cc21
-rw-r--r--lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc21
-rw-r--r--lib/lsan/lit_tests/TestCases/use_registers.cc51
-rw-r--r--lib/lsan/lit_tests/TestCases/use_stacks.cc20
-rw-r--r--lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc36
-rw-r--r--lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc33
-rw-r--r--lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc37
-rw-r--r--lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc31
-rw-r--r--lib/lsan/lit_tests/TestCases/use_tls_static.cc21
-rw-r--r--lib/lsan/lit_tests/TestCases/use_unaligned.cc23
-rw-r--r--lib/lsan/lit_tests/Unit/lit.site.cfg.in12
-rw-r--r--lib/lsan/lit_tests/lit.common.cfg43
-rw-r--r--lib/lsan/lsan.cc26
-rw-r--r--lib/lsan/lsan.h20
-rw-r--r--lib/lsan/lsan_allocator.cc50
-rw-r--r--lib/lsan/lsan_allocator.h2
-rw-r--r--lib/lsan/lsan_common.cc430
-rw-r--r--lib/lsan/lsan_common.h49
-rw-r--r--lib/lsan/lsan_common_linux.cc32
-rw-r--r--lib/lsan/lsan_interceptors.cc50
-rw-r--r--lib/lsan/lsan_preinit.cc6
-rw-r--r--lib/lsan/tests/CMakeLists.txt58
-rw-r--r--lib/lsan/tests/lsan_dummy_unittest.cc22
-rw-r--r--lib/lsan/tests/lsan_testlib.cc25
52 files changed, 441 insertions, 1400 deletions
diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt
index 3018a06622ee..2ea765de1bc7 100644
--- a/lib/lsan/CMakeLists.txt
+++ b/lib/lsan/CMakeLists.txt
@@ -1,8 +1,7 @@
include_directories(..)
-set(LSAN_CFLAGS
- ${SANITIZER_COMMON_CFLAGS}
- -fno-rtti)
+set(LSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+append_no_rtti_flag(LSAN_CFLAGS)
set(LSAN_COMMON_SOURCES
lsan_common.cc
@@ -17,16 +16,7 @@ set(LSAN_SOURCES
set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-# The common files need to build on every arch supported by ASan.
-# (Even if they build into dummy object files.)
-filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH
- x86_64 i386 powerpc64)
-
-# Architectures supported by the standalone LSan.
-filter_available_targets(LSAN_SUPPORTED_ARCH
- x86_64)
-
-set(LSAN_RUNTIME_LIBRARIES)
+add_custom_target(lsan)
if(APPLE)
foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
@@ -35,7 +25,7 @@ if(APPLE)
SOURCES ${LSAN_COMMON_SOURCES}
CFLAGS ${LSAN_CFLAGS})
endforeach()
-elseif(NOT ANDROID)
+else()
foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH})
add_compiler_rt_object_library(RTLSanCommon ${arch}
SOURCES ${LSAN_COMMON_SOURCES}
@@ -43,18 +33,15 @@ elseif(NOT ANDROID)
endforeach()
foreach(arch ${LSAN_SUPPORTED_ARCH})
- add_compiler_rt_static_runtime(clang_rt.lsan-${arch} ${arch}
+ add_compiler_rt_runtime(clang_rt.lsan-${arch} ${arch} STATIC
SOURCES ${LSAN_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
$<TARGET_OBJECTS:RTLSanCommon.${arch}>
CFLAGS ${LSAN_CFLAGS})
- list(APPEND LSAN_RUNTIME_LIBRARIES clang_rt.lsan-${arch})
+ add_dependencies(lsan clang_rt.lsan-${arch})
endforeach()
endif()
-if (LLVM_INCLUDE_TESTS)
- add_subdirectory(tests)
-endif()
-add_subdirectory(lit_tests)
+add_dependencies(compiler-rt lsan)
diff --git a/lib/lsan/lit_tests/AsanConfig/lit.cfg b/lib/lsan/lit_tests/AsanConfig/lit.cfg
deleted file mode 100644
index ae9198173ffc..000000000000
--- a/lib/lsan/lit_tests/AsanConfig/lit.cfg
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- Python -*-
-
-import os
-
-def get_required_attr(config, attr_name):
- attr_value = getattr(config, attr_name, None)
- if not attr_value:
- lit_config.fatal(
- "No attribute %r in test configuration! You may need to run "
- "tests from your build directory or add this attribute "
- "to lit.site.cfg " % attr_name)
- return attr_value
-
-lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root")
-lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg")
-if not os.path.exists(lsan_lit_cfg):
- lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg)
-lit_config.load_config(config, lsan_lit_cfg)
-
-config.name = 'LeakSanitizer-AddressSanitizer'
-
-clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=address "
-
-config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " +
- clang_lsan_cxxflags + " ")) )
-
-clang_lsan_cflags = config.clang_cflags + " -fsanitize=address "
-
-config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " +
- clang_lsan_cflags + " ")) )
-
-config.environment['ASAN_OPTIONS'] = 'detect_leaks=1'
diff --git a/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in b/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in
deleted file mode 100644
index 9cf6572c54b7..000000000000
--- a/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in
+++ /dev/null
@@ -1,8 +0,0 @@
-# Load common config for all compiler-rt lit tests.
-lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured")
-
-# Tool-specific config options.
-config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@"
-
-# Load tool-specific config that would do the real work.
-lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/AsanConfig/lit.cfg")
diff --git a/lib/lsan/lit_tests/CMakeLists.txt b/lib/lsan/lit_tests/CMakeLists.txt
deleted file mode 100644
index 526d71bf3418..000000000000
--- a/lib/lsan/lit_tests/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-set(LSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
-set(LSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..)
-
-set(LSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-
-configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/LsanConfig/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig/lit.site.cfg
- )
-
-configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/AsanConfig/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg
- )
-
-configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
- )
-
-if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT APPLE AND NOT ANDROID)
- set(LSAN_TEST_DEPS
- ${SANITIZER_COMMON_LIT_TEST_DEPS}
- ${LSAN_RUNTIME_LIBRARIES})
- foreach(arch ${LSAN_SUPPORTED_ARCH})
- list(APPEND LSAN_TEST_DEPS clang_rt.asan-${arch})
- endforeach()
- if(LLVM_INCLUDE_TESTS)
- list(APPEND LSAN_TEST_DEPS LsanUnitTests)
- endif()
- add_lit_testsuite(check-lsan "Running the LeakSanitizer tests"
- ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig
- ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig
- ${CMAKE_CURRENT_BINARY_DIR}/Unit
- DEPENDS ${LSAN_TEST_DEPS})
- set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests")
-endif()
diff --git a/lib/lsan/lit_tests/LsanConfig/lit.cfg b/lib/lsan/lit_tests/LsanConfig/lit.cfg
deleted file mode 100644
index 84faf9167a78..000000000000
--- a/lib/lsan/lit_tests/LsanConfig/lit.cfg
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- Python -*-
-
-import os
-
-def get_required_attr(config, attr_name):
- attr_value = getattr(config, attr_name, None)
- if not attr_value:
- lit_config.fatal(
- "No attribute %r in test configuration! You may need to run "
- "tests from your build directory or add this attribute "
- "to lit.site.cfg " % attr_name)
- return attr_value
-
-lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root")
-lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg")
-if not os.path.exists(lsan_lit_cfg):
- lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg)
-lit_config.load_config(config, lsan_lit_cfg)
-
-config.name = 'LeakSanitizer-Standalone'
-
-clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=leak "
-
-config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " +
- clang_lsan_cxxflags + " ")) )
-
-clang_lsan_cflags = config.clang_cflags + " -fsanitize=leak "
-
-config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " +
- clang_lsan_cflags + " ")) )
diff --git a/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in b/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in
deleted file mode 100644
index 2a6d724c0436..000000000000
--- a/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in
+++ /dev/null
@@ -1,8 +0,0 @@
-# Load common config for all compiler-rt lit tests.
-lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured")
-
-# Tool-specific config options.
-config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@"
-
-# Load tool-specific config that would do the real work.
-lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/LsanConfig/lit.cfg")
diff --git a/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc b/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc
deleted file mode 100644
index 475a66ec12a4..000000000000
--- a/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-// A loadable module with a large thread local section, which would require
-// allocation of a new TLS storage chunk when loaded with dlopen(). We use it
-// to test the reachability of such chunks in LSan tests.
-
-// This must be large enough that it doesn't fit into preallocated static TLS
-// space (see STATIC_TLS_SURPLUS in glibc).
-__thread void *huge_thread_local_array[(1 << 20) / sizeof(void *)]; // NOLINT
-
-extern "C" void **StoreToTLS(void *p) {
- huge_thread_local_array[0] = p;
- return &huge_thread_local_array[0];
-}
diff --git a/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg b/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg
deleted file mode 100644
index b3677c17a0f2..000000000000
--- a/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-# Sources in this directory are compiled as shared libraries and used by
-# tests in parent directory.
-
-config.suffixes = []
diff --git a/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc
deleted file mode 100644
index ab368245317c..000000000000
--- a/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Regression test for thread lifetime tracking. Thread data should be
-// considered live during the thread's termination, at least until the
-// user-installed TSD destructors have finished running (since they may contain
-// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it
-// makes its best effort.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %t 2>&1 | FileCheck %s
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-pthread_key_t key;
-__thread void *p;
-
-void key_destructor(void *arg) {
- // Generally this may happen on a different thread.
- __lsan_do_leak_check();
-}
-
-void *thread_func(void *arg) {
- p = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", p);
- int res = pthread_setspecific(key, (void*)1);
- assert(res == 0);
- return 0;
-}
-
-int main() {
- int res = pthread_key_create(&key, &key_destructor);
- assert(res == 0);
- pthread_t thread_id;
- res = pthread_create(&thread_id, 0, thread_func, 0);
- assert(res == 0);
- res = pthread_join(thread_id, 0);
- assert(res == 0);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: leaked 1337 byte object at [[ADDR]]
diff --git a/lib/lsan/lit_tests/TestCases/disabler.cc b/lib/lsan/lit_tests/TestCases/disabler.cc
deleted file mode 100644
index db0cd8fabe4d..000000000000
--- a/lib/lsan/lit_tests/TestCases/disabler.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Test for ScopedDisabler.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-int main() {
- void **p;
- {
- __lsan::ScopedDisabler d;
- p = new void *;
- }
- *reinterpret_cast<void **>(p) = malloc(666);
- void *q = malloc(1337);
- // Break optimization.
- fprintf(stderr, "Test alloc: %p.\n", q);
- return 0;
-}
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc
deleted file mode 100644
index 94e4fc390b3b..000000000000
--- a/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Regression test. Disabler should not depend on TSD validity.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE %t
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-pthread_key_t key;
-
-void key_destructor(void *arg) {
- __lsan::ScopedDisabler d;
- void *p = malloc(1337);
- // Break optimization.
- fprintf(stderr, "Test alloc: %p.\n", p);
- pthread_setspecific(key, 0);
-}
-
-void *thread_func(void *arg) {
- int res = pthread_setspecific(key, (void*)1);
- assert(res == 0);
- return 0;
-}
-
-int main() {
- int res = pthread_key_create(&key, &key_destructor);
- assert(res == 0);
- pthread_t thread_id;
- res = pthread_create(&thread_id, 0, thread_func, 0);
- assert(res == 0);
- res = pthread_join(thread_id, 0);
- assert(res == 0);
- return 0;
-}
diff --git a/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc b/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc
deleted file mode 100644
index be0ed0a6d48f..000000000000
--- a/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Test for __lsan_do_leak_check(). We test it by making the leak check run
-// before global destructors, which also tests compatibility with HeapChecker's
-// "normal" mode (LSan runs in "strict" mode by default).
-// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sanitizer/lsan_interface.h>
-
-struct LeakyGlobal {
- LeakyGlobal() {
- p = malloc(1337);
- }
- ~LeakyGlobal() {
- p = 0;
- }
- void *p;
-};
-
-LeakyGlobal leaky_global;
-
-int main(int argc, char *argv[]) {
- // Register leak check to run before global destructors.
- if (argc > 1)
- atexit(&__lsan_do_leak_check);
- void *p = malloc(666);
- printf("Test alloc: %p\n", p);
- printf("Test alloc in leaky global: %p\n", leaky_global.p);
- return 0;
-}
-
-// CHECK-strict: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s)
-// CHECK-normal: SUMMARY: {{(Leak|Address)}}Sanitizer: 666 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/fork.cc b/lib/lsan/lit_tests/TestCases/fork.cc
deleted file mode 100644
index 69258d9a0c72..000000000000
--- a/lib/lsan/lit_tests/TestCases/fork.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Test that thread local data is handled correctly after forking without exec().
-// RUN: %clangxx_lsan %s -o %t
-// RUN: %t 2>&1
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-__thread void *thread_local_var;
-
-int main() {
- int status = 0;
- thread_local_var = malloc(1337);
- pid_t pid = fork();
- assert(pid >= 0);
- if (pid > 0) {
- waitpid(pid, &status, 0);
- assert(WIFEXITED(status));
- return WEXITSTATUS(status);
- }
- return 0;
-}
diff --git a/lib/lsan/lit_tests/TestCases/fork_threaded.cc b/lib/lsan/lit_tests/TestCases/fork_threaded.cc
deleted file mode 100644
index 24a586109e28..000000000000
--- a/lib/lsan/lit_tests/TestCases/fork_threaded.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Test that thread local data is handled correctly after forking without
-// exec(). In this test leak checking is initiated from a non-main thread.
-// RUN: %clangxx_lsan %s -o %t
-// RUN: %t 2>&1
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-__thread void *thread_local_var;
-
-void *exit_thread_func(void *arg) {
- exit(0);
-}
-
-void ExitFromThread() {
- pthread_t tid;
- int res;
- res = pthread_create(&tid, 0, exit_thread_func, 0);
- assert(res == 0);
- pthread_join(tid, 0);
-}
-
-int main() {
- int status = 0;
- thread_local_var = malloc(1337);
- pid_t pid = fork();
- assert(pid >= 0);
- if (pid > 0) {
- waitpid(pid, &status, 0);
- assert(WIFEXITED(status));
- return WEXITSTATUS(status);
- } else {
- // Spawn a thread and call exit() from there, to check that we track main
- // thread's pid correctly even if leak checking is initiated from another
- // thread.
- ExitFromThread();
- }
- return 0;
-}
diff --git a/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc b/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc
deleted file mode 100644
index 1cecb2a550a1..000000000000
--- a/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// A benchmark that executes malloc/free pairs in parallel.
-// Usage: ./a.out number_of_threads total_number_of_allocations
-// RUN: %clangxx_lsan %s -o %t
-// RUN: %t 5 1000000 2>&1
-#include <assert.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-int num_threads;
-int total_num_alloc;
-const int kMaxNumThreads = 5000;
-pthread_t tid[kMaxNumThreads];
-
-pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
-pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-bool go = false;
-
-void *thread_fun(void *arg) {
- pthread_mutex_lock(&mutex);
- while (!go) pthread_cond_wait(&cond, &mutex);
- pthread_mutex_unlock(&mutex);
- for (int i = 0; i < total_num_alloc / num_threads; i++) {
- void *p = malloc(10);
- __asm__ __volatile__("" : : "r"(p) : "memory");
- free((void *)p);
- }
- return 0;
-}
-
-int main(int argc, char** argv) {
- assert(argc == 3);
- num_threads = atoi(argv[1]);
- assert(num_threads > 0);
- assert(num_threads <= kMaxNumThreads);
- total_num_alloc = atoi(argv[2]);
- assert(total_num_alloc > 0);
- printf("%d threads, %d allocations in each\n", num_threads,
- total_num_alloc / num_threads);
- for (int i = 0; i < num_threads; i++)
- pthread_create(&tid[i], 0, thread_fun, 0);
- pthread_mutex_lock(&mutex);
- go = true;
- pthread_cond_broadcast(&cond);
- pthread_mutex_unlock(&mutex);
- for (int i = 0; i < num_threads; i++) pthread_join(tid[i], 0);
- return 0;
-}
diff --git a/lib/lsan/lit_tests/TestCases/ignore_object.cc b/lib/lsan/lit_tests/TestCases/ignore_object.cc
deleted file mode 100644
index cbc743b75497..000000000000
--- a/lib/lsan/lit_tests/TestCases/ignore_object.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Test for __lsan_ignore_object().
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=3"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-int main() {
- {
- // The first malloc call can cause an allocation in libdl. Ignore it here so
- // it doesn't show up in our output.
- __lsan::ScopedDisabler d;
- malloc(1);
- }
- // Explicitly ignored object.
- void **p = new void *;
- // Transitively ignored object.
- *p = malloc(666);
- // Non-ignored object.
- volatile void *q = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", p);
- __lsan_ignore_object(p);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: ignoring heap object at [[ADDR]]
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc b/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc
deleted file mode 100644
index 2a6c72551772..000000000000
--- a/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Test for incorrect use of __lsan_ignore_object().
-// RUN: LSAN_BASE="verbosity=2"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-int main() {
- void *p = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", p);
- __lsan_ignore_object(p);
- __lsan_ignore_object(p);
- free(p);
- __lsan_ignore_object(p);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: heap object at [[ADDR]] is already being ignored
-// CHECK: no heap object found at [[ADDR]]
diff --git a/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc b/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc
deleted file mode 100644
index 57d056597ee2..000000000000
--- a/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int main() {
- // maxsize in primary allocator is always less than this (1 << 25).
- void *large_alloc = malloc(33554432);
- fprintf(stderr, "Test alloc: %p.\n", large_alloc);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 33554432 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc b/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc
deleted file mode 100644
index 38c1063b6ebb..000000000000
--- a/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Test for the leak_check_at_exit flag.
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS="verbosity=1" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
-// RUN: LSAN_OPTIONS="verbosity=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-do
-// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
-// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont
-
-#include <stdio.h>
-#include <sanitizer/lsan_interface.h>
-
-int main(int argc, char *argv[]) {
- printf("printf to break optimization\n");
- if (argc > 1)
- __lsan_do_leak_check();
- return 0;
-}
-
-// CHECK-do: SUMMARY: {{(Leak|Address)}}Sanitizer:
-// CHECK-dont-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/link_turned_off.cc b/lib/lsan/lit_tests/TestCases/link_turned_off.cc
deleted file mode 100644
index 93628a1d15ee..000000000000
--- a/lib/lsan/lit_tests/TestCases/link_turned_off.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Test for disabling LSan at link-time.
-// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck %s
-
-#include <sanitizer/lsan_interface.h>
-
-int argc_copy;
-
-extern "C" {
-int __lsan_is_turned_off() {
- return (argc_copy == 1);
-}
-}
-
-int main(int argc, char *argv[]) {
- volatile int *x = new int;
- *x = 42;
- argc_copy = argc;
- return 0;
-}
-
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 4 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/pointer_to_self.cc b/lib/lsan/lit_tests/TestCases/pointer_to_self.cc
deleted file mode 100644
index 0d2818d2fa1d..000000000000
--- a/lib/lsan/lit_tests/TestCases/pointer_to_self.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Regression test: pointers to self should not confuse LSan into thinking the
-// object is indirectly leaked. Only external pointers count.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int main() {
- void *p = malloc(1337);
- *reinterpret_cast<void **>(p) = p;
- fprintf(stderr, "Test alloc: %p.\n", p);
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c b/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c
deleted file mode 100644
index 085412b47d55..000000000000
--- a/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// Check that we can build C code.
-// RUN: %clang_lsan %s -o %t
-#ifdef __cplusplus
-#error "This test must be built in C mode"
-#endif
-
-int main() {
- // FIXME: ideally this should somehow check that we don't have libstdc++
- return 0;
-}
diff --git a/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc b/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc
deleted file mode 100644
index fabfb4ff21a9..000000000000
--- a/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Test that out-of-scope local variables are ignored by LSan.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void **pp;
-
-// Put pointer far enough on the stack that LSan has space to run in without
-// overwriting it.
-// Hopefully the argument p will be passed on a register, saving us from false
-// negatives.
-__attribute__((noinline))
-void *PutPointerOnStaleStack(void *p) {
- void *locals[2048];
- locals[0] = p;
- pp = &locals[0];
- fprintf(stderr, "Test alloc: %p.\n", locals[0]);
- return 0;
-}
-
-int main() {
- PutPointerOnStaleStack(malloc(1337));
- return 0;
-}
-
-// This must run after LSan, to ensure LSan didn't overwrite the pointer before
-// it had a chance to see it. If LSan is invoked with atexit(), this works.
-// Otherwise, we need a different method.
-__attribute__((destructor))
-void ConfirmPointerHasSurvived() {
- fprintf(stderr, "Value after LSan: %p.\n", *pp);
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK-sanity: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
-// CHECK-sanity: Value after LSan: [[ADDR]].
diff --git a/lib/lsan/lit_tests/TestCases/suppressions_default.cc b/lib/lsan/lit_tests/TestCases/suppressions_default.cc
deleted file mode 100644
index 9a165f8770f9..000000000000
--- a/lib/lsan/lit_tests/TestCases/suppressions_default.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Test for ScopedDisabler.
-// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-extern "C"
-const char *__lsan_default_suppressions() {
- return "leak:*LSanTestLeakingFunc*";
-}
-
-void LSanTestLeakingFunc() {
- void *p = malloc(666);
- fprintf(stderr, "Test alloc: %p.\n", p);
-}
-
-int main() {
- LSanTestLeakingFunc();
- void *q = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", q);
- return 0;
-}
-// CHECK: Suppressions used:
-// CHECK: 1 666 *LSanTestLeakingFunc*
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/suppressions_file.cc b/lib/lsan/lit_tests/TestCases/suppressions_file.cc
deleted file mode 100644
index 9a165f8770f9..000000000000
--- a/lib/lsan/lit_tests/TestCases/suppressions_file.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Test for ScopedDisabler.
-// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-extern "C"
-const char *__lsan_default_suppressions() {
- return "leak:*LSanTestLeakingFunc*";
-}
-
-void LSanTestLeakingFunc() {
- void *p = malloc(666);
- fprintf(stderr, "Test alloc: %p.\n", p);
-}
-
-int main() {
- LSanTestLeakingFunc();
- void *q = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", q);
- return 0;
-}
-// CHECK: Suppressions used:
-// CHECK: 1 666 *LSanTestLeakingFunc*
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp b/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp
deleted file mode 100644
index 8d8e560cba4c..000000000000
--- a/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp
+++ /dev/null
@@ -1 +0,0 @@
-leak:*LSanTestLeakingFunc*
diff --git a/lib/lsan/lit_tests/TestCases/swapcontext.cc b/lib/lsan/lit_tests/TestCases/swapcontext.cc
deleted file mode 100644
index a06685ca2f03..000000000000
--- a/lib/lsan/lit_tests/TestCases/swapcontext.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// We can't unwind stack if we're running coroutines on heap-allocated
-// memory. Make sure we don't report these leaks.
-
-// RUN: %clangxx_lsan %s -o %t
-// RUN: %t 2>&1
-// RUN: not %t foo 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-const int kStackSize = 1 << 20;
-
-void Child() {
- int child_stack;
- printf("Child: %p\n", &child_stack);
- int *leaked = new int[666];
-}
-
-int main(int argc, char *argv[]) {
- char stack_memory[kStackSize + 1];
- char *heap_memory = new char[kStackSize + 1];
- char *child_stack = (argc > 1) ? stack_memory : heap_memory;
-
- printf("Child stack: %p\n", child_stack);
- ucontext_t orig_context;
- ucontext_t child_context;
- getcontext(&child_context);
- child_context.uc_stack.ss_sp = child_stack;
- child_context.uc_stack.ss_size = kStackSize / 2;
- child_context.uc_link = &orig_context;
- makecontext(&child_context, Child, 0);
- if (swapcontext(&orig_context, &child_context) < 0) {
- perror("swapcontext");
- return 1;
- }
-
- delete[] heap_memory;
- return 0;
-}
-
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 2664 byte(s) leaked in 1 allocation(s)
diff --git a/lib/lsan/lit_tests/TestCases/use_after_return.cc b/lib/lsan/lit_tests/TestCases/use_after_return.cc
deleted file mode 100644
index 93b0ea6068ef..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_after_return.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Test that fake stack (introduced by ASan's use-after-return mode) is included
-// in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0"
-// RUN: %clangxx_lsan %s -O2 -o %t
-// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s
-// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1
-// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %t 2>&1
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int main() {
- void *stack_var = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", stack_var);
- // Take pointer to variable, to ensure it's not optimized into a register.
- fprintf(stderr, "Stack var at: %p.\n", &stack_var);
- // Do not return from main to prevent the pointer from going out of scope.
- exit(0);
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc b/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc
deleted file mode 100644
index 5a7c48bdf49a..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Test that initialized globals are included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void *data_var = (void *)1;
-
-int main() {
- data_var = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", data_var);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc b/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc
deleted file mode 100644
index e1d045e3f79f..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Test that uninitialized globals are included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <stdio.h>
-#include <stdlib.h>
-
-void *bss_var;
-
-int main() {
- bss_var = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", bss_var);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_registers.cc b/lib/lsan/lit_tests/TestCases/use_registers.cc
deleted file mode 100644
index a7d8a69d7173..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_registers.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Test that registers of running threads are included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0"
-// RUN: %clangxx_lsan -pthread %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-extern "C"
-void *registers_thread_func(void *arg) {
- int *sync = reinterpret_cast<int *>(arg);
- void *p = malloc(1337);
- // To store the pointer, choose a register which is unlikely to be reused by
- // a function call.
-#if defined(__i386__)
- asm ( "mov %0, %%esi"
- :
- : "r" (p)
- );
-#elif defined(__x86_64__)
- asm ( "mov %0, %%r15"
- :
- : "r" (p)
- );
-#else
-#error "Test is not supported on this architecture."
-#endif
- fprintf(stderr, "Test alloc: %p.\n", p);
- fflush(stderr);
- __sync_fetch_and_xor(sync, 1);
- while (true)
- pthread_yield();
-}
-
-int main() {
- int sync = 0;
- pthread_t thread_id;
- int res = pthread_create(&thread_id, 0, registers_thread_func, &sync);
- assert(res == 0);
- while (!__sync_fetch_and_xor(&sync, 0))
- pthread_yield();
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_stacks.cc b/lib/lsan/lit_tests/TestCases/use_stacks.cc
deleted file mode 100644
index 4287a96b2285..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_stacks.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Test that stack of main thread is included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int main() {
- void *stack_var = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", stack_var);
- // Do not return from main to prevent the pointer from going out of scope.
- exit(0);
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc b/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc
deleted file mode 100644
index c7dfaf8abad6..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Test that stacks of non-main threads are included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0"
-// RUN: %clangxx_lsan -pthread %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-extern "C"
-void *stacks_thread_func(void *arg) {
- int *sync = reinterpret_cast<int *>(arg);
- void *p = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", p);
- fflush(stderr);
- __sync_fetch_and_xor(sync, 1);
- while (true)
- pthread_yield();
-}
-
-int main() {
- int sync = 0;
- pthread_t thread_id;
- int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync);
- assert(res == 0);
- while (!__sync_fetch_and_xor(&sync, 0))
- pthread_yield();
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc b/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc
deleted file mode 100644
index 2570b63f0c5e..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Test that dynamically allocated TLS space is included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx %p/SharedLibs/huge_tls_lib_so.cc -fPIC -shared -o %t-so.so
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <assert.h>
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-
-int main(int argc, char *argv[]) {
- std::string path = std::string(argv[0]) + "-so.so";
-
- void *handle = dlopen(path.c_str(), RTLD_LAZY);
- assert(handle != 0);
- typedef void **(* store_t)(void *p);
- store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS");
- assert(dlerror() == 0);
-
- void *p = malloc(1337);
- void **p_in_tls = StoreToTLS(p);
- assert(*p_in_tls == p);
- fprintf(stderr, "Test alloc: %p.\n", p);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc
deleted file mode 100644
index 3dea41edddd4..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Test that dynamically allocated thread-specific storage is included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-// From glibc: this many keys are stored in the thread descriptor directly.
-const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32;
-
-int main() {
- static const unsigned kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE;
- int res;
- pthread_key_t dummy_keys[kDummyKeysCount];
- for (unsigned i = 0; i < kDummyKeysCount; i++) {
- res = pthread_key_create(&dummy_keys[i], NULL);
- assert(res == 0);
- }
- pthread_key_t key;
- res = pthread_key_create(&key, NULL);
- assert(key >= PTHREAD_KEY_2NDLEVEL_SIZE);
- assert(res == 0);
- void *p = malloc(1337);
- res = pthread_setspecific(key, p);
- assert(res == 0);
- fprintf(stderr, "Test alloc: %p.\n", p);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc
deleted file mode 100644
index b75f15153863..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Test that statically allocated thread-specific storage is included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-// From glibc: this many keys are stored in the thread descriptor directly.
-const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32;
-
-int main() {
- pthread_key_t key;
- int res;
- res = pthread_key_create(&key, NULL);
- assert(res == 0);
- assert(key < PTHREAD_KEY_2NDLEVEL_SIZE);
- void *p = malloc(1337);
- res = pthread_setspecific(key, p);
- assert(res == 0);
- fprintf(stderr, "Test alloc: %p.\n", p);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_tls_static.cc b/lib/lsan/lit_tests/TestCases/use_tls_static.cc
deleted file mode 100644
index 9ccb2b2b7fb1..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_tls_static.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Test that statically allocated TLS space is included in the root set.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1
-// RUN: LSAN_OPTIONS="" %t 2>&1
-
-#include <stdio.h>
-#include <stdlib.h>
-
-__thread void *tls_var;
-
-int main() {
- tls_var = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", tls_var);
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/TestCases/use_unaligned.cc b/lib/lsan/lit_tests/TestCases/use_unaligned.cc
deleted file mode 100644
index bc75f11b99f0..000000000000
--- a/lib/lsan/lit_tests/TestCases/use_unaligned.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Test that unaligned pointers are detected correctly.
-// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %t 2>&1 | FileCheck %s
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %t 2>&1
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-void *arr[2];
-
-int main() {
- void *p = malloc(1337);
- fprintf(stderr, "Test alloc: %p.\n", p);
- char *char_arr = (char *)arr;
- memcpy(char_arr + 1, &p, sizeof(p));
- return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: Directly leaked 1337 byte object at [[ADDR]]
-// CHECK: LeakSanitizer: detected memory leaks
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
diff --git a/lib/lsan/lit_tests/Unit/lit.site.cfg.in b/lib/lsan/lit_tests/Unit/lit.site.cfg.in
deleted file mode 100644
index a3a4e9ad0b13..000000000000
--- a/lib/lsan/lit_tests/Unit/lit.site.cfg.in
+++ /dev/null
@@ -1,12 +0,0 @@
-## Autogenerated by LLVM/Clang configuration.
-# Do not edit!
-
-# Load common config for all compiler-rt unit tests.
-lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured")
-
-# Setup config name.
-config.name = 'LeakSanitizer-Unit'
-# Setup test source and exec root. For unit tests, we define
-# it as build directory with LSan unit tests.
-config.test_exec_root = "@LSAN_BINARY_DIR@/tests"
-config.test_source_root = config.test_exec_root
diff --git a/lib/lsan/lit_tests/lit.common.cfg b/lib/lsan/lit_tests/lit.common.cfg
deleted file mode 100644
index 96dc1b1f55fc..000000000000
--- a/lib/lsan/lit_tests/lit.common.cfg
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- Python -*-
-
-# Common configuration for running leak detection tests under LSan/ASan.
-
-import os
-
-def get_required_attr(config, attr_name):
- attr_value = getattr(config, attr_name, None)
- if not attr_value:
- lit_config.fatal(
- "No attribute %r in test configuration! You may need to run "
- "tests from your build directory or add this attribute "
- "to lit.site.cfg " % attr_name)
- return attr_value
-
-# Setup source root.
-lsan_lit_src_root = get_required_attr(config, 'lsan_lit_src_root')
-config.test_source_root = os.path.join(lsan_lit_src_root, 'TestCases')
-
-clang_cxxflags = ("--driver-mode=g++ "
- + "-g "
- + "-O0 "
- + "-m64 ")
-
-clang_cflags = ("-g "
- + "-O0 "
- + "-m64 ")
-
-config.clang_cxxflags = clang_cxxflags
-
-config.substitutions.append( ("%clangxx ", (" " + config.clang + " " +
- clang_cxxflags + " ")) )
-
-config.clang_cflags = clang_cflags
-
-config.substitutions.append( ("%clang ", (" " + config.clang + " " +
- clang_cflags + " ")) )
-
-# LeakSanitizer tests are currently supported on x86-64 Linux only.
-if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']:
- config.unsupported = True
-
-config.suffixes = ['.c', '.cc', '.cpp']
diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc
index 058bbdba3907..1598fcac4a76 100644
--- a/lib/lsan/lsan.cc
+++ b/lib/lsan/lsan.cc
@@ -25,14 +25,9 @@ bool lsan_init_is_running;
namespace __lsan {
-static void InitializeCommonFlags() {
- CommonFlags *cf = common_flags();
- SetCommonFlagDefaults();
- cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
- cf->malloc_context_size = 30;
- cf->detect_leaks = true;
-
- ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS"));
+///// Interface to the common LSan module. /////
+bool WordIsPoisoned(uptr addr) {
+ return false;
}
} // namespace __lsan
@@ -45,7 +40,7 @@ extern "C" void __lsan_init() {
return;
lsan_init_is_running = true;
SanitizerToolName = "LeakSanitizer";
- InitializeCommonFlags();
+ InitCommonLsan(true);
InitializeAllocator();
InitTlsSize();
InitializeInterceptors();
@@ -55,17 +50,14 @@ extern "C" void __lsan_init() {
ThreadStart(tid, GetTid());
SetCurrentThread(tid);
- // Start symbolizer process if necessary.
- if (common_flags()->symbolize) {
- Symbolizer::Init(common_flags()->external_symbolizer_path);
- } else {
- Symbolizer::Disable();
- }
-
- InitCommonLsan();
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
Atexit(DoLeakCheck);
lsan_inited = true;
lsan_init_is_running = false;
}
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ GET_STACK_TRACE_FATAL;
+ stack.Print();
+}
diff --git a/lib/lsan/lsan.h b/lib/lsan/lsan.h
index 3e7f76b08193..53783cdc9c7d 100644
--- a/lib/lsan/lsan.h
+++ b/lib/lsan/lsan.h
@@ -15,6 +15,26 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
+#define GET_STACK_TRACE(max_size, fast) \
+ BufferedStackTrace stack; \
+ { \
+ uptr stack_top = 0, stack_bottom = 0; \
+ ThreadContext *t; \
+ if (fast && (t = CurrentThreadContext())) { \
+ stack_top = t->stack_end(); \
+ stack_bottom = t->stack_begin(); \
+ } \
+ stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
+ /* context */ 0, stack_top, stack_bottom, fast); \
+ }
+
+#define GET_STACK_TRACE_FATAL \
+ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
+ common_flags()->fast_unwind_on_malloc)
+
namespace __lsan {
void InitializeInterceptors();
diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc
index f7eee1314bf0..8be2a2ad9224 100644
--- a/lib/lsan/lsan_allocator.cc
+++ b/lib/lsan/lsan_allocator.cc
@@ -15,6 +15,7 @@
#include "lsan_allocator.h"
#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
@@ -53,7 +54,7 @@ void AllocatorThreadFinish() {
allocator.SwallowCache(&cache);
}
-static ChunkMetadata *Metadata(void *p) {
+static ChunkMetadata *Metadata(const void *p) {
return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
}
@@ -62,7 +63,7 @@ static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
ChunkMetadata *m = Metadata(p);
CHECK(m);
m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
- m->stack_trace_id = StackDepotPut(stack.trace, stack.size);
+ m->stack_trace_id = StackDepotPut(stack);
m->requested_size = size;
atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
}
@@ -87,10 +88,12 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
if (cleared && allocator.FromPrimary(p))
memset(p, 0, size);
RegisterAllocation(stack, p, size);
+ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
return p;
}
void Deallocate(void *p) {
+ if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
RegisterDeallocation(p);
allocator.Deallocate(&cache, p);
}
@@ -113,7 +116,7 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end) {
*end = *begin + sizeof(cache);
}
-uptr GetMallocUsableSize(void *p) {
+uptr GetMallocUsableSize(const void *p) {
ChunkMetadata *m = Metadata(p);
if (!m) return 0;
return m->requested_size;
@@ -143,7 +146,11 @@ uptr PointsIntoChunk(void* p) {
if (addr < chunk) return 0;
ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
CHECK(m);
- if (m->allocated && addr < chunk + m->requested_size)
+ if (!m->allocated)
+ return 0;
+ if (addr < chunk + m->requested_size)
+ return chunk;
+ if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr))
return chunk;
return 0;
}
@@ -196,3 +203,38 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) {
}
}
} // namespace __lsan
+
+using namespace __lsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_free_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_unmapped_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return GetMallocUsableSize(p);
+}
+} // extern "C"
diff --git a/lib/lsan/lsan_allocator.h b/lib/lsan/lsan_allocator.h
index 00c55ae02f15..f564601193bd 100644
--- a/lib/lsan/lsan_allocator.h
+++ b/lib/lsan/lsan_allocator.h
@@ -25,7 +25,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
void Deallocate(void *p);
void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
uptr alignment);
-uptr GetMallocUsableSize(void *p);
+uptr GetMallocUsableSize(const void *p);
template<typename Callable>
void ForEachChunk(const Callable &callback);
diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index 152588411e2f..c2ba52e46911 100644
--- a/lib/lsan/lsan_common.cc
+++ b/lib/lsan/lsan_common.cc
@@ -17,6 +17,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_stoptheworld.h"
@@ -26,7 +27,8 @@
#if CAN_SANITIZE_LEAKS
namespace __lsan {
-// This mutex is used to prevent races between DoLeakCheck and IgnoreObject.
+// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
+// also to protect the global list of root regions.
BlockingMutex global_mutex(LINKER_INITIALIZED);
THREADLOCAL int disable_counter;
@@ -34,65 +36,90 @@ bool DisabledInThisThread() { return disable_counter > 0; }
Flags lsan_flags;
-static void InitializeFlags() {
+static void InitializeFlags(bool standalone) {
Flags *f = flags();
// Default values.
f->report_objects = false;
f->resolution = 0;
f->max_leaks = 0;
f->exitcode = 23;
- f->suppressions="";
f->use_registers = true;
f->use_globals = true;
f->use_stacks = true;
f->use_tls = true;
+ f->use_root_regions = true;
f->use_unaligned = false;
- f->verbosity = 0;
+ f->use_poisoned = false;
f->log_pointers = false;
f->log_threads = false;
const char *options = GetEnv("LSAN_OPTIONS");
if (options) {
- ParseFlag(options, &f->use_registers, "use_registers");
- ParseFlag(options, &f->use_globals, "use_globals");
- ParseFlag(options, &f->use_stacks, "use_stacks");
- ParseFlag(options, &f->use_tls, "use_tls");
- ParseFlag(options, &f->use_unaligned, "use_unaligned");
- ParseFlag(options, &f->report_objects, "report_objects");
- ParseFlag(options, &f->resolution, "resolution");
+ ParseFlag(options, &f->use_registers, "use_registers", "");
+ ParseFlag(options, &f->use_globals, "use_globals", "");
+ ParseFlag(options, &f->use_stacks, "use_stacks", "");
+ ParseFlag(options, &f->use_tls, "use_tls", "");
+ ParseFlag(options, &f->use_root_regions, "use_root_regions", "");
+ ParseFlag(options, &f->use_unaligned, "use_unaligned", "");
+ ParseFlag(options, &f->use_poisoned, "use_poisoned", "");
+ ParseFlag(options, &f->report_objects, "report_objects", "");
+ ParseFlag(options, &f->resolution, "resolution", "");
CHECK_GE(&f->resolution, 0);
- ParseFlag(options, &f->max_leaks, "max_leaks");
+ ParseFlag(options, &f->max_leaks, "max_leaks", "");
CHECK_GE(&f->max_leaks, 0);
- ParseFlag(options, &f->verbosity, "verbosity");
- ParseFlag(options, &f->log_pointers, "log_pointers");
- ParseFlag(options, &f->log_threads, "log_threads");
- ParseFlag(options, &f->exitcode, "exitcode");
- ParseFlag(options, &f->suppressions, "suppressions");
+ ParseFlag(options, &f->log_pointers, "log_pointers", "");
+ ParseFlag(options, &f->log_threads, "log_threads", "");
+ ParseFlag(options, &f->exitcode, "exitcode", "");
}
+
+ // Set defaults for common flags (only in standalone mode) and parse
+ // them from LSAN_OPTIONS.
+ CommonFlags *cf = common_flags();
+ if (standalone) {
+ SetCommonFlagsDefaults(cf);
+ cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
+ cf->malloc_context_size = 30;
+ cf->detect_leaks = true;
+ }
+ ParseCommonFlagsFromString(cf, options);
}
-SuppressionContext *suppression_ctx;
+#define LOG_POINTERS(...) \
+ do { \
+ if (flags()->log_pointers) Report(__VA_ARGS__); \
+ } while (0);
+
+#define LOG_THREADS(...) \
+ do { \
+ if (flags()->log_threads) Report(__VA_ARGS__); \
+ } while (0);
+
+static bool suppressions_inited = false;
void InitializeSuppressions() {
- CHECK(!suppression_ctx);
- ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
- suppression_ctx = new(placeholder_) SuppressionContext;
- char *suppressions_from_file;
- uptr buffer_size;
- if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file,
- &buffer_size, 1 << 26 /* max_len */))
- suppression_ctx->Parse(suppressions_from_file);
- if (flags()->suppressions[0] && !buffer_size) {
- Printf("LeakSanitizer: failed to read suppressions file '%s'\n",
- flags()->suppressions);
- Die();
- }
+ CHECK(!suppressions_inited);
+ SuppressionContext::InitIfNecessary();
if (&__lsan_default_suppressions)
- suppression_ctx->Parse(__lsan_default_suppressions());
+ SuppressionContext::Get()->Parse(__lsan_default_suppressions());
+ suppressions_inited = true;
+}
+
+struct RootRegion {
+ const void *begin;
+ uptr size;
+};
+
+InternalMmapVector<RootRegion> *root_regions;
+
+void InitializeRootRegions() {
+ CHECK(!root_regions);
+ ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
+ root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
}
-void InitCommonLsan() {
- InitializeFlags();
+void InitCommonLsan(bool standalone) {
+ InitializeFlags(standalone);
+ InitializeRootRegions();
if (common_flags()->detect_leaks) {
// Initialization which can fail or print warnings should only be done if
// LSan is actually enabled.
@@ -101,9 +128,9 @@ void InitCommonLsan() {
}
}
-class Decorator: private __sanitizer::AnsiColorDecorator {
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
- Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+ Decorator() : SanitizerCommonDecorator() { }
const char *Error() { return Red(); }
const char *Leak() { return Blue(); }
const char *End() { return Default(); }
@@ -132,8 +159,7 @@ void ScanRangeForPointers(uptr begin, uptr end,
Frontier *frontier,
const char *region_type, ChunkTag tag) {
const uptr alignment = flags()->pointer_alignment();
- if (flags()->log_pointers)
- Report("Scanning %s range %p-%p.\n", region_type, begin, end);
+ LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
uptr pp = begin;
if (pp % alignment)
pp = pp + alignment - pp % alignment;
@@ -148,10 +174,19 @@ void ScanRangeForPointers(uptr begin, uptr end,
// Reachable beats ignored beats leaked.
if (m.tag() == kReachable) continue;
if (m.tag() == kIgnored && tag != kReachable) continue;
+
+ // Do this check relatively late so we can log only the interesting cases.
+ if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
+ LOG_POINTERS(
+ "%p is poisoned: ignoring %p pointing into chunk %p-%p of size "
+ "%zu.\n",
+ pp, p, chunk, chunk + m.requested_size(), m.requested_size());
+ continue;
+ }
+
m.set_tag(tag);
- if (flags()->log_pointers)
- Report("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
- chunk, chunk + m.requested_size(), m.requested_size());
+ LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
+ chunk, chunk + m.requested_size(), m.requested_size());
if (frontier)
frontier->push_back(chunk);
}
@@ -170,7 +205,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
uptr registers_end = registers_begin + registers.size();
for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
- if (flags()->log_threads) Report("Processing thread %d.\n", os_id);
+ LOG_THREADS("Processing thread %d.\n", os_id);
uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
&tls_begin, &tls_end,
@@ -178,8 +213,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
if (!thread_found) {
// If a thread can't be found in the thread registry, it's probably in the
// process of destruction. Log this event and move on.
- if (flags()->log_threads)
- Report("Thread %d not found in registry.\n", os_id);
+ LOG_THREADS("Thread %d not found in registry.\n", os_id);
continue;
}
uptr sp;
@@ -196,14 +230,12 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
"REGISTERS", kReachable);
if (flags()->use_stacks) {
- if (flags()->log_threads)
- Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp);
+ LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
if (sp < stack_begin || sp >= stack_end) {
// SP is outside the recorded stack range (e.g. the thread is running a
// signal handler on alternate stack). Again, consider the entire stack
// range to be reachable.
- if (flags()->log_threads)
- Report("WARNING: stack pointer not in stack range.\n");
+ LOG_THREADS("WARNING: stack pointer not in stack range.\n");
} else {
// Shrink the stack range to ignore out-of-scope values.
stack_begin = sp;
@@ -214,7 +246,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
}
if (flags()->use_tls) {
- if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end);
+ LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
if (cache_begin == cache_end) {
ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
} else {
@@ -232,6 +264,37 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
}
}
+static void ProcessRootRegion(Frontier *frontier, uptr root_begin,
+ uptr root_end) {
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ uptr begin, end, prot;
+ while (proc_maps.Next(&begin, &end,
+ /*offset*/ 0, /*filename*/ 0, /*filename_size*/ 0,
+ &prot)) {
+ uptr intersection_begin = Max(root_begin, begin);
+ uptr intersection_end = Min(end, root_end);
+ if (intersection_begin >= intersection_end) continue;
+ bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
+ LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
+ root_begin, root_end, begin, end,
+ is_readable ? "readable" : "unreadable");
+ if (is_readable)
+ ScanRangeForPointers(intersection_begin, intersection_end, frontier,
+ "ROOT", kReachable);
+ }
+}
+
+// Scans root regions for heap pointers.
+static void ProcessRootRegions(Frontier *frontier) {
+ if (!flags()->use_root_regions) return;
+ CHECK(root_regions);
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ RootRegion region = (*root_regions)[i];
+ uptr begin_addr = reinterpret_cast<uptr>(region.begin);
+ ProcessRootRegion(frontier, begin_addr, begin_addr + region.size);
+ }
+}
+
static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
while (frontier->size()) {
uptr next_chunk = frontier->back();
@@ -266,41 +329,37 @@ static void CollectIgnoredCb(uptr chunk, void *arg) {
// Sets the appropriate tag on each chunk.
static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
// Holds the flood fill frontier.
- Frontier frontier(GetPageSizeCached());
+ Frontier frontier(1);
- if (flags()->use_globals)
- ProcessGlobalRegions(&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.
- if (flags()->log_pointers)
- Report("Processing platform-specific allocations.\n");
+ LOG_POINTERS("Processing platform-specific allocations.\n");
ProcessPlatformSpecificAllocations(&frontier);
FloodFillTag(&frontier, kReachable);
- if (flags()->log_pointers)
- Report("Scanning ignored chunks.\n");
+ 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.
- if (flags()->log_pointers)
- Report("Scanning leaked chunks.\n");
+ LOG_POINTERS("Scanning leaked chunks.\n");
ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */);
}
static void PrintStackTraceById(u32 stack_trace_id) {
CHECK(stack_trace_id);
- uptr size = 0;
- const uptr *trace = StackDepotGet(stack_trace_id, &size);
- StackTrace::PrintStack(trace, size);
+ StackDepotGet(stack_trace_id).Print();
}
-// ForEachChunk callback. Aggregates unreachable chunks into a LeakReport.
+// ForEachChunk callback. Aggregates information about unreachable chunks into
+// a LeakReport.
static void CollectLeaksCb(uptr chunk, void *arg) {
CHECK(arg);
LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg);
@@ -309,32 +368,22 @@ static void CollectLeaksCb(uptr chunk, void *arg) {
if (!m.allocated()) return;
if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
uptr resolution = flags()->resolution;
+ u32 stack_trace_id = 0;
if (resolution > 0) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(m.stack_trace_id(), &size);
- size = Min(size, resolution);
- leak_report->Add(StackDepotPut(trace, size), m.requested_size(), m.tag());
+ StackTrace stack = StackDepotGet(m.stack_trace_id());
+ stack.size = Min(stack.size, resolution);
+ stack_trace_id = StackDepotPut(stack);
} else {
- leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag());
+ stack_trace_id = m.stack_trace_id();
}
- }
-}
-
-// ForEachChunkCallback. Prints addresses of unreachable chunks.
-static void PrintLeakedCb(uptr chunk, void *arg) {
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (!m.allocated()) return;
- if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
- Printf("%s leaked %zu byte object at %p.\n",
- m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly",
- m.requested_size(), chunk);
+ leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(),
+ m.tag());
}
}
static void PrintMatchedSuppressions() {
InternalMmapVector<Suppression *> matched(1);
- suppression_ctx->GetMatched(&matched);
+ SuppressionContext::Get()->GetMatched(&matched);
if (!matched.size())
return;
const char *line = "-----------------------------------------------------";
@@ -347,12 +396,6 @@ static void PrintMatchedSuppressions() {
Printf("%s\n\n", line);
}
-static void PrintLeaked() {
- Printf("\n");
- Printf("Reporting individual objects:\n");
- ForEachChunk(PrintLeakedCb, 0 /* arg */);
-}
-
struct DoLeakCheckParam {
bool success;
LeakReport leak_report;
@@ -363,11 +406,8 @@ static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg);
CHECK(param);
CHECK(!param->success);
- CHECK(param->leak_report.IsEmpty());
ClassifyAllChunks(suspended_threads);
ForEachChunk(CollectLeaksCb, &param->leak_report);
- if (!param->leak_report.IsEmpty() && flags()->report_objects)
- PrintLeaked();
param->success = true;
}
@@ -378,7 +418,7 @@ void DoLeakCheck() {
if (already_done) return;
already_done = true;
if (&__lsan_is_turned_off && __lsan_is_turned_off())
- return;
+ return;
DoLeakCheckParam param;
param.success = false;
@@ -392,8 +432,9 @@ void DoLeakCheck() {
Report("LeakSanitizer has encountered a fatal error.\n");
Die();
}
- uptr have_unsuppressed = param.leak_report.ApplySuppressions();
- if (have_unsuppressed) {
+ param.leak_report.ApplySuppressions();
+ uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
+ if (unsuppressed_count > 0) {
Decorator d;
Printf("\n"
"================================================================="
@@ -401,38 +442,49 @@ void DoLeakCheck() {
Printf("%s", d.Error());
Report("ERROR: LeakSanitizer: detected memory leaks\n");
Printf("%s", d.End());
- param.leak_report.PrintLargest(flags()->max_leaks);
+ param.leak_report.ReportTopLeaks(flags()->max_leaks);
}
- if (have_unsuppressed || (flags()->verbosity >= 1)) {
+ if (common_flags()->print_suppressions)
PrintMatchedSuppressions();
+ if (unsuppressed_count > 0) {
param.leak_report.PrintSummary();
+ if (flags()->exitcode) {
+ if (common_flags()->coverage)
+ __sanitizer_cov_dump();
+ internal__exit(flags()->exitcode);
+ }
}
- if (have_unsuppressed && flags()->exitcode)
- internal__exit(flags()->exitcode);
}
static Suppression *GetSuppressionForAddr(uptr addr) {
- static const uptr kMaxAddrFrames = 16;
- InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
- for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo();
- uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode(
- addr, addr_frames.data(), kMaxAddrFrames);
- for (uptr i = 0; i < addr_frames_num; i++) {
- Suppression* s;
- if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) ||
- suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s) ||
- suppression_ctx->Match(addr_frames[i].module, SuppressionLeak, &s))
- return s;
+ Suppression *s = nullptr;
+
+ // Suppress by module name.
+ const char *module_name;
+ uptr module_offset;
+ if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name,
+ &module_offset) &&
+ SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s))
+ return s;
+
+ // Suppress by file or function name.
+ SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ if (SuppressionContext::Get()->Match(cur->info.function, SuppressionLeak,
+ &s) ||
+ SuppressionContext::Get()->Match(cur->info.file, SuppressionLeak, &s)) {
+ break;
+ }
}
- return 0;
+ frames->ClearAll();
+ return s;
}
static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(stack_trace_id, &size);
- for (uptr i = 0; i < size; i++) {
- Suppression *s =
- GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i]));
+ StackTrace stack = StackDepotGet(stack_trace_id);
+ for (uptr i = 0; i < stack.size; i++) {
+ Suppression *s = GetSuppressionForAddr(
+ StackTrace::GetPreviousInstructionPc(stack.trace[i]));
if (s) return s;
}
return 0;
@@ -441,26 +493,35 @@ static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
///// LeakReport implementation. /////
// A hard limit on the number of distinct leaks, to avoid quadratic complexity
-// in LeakReport::Add(). We don't expect to ever see this many leaks in
-// real-world applications.
+// in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks
+// in real-world applications.
// FIXME: Get rid of this limit by changing the implementation of LeakReport to
// use a hash table.
const uptr kMaxLeaksConsidered = 5000;
-void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) {
+void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
+ uptr leaked_size, ChunkTag tag) {
CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
bool is_directly_leaked = (tag == kDirectlyLeaked);
- for (uptr i = 0; i < leaks_.size(); i++)
+ uptr i;
+ for (i = 0; i < leaks_.size(); i++) {
if (leaks_[i].stack_trace_id == stack_trace_id &&
leaks_[i].is_directly_leaked == is_directly_leaked) {
leaks_[i].hit_count++;
leaks_[i].total_size += leaked_size;
- return;
+ break;
}
- if (leaks_.size() == kMaxLeaksConsidered) return;
- Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id,
- is_directly_leaked, /* is_suppressed */ false };
- leaks_.push_back(leak);
+ }
+ if (i == leaks_.size()) {
+ if (leaks_.size() == kMaxLeaksConsidered) return;
+ Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id,
+ is_directly_leaked, /* is_suppressed */ false };
+ leaks_.push_back(leak);
+ }
+ if (flags()->report_objects) {
+ LeakedObject obj = {leaks_[i].id, chunk, leaked_size};
+ leaked_objects_.push_back(obj);
+ }
}
static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
@@ -470,7 +531,7 @@ static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
return leak1.is_directly_leaked;
}
-void LeakReport::PrintLargest(uptr num_leaks_to_print) {
+void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) {
CHECK(leaks_.size() <= kMaxLeaksConsidered);
Printf("\n");
if (leaks_.size() == kMaxLeaksConsidered)
@@ -478,31 +539,49 @@ void LeakReport::PrintLargest(uptr num_leaks_to_print) {
"reported.\n",
kMaxLeaksConsidered);
- uptr unsuppressed_count = 0;
- for (uptr i = 0; i < leaks_.size(); i++)
- if (!leaks_[i].is_suppressed) unsuppressed_count++;
- if (num_leaks_to_print > 0 && num_leaks_to_print < unsuppressed_count)
- Printf("The %zu largest leak(s):\n", num_leaks_to_print);
+ uptr unsuppressed_count = UnsuppressedLeakCount();
+ if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count)
+ Printf("The %zu top leak(s):\n", num_leaks_to_report);
InternalSort(&leaks_, leaks_.size(), LeakComparator);
- uptr leaks_printed = 0;
- Decorator d;
+ uptr leaks_reported = 0;
for (uptr i = 0; i < leaks_.size(); i++) {
if (leaks_[i].is_suppressed) continue;
- Printf("%s", d.Leak());
- Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
- leaks_[i].is_directly_leaked ? "Direct" : "Indirect",
- leaks_[i].total_size, leaks_[i].hit_count);
- Printf("%s", d.End());
- PrintStackTraceById(leaks_[i].stack_trace_id);
- leaks_printed++;
- if (leaks_printed == num_leaks_to_print) break;
+ PrintReportForLeak(i);
+ leaks_reported++;
+ if (leaks_reported == num_leaks_to_report) break;
}
- if (leaks_printed < unsuppressed_count) {
- uptr remaining = unsuppressed_count - leaks_printed;
+ if (leaks_reported < unsuppressed_count) {
+ uptr remaining = unsuppressed_count - leaks_reported;
Printf("Omitting %zu more leak(s).\n", remaining);
}
}
+void LeakReport::PrintReportForLeak(uptr index) {
+ Decorator d;
+ Printf("%s", d.Leak());
+ Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
+ leaks_[index].is_directly_leaked ? "Direct" : "Indirect",
+ leaks_[index].total_size, leaks_[index].hit_count);
+ Printf("%s", d.End());
+
+ PrintStackTraceById(leaks_[index].stack_trace_id);
+
+ if (flags()->report_objects) {
+ Printf("Objects leaked above:\n");
+ PrintLeakedObjectsForLeak(index);
+ Printf("\n");
+ }
+}
+
+void LeakReport::PrintLeakedObjectsForLeak(uptr index) {
+ u32 leak_id = leaks_[index].id;
+ for (uptr j = 0; j < leaked_objects_.size(); j++) {
+ if (leaked_objects_[j].leak_id == leak_id)
+ Printf("%p (%zu bytes)\n", leaked_objects_[j].addr,
+ leaked_objects_[j].size);
+ }
+}
+
void LeakReport::PrintSummary() {
CHECK(leaks_.size() <= kMaxLeaksConsidered);
uptr bytes = 0, allocations = 0;
@@ -511,27 +590,30 @@ void LeakReport::PrintSummary() {
bytes += leaks_[i].total_size;
allocations += leaks_[i].hit_count;
}
- InternalScopedBuffer<char> summary(kMaxSummaryLength);
- internal_snprintf(summary.data(), summary.size(),
- "%zu byte(s) leaked in %zu allocation(s).", bytes,
- allocations);
+ InternalScopedString summary(kMaxSummaryLength);
+ summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes,
+ allocations);
ReportErrorSummary(summary.data());
}
-uptr LeakReport::ApplySuppressions() {
- uptr unsuppressed_count = 0;
+void LeakReport::ApplySuppressions() {
for (uptr i = 0; i < leaks_.size(); i++) {
Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
if (s) {
s->weight += leaks_[i].total_size;
s->hit_count += leaks_[i].hit_count;
leaks_[i].is_suppressed = true;
- } else {
- unsuppressed_count++;
}
}
- return unsuppressed_count;
}
+
+uptr LeakReport::UnsuppressedLeakCount() {
+ uptr result = 0;
+ for (uptr i = 0; i < leaks_.size(); i++)
+ if (!leaks_[i].is_suppressed) result++;
+ return result;
+}
+
} // namespace __lsan
#endif // CAN_SANITIZE_LEAKS
@@ -547,13 +629,51 @@ void __lsan_ignore_object(const void *p) {
// locked.
BlockingMutexLock l(&global_mutex);
IgnoreObjectResult res = IgnoreObjectLocked(p);
- if (res == kIgnoreObjectInvalid && flags()->verbosity >= 2)
- Report("__lsan_ignore_object(): no heap object found at %p", p);
- if (res == kIgnoreObjectAlreadyIgnored && flags()->verbosity >= 2)
- Report("__lsan_ignore_object(): "
+ if (res == kIgnoreObjectInvalid)
+ VReport(1, "__lsan_ignore_object(): no heap object found at %p", p);
+ if (res == kIgnoreObjectAlreadyIgnored)
+ VReport(1, "__lsan_ignore_object(): "
"heap object at %p is already being ignored\n", p);
- if (res == kIgnoreObjectSuccess && flags()->verbosity >= 3)
- Report("__lsan_ignore_object(): ignoring heap object at %p\n", p);
+ if (res == kIgnoreObjectSuccess)
+ VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_register_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+ BlockingMutexLock l(&global_mutex);
+ CHECK(root_regions);
+ RootRegion region = {begin, size};
+ root_regions->push_back(region);
+ VReport(1, "Registered root region at %p of size %llu\n", begin, size);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_unregister_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+ BlockingMutexLock l(&global_mutex);
+ CHECK(root_regions);
+ bool removed = false;
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ RootRegion region = (*root_regions)[i];
+ if (region.begin == begin && region.size == size) {
+ removed = true;
+ uptr last_index = root_regions->size() - 1;
+ (*root_regions)[i] = (*root_regions)[last_index];
+ root_regions->pop_back();
+ VReport(1, "Unregistered root region at %p of size %llu\n", begin, size);
+ break;
+ }
+ }
+ if (!removed) {
+ Report(
+ "__lsan_unregister_root_region(): region at %p of size %llu has not "
+ "been registered.\n",
+ begin, size);
+ Die();
+ }
#endif // CAN_SANITIZE_LEAKS
}
diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h
index d490f8bafd9e..86ff12da6e0f 100644
--- a/lib/lsan/lsan_common.h
+++ b/lib/lsan/lsan_common.h
@@ -21,7 +21,7 @@
#include "sanitizer_common/sanitizer_platform.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
-#if SANITIZER_LINUX && defined(__x86_64__)
+#if SANITIZER_LINUX && defined(__x86_64__) && (SANITIZER_WORDSIZE == 64)
#define CAN_SANITIZE_LEAKS 1
#else
#define CAN_SANITIZE_LEAKS 0
@@ -51,8 +51,6 @@ struct Flags {
int max_leaks;
// If nonzero kill the process with this exit code upon finding leaks.
int exitcode;
- // Suppressions file name.
- const char* suppressions;
// Flags controlling the root set of reachable memory.
// Global variables (.data and .bss).
@@ -63,12 +61,13 @@ struct Flags {
bool use_registers;
// TLS and thread-specific storage.
bool use_tls;
+ // Regions added via __lsan_register_root_region().
+ bool use_root_regions;
// Consider unaligned pointers valid.
bool use_unaligned;
-
- // User-visible verbosity.
- int verbosity;
+ // Consider pointers found in poisoned memory to be valid.
+ bool use_poisoned;
// Debug logging.
bool log_pointers;
@@ -79,6 +78,7 @@ extern Flags lsan_flags;
inline Flags *flags() { return &lsan_flags; }
struct Leak {
+ u32 id;
uptr hit_count;
uptr total_size;
u32 stack_trace_id;
@@ -86,17 +86,31 @@ struct Leak {
bool is_suppressed;
};
+struct LeakedObject {
+ u32 leak_id;
+ uptr addr;
+ uptr size;
+};
+
// Aggregates leaks by stack trace prefix.
class LeakReport {
public:
- LeakReport() : leaks_(1) {}
- void Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag);
- void PrintLargest(uptr max_leaks);
+ LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {}
+ void AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size,
+ ChunkTag tag);
+ void ReportTopLeaks(uptr max_leaks);
void PrintSummary();
- bool IsEmpty() { return leaks_.size() == 0; }
- uptr ApplySuppressions();
+ void ApplySuppressions();
+ uptr UnsuppressedLeakCount();
+
+
private:
+ void PrintReportForLeak(uptr index);
+ void PrintLeakedObjectsForLeak(uptr index);
+
+ u32 next_id_;
InternalMmapVector<Leak> leaks_;
+ InternalMmapVector<LeakedObject> leaked_objects_;
};
typedef InternalMmapVector<uptr> Frontier;
@@ -117,10 +131,19 @@ enum IgnoreObjectResult {
};
// Functions called from the parent tool.
-void InitCommonLsan();
+void InitCommonLsan(bool standalone);
void DoLeakCheck();
bool DisabledInThisThread();
+// Special case for "new T[0]" where T is a type with DTOR.
+// new T[0] will allocate one word for the array size (0) and store a pointer
+// to the end of allocated chunk.
+inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size,
+ uptr addr) {
+ return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr &&
+ *reinterpret_cast<uptr *>(chunk_beg) == 0;
+}
+
// The following must be implemented in the parent tool.
void ForEachChunk(ForEachChunkCallback callback, void *arg);
@@ -129,6 +152,8 @@ void GetAllocatorGlobalRange(uptr *begin, uptr *end);
// Wrappers for allocator's ForceLock()/ForceUnlock().
void LockAllocator();
void UnlockAllocator();
+// Returns true if [addr, addr + sizeof(void *)) is poisoned.
+bool WordIsPoisoned(uptr addr);
// Wrappers for ThreadRegistry access.
void LockThreadRegistry();
void UnlockThreadRegistry();
diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc
index ef8857fe8dbb..ba51868c76e8 100644
--- a/lib/lsan/lsan_common_linux.cc
+++ b/lib/lsan/lsan_common_linux.cc
@@ -19,6 +19,7 @@
#include <link.h>
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
@@ -43,11 +44,11 @@ void InitializePlatformSpecificModules() {
return;
}
if (num_matches == 0)
- Report("LeakSanitizer: Dynamic linker not found. "
- "TLS will not be handled correctly.\n");
+ VReport(1, "LeakSanitizer: Dynamic linker not found. "
+ "TLS will not be handled correctly.\n");
else if (num_matches > 1)
- Report("LeakSanitizer: Multiple modules match \"%s\". "
- "TLS will not be handled correctly.\n", kLinkerName);
+ VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
+ "TLS will not be handled correctly.\n", kLinkerName);
linker = 0;
}
@@ -83,6 +84,7 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
// Scans global variables for heap pointers.
void ProcessGlobalRegions(Frontier *frontier) {
+ if (!flags()->use_globals) return;
// FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of
// deadlocking by running this under StopTheWorld. However, the lock is
// reentrant, so we should be able to fix this by acquiring the lock before
@@ -92,11 +94,10 @@ void ProcessGlobalRegions(Frontier *frontier) {
static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
CHECK(stack_id);
- uptr size = 0;
- const uptr *trace = map->Get(stack_id, &size);
+ StackTrace stack = map->Get(stack_id);
// The top frame is our malloc/calloc/etc. The next frame is the caller.
- if (size >= 2)
- return trace[1];
+ if (stack.size >= 2)
+ return stack.trace[1];
return 0;
}
@@ -129,6 +130,21 @@ static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) {
// Handles dynamically allocated TLS blocks by treating all chunks allocated
// from ld-linux.so as reachable.
+// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
+// They are allocated with a __libc_memalign() call in allocate_and_init()
+// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
+// blocks, but we can make sure they come from our own allocator by intercepting
+// __libc_memalign(). On top of that, there is no easy way to reach them. Their
+// addresses are stored in a dynamically allocated array (the DTV) which is
+// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
+// being reachable from the static TLS, and the dynamic TLS being reachable from
+// the DTV. This is because the initial DTV is allocated before our interception
+// mechanism kicks in, and thus we don't recognize it as allocated memory. We
+// can't special-case it either, since we don't know its size.
+// Our solution is to include in the root set all allocations made from
+// ld-linux.so (which is where allocate_and_init() is implemented). This is
+// guaranteed to include all dynamic TLS blocks (and possibly other allocations
+// which we don't care about).
void ProcessPlatformSpecificAllocations(Frontier *frontier) {
if (!flags()->use_tls) return;
if (!linker) return;
diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index 400230b885ff..ba2519dcb25d 100644
--- a/lib/lsan/lsan_interceptors.cc
+++ b/lib/lsan/lsan_interceptors.cc
@@ -34,21 +34,6 @@ int pthread_key_create(unsigned *key, void (*destructor)(void* v));
int pthread_setspecific(unsigned key, const void *v);
}
-#define GET_STACK_TRACE \
- StackTrace stack; \
- { \
- uptr stack_top = 0, stack_bottom = 0; \
- ThreadContext *t; \
- bool fast = common_flags()->fast_unwind_on_malloc; \
- if (fast && (t = CurrentThreadContext())) { \
- stack_top = t->stack_end(); \
- stack_bottom = t->stack_begin(); \
- } \
- stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \
- StackTrace::GetCurrentPc(), \
- GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \
- }
-
#define ENSURE_LSAN_INITED do { \
CHECK(!lsan_init_is_running); \
if (!lsan_inited) \
@@ -65,7 +50,7 @@ namespace std {
INTERCEPTOR(void*, malloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, 1, kAlwaysClearMemory);
}
@@ -88,26 +73,32 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
}
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
size *= nmemb;
return Allocate(stack, size, 1, true);
}
INTERCEPTOR(void*, realloc, void *q, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Reallocate(stack, q, size, 1);
}
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
+ return Allocate(stack, size, alignment, kAlwaysClearMemory);
+}
+
+INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, alignment, kAlwaysClearMemory);
}
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
// FIXME: Return ENOMEM if user requested more than max alloc size.
return 0;
@@ -115,7 +106,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
INTERCEPTOR(void*, valloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
if (size == 0)
size = GetPageSizeCached();
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
@@ -142,7 +133,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
INTERCEPTOR(void*, pvalloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
uptr PageSize = GetPageSizeCached();
size = RoundUpTo(size, PageSize);
if (size == 0) {
@@ -152,11 +143,11 @@ INTERCEPTOR(void*, pvalloc, uptr size) {
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
}
-INTERCEPTOR(void, cfree, void *p) ALIAS("free");
+INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
#define OPERATOR_NEW_BODY \
ENSURE_LSAN_INITED; \
- GET_STACK_TRACE; \
+ GET_STACK_TRACE_MALLOC; \
return Allocate(stack, size, 1, kAlwaysClearMemory);
INTERCEPTOR_ATTRIBUTE
@@ -173,9 +164,9 @@ void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
Deallocate(ptr);
INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; }
INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; }
INTERCEPTOR_ATTRIBUTE
void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
INTERCEPTOR_ATTRIBUTE
@@ -185,7 +176,8 @@ void operator delete[](void *ptr, std::nothrow_t const &) {
// We need this to intercept the __libc_memalign calls that are used to
// allocate dynamic TLS space in ld-linux.so.
-INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) ALIAS("memalign");
+INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s)
+ ALIAS(WRAPPER_NAME(memalign));
///// Thread initialization and finalization. /////
@@ -223,9 +215,9 @@ extern "C" void *__lsan_thread_start_func(void *arg) {
int tid = 0;
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
internal_sched_yield();
- atomic_store(&p->tid, 0, memory_order_release);
SetCurrentThread(tid);
ThreadStart(tid, GetTid());
+ atomic_store(&p->tid, 0, memory_order_release);
return callback(param);
}
@@ -238,7 +230,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr,
pthread_attr_init(&myattr);
attr = &myattr;
}
- AdjustStackSizeLinux(attr);
+ AdjustStackSize(attr);
int detached = 0;
pthread_attr_getdetachstate(attr, &detached);
ThreadParam p;
diff --git a/lib/lsan/lsan_preinit.cc b/lib/lsan/lsan_preinit.cc
index e6639516dc81..5a190959c15d 100644
--- a/lib/lsan/lsan_preinit.cc
+++ b/lib/lsan/lsan_preinit.cc
@@ -14,11 +14,7 @@
#include "lsan.h"
-#ifndef LSAN_USE_PREINIT_ARRAY
-#define LSAN_USE_PREINIT_ARRAY 1
-#endif
-
-#if LSAN_USE_PREINIT_ARRAY && !defined(PIC)
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
// We force __lsan_init to be called before anyone else by placing it into
// .preinit_array section.
__attribute__((section(".preinit_array"), used))
diff --git a/lib/lsan/tests/CMakeLists.txt b/lib/lsan/tests/CMakeLists.txt
deleted file mode 100644
index 2221e0650237..000000000000
--- a/lib/lsan/tests/CMakeLists.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-include(CheckCXXCompilerFlag)
-include(CompilerRTCompile)
-include(CompilerRTLink)
-
-include_directories(..)
-include_directories(../..)
-
-set(LSAN_TESTS_SRC
- lsan_dummy_unittest.cc)
-
-set(LSAN_TESTS_CFLAGS
- ${SANITIZER_COMMON_CFLAGS}
- ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
- -I${COMPILER_RT_SOURCE_DIR}/lib
- -I${LSAN_SRC_DIR})
-
-set(LSAN_TEST_LINK_FLAGS_COMMON
- -lstdc++ -ldl -lpthread -lm)
-
-add_custom_target(LsanUnitTests)
-set_target_properties(LsanUnitTests PROPERTIES
- FOLDER "LSan unit tests")
-
-# Compile source for the given architecture, using compiler
-# options in ${ARGN}, and add it to the object list.
-macro(lsan_compile obj_list source arch)
- get_filename_component(basename ${source} NAME)
- set(output_obj "${basename}.${arch}.o")
- get_target_flags_for_arch(${arch} TARGET_CFLAGS)
- clang_compile(${output_obj} ${source}
- CFLAGS ${ARGN} ${TARGET_CFLAGS}
- DEPS gtest ${LSAN_RUNTIME_LIBRARIES})
- list(APPEND ${obj_list} ${output_obj})
-endmacro()
-
-function(add_lsan_test test_suite test_name arch)
- get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
- add_compiler_rt_test(${test_suite} ${test_name}
- OBJECTS ${ARGN}
- DEPS ${LSAN_RUNTIME_LIBRARIES} ${ARGN}
- LINK_FLAGS ${LSAN_TEST_LINK_FLAGS_COMMON}
- ${TARGET_LINK_FLAGS})
-endfunction()
-
-macro(add_lsan_tests_for_arch arch)
- set(LSAN_TESTS_OBJ)
- set(LSAN_TEST_SOURCES ${LSAN_TESTS_SRC}
- ${COMPILER_RT_GTEST_SOURCE})
- foreach(source ${LSAN_TEST_SOURCES})
- lsan_compile(LSAN_TESTS_OBJ ${source} ${arch} ${LSAN_TESTS_CFLAGS})
- endforeach()
- add_lsan_test(LsanUnitTests Lsan-${arch}-Test ${arch} ${LSAN_TESTS_OBJ})
-endmacro()
-
-# Build tests for 64-bit Linux only.
-if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64)
- add_lsan_tests_for_arch(x86_64)
-endif()
diff --git a/lib/lsan/tests/lsan_dummy_unittest.cc b/lib/lsan/tests/lsan_dummy_unittest.cc
deleted file mode 100644
index 5468400775a0..000000000000
--- a/lib/lsan/tests/lsan_dummy_unittest.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-//===-- lsan_dummy_unittest.cc --------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer runtime.
-//
-//===----------------------------------------------------------------------===//
-#include "gtest/gtest.h"
-
-TEST(LeakSanitizer, EmptyTest) {
- // Empty test to suppress LIT warnings about lack of tests.
-}
-
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/lib/lsan/tests/lsan_testlib.cc b/lib/lsan/tests/lsan_testlib.cc
deleted file mode 100644
index 8db6cf1e2f8c..000000000000
--- a/lib/lsan/tests/lsan_testlib.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-//===-- lsan_testlib.cc ---------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// Standalone LSan tool as a shared library, to be used with LD_PRELOAD.
-//
-//===----------------------------------------------------------------------===//
-/* Usage:
-clang++ ../sanitizer_common/sanitizer_*.cc ../interception/interception_*.cc \
- lsan*.cc tests/lsan_testlib.cc -I. -I.. -g -ldl -lpthread -fPIC -shared -O2 \
- -DLSAN_USE_PREINIT_ARRAY=0 -o lsan.so
-LD_PRELOAD=./lsan.so /your/app
-*/
-#include "lsan.h"
-
-__attribute__((constructor))
-void constructor() {
- __lsan_init();
-}