Hi,
This patch adds initial set of tests and dg infrastructure for
LeakSanitizer runtime.
Tested on x86_64.
Ok to commit?
-Maxim
2014-03-20 Max Ostapenko <m.ostape...@partner.samsung.com>
* c-c++-common/lsan/fork.c: New test.
* c-c++-common/lsan/ignore_object.c: New test.
* c-c++-common/lsan/ignore_object_errors.c: New test.
* c-c++-common/lsan/large_allocation_leak.c: New test.
* c-c++-common/lsan/leak_check_at_exit-1.c: New test.
* c-c++-common/lsan/leak_check_at_exit-2.c: New test.
* c-c++-common/lsan/link_turned_off.c: New test.
* c-c++-common/lsan/pointer_to_self.c: New test.
* c-c++-common/lsan/suppressions_default.c: New test.
* c-c++-common/lsan/swapcontext-1.c: New test.
* c-c++-common/lsan/swapcontext-2.c: New test.
* c-c++-common/lsan/use_after_return.c: New test.
* c-c++-common/lsan/use_globals_initialized.c: New test.
* c-c++-common/lsan/use_globals_uninitialized.c: New test.
* c-c++-common/lsan/use_stacks.c: New test.
* c-c++-common/lsan/use_tls_static.c: New test.
* c-c++-common/lsan/use_unaligned.c: New test.
* g++.dg/lsan/lsan.exp: New file.
* gcc.dg/lsan/lsan.exp: New file.
* lib/lsan-dg.exp: New file.
diff --git a/gcc/testsuite/c-c++-common/lsan/fork.c b/gcc/testsuite/c-c++-common/lsan/fork.c
new file mode 100644
index 0000000..4dc9d4b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/fork.c
@@ -0,0 +1,23 @@
+// Test that thread local data is handled correctly after forking without exec().
+/* { dg-do run } */
+
+#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/gcc/testsuite/c-c++-common/lsan/ignore_object.c b/gcc/testsuite/c-c++-common/lsan/ignore_object.c
new file mode 100644
index 0000000..d73f08b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/ignore_object.c
@@ -0,0 +1,31 @@
+// Test for __lsan_ignore_object().
+
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=3" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "verbosity=3" } */
+/* { dg-shouldfail "lsan" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __lsan_ignore_object(void **p);
+
+int main() {
+ // Explicitly ignored object.
+ void **p = (void **) malloc(sizeof(void **));
+ // Transitively ignored object.
+ *p = malloc(666);
+ // Non-ignored object.
+ volatile void *q = malloc(1337);
+ fprintf(stderr, "Test alloc: %p.\n", p);
+ fprintf(stderr, "Test alloc_2: %p.\n", q);
+ __lsan_ignore_object(p);
+ return 0;
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "ignoring heap object at .*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer: 1337 byte\\(s\\) leaked in 1 allocation\\(s\\).*" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/ignore_object_errors.c b/gcc/testsuite/c-c++-common/lsan/ignore_object_errors.c
new file mode 100644
index 0000000..47a1cd1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/ignore_object_errors.c
@@ -0,0 +1,25 @@
+// Test for incorrect use of __lsan_ignore_object().
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "verbosity=2" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __lsan_ignore_object(const void *p);
+
+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;
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "heap object at .* is already being ignored.*" } */
+/* { dg-output "no heap object found at .*" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/large_allocation_leak.c b/gcc/testsuite/c-c++-common/lsan/large_allocation_leak.c
new file mode 100644
index 0000000..36511d3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/large_allocation_leak.c
@@ -0,0 +1,19 @@
+// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#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;
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 33554432 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/leak_check_at_exit-1.c b/gcc/testsuite/c-c++-common/lsan/leak_check_at_exit-1.c
new file mode 100644
index 0000000..443a2b1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/leak_check_at_exit-1.c
@@ -0,0 +1,24 @@
+// Test for the leak_check_at_exit flag.
+
+/* { dg-do run } */
+/* { dg-options "-fno-builtin" } */
+/* { dg-set-target-env-var LSAN_OPTIONS "verbosity=1:leak_check_at_exit=0" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "leak_check_at_exit=0" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void __lsan_do_leak_check();
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int printf (const char *format, ...);
+
+int main(int argc, char *argv[]) {
+ printf("printf to break optimization\n");
+ __lsan_do_leak_check();
+ return 0;
+}
+
+/* { dg-output "(Leak|Address)Sanitizer: .*" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/leak_check_at_exit-2.c b/gcc/testsuite/c-c++-common/lsan/leak_check_at_exit-2.c
new file mode 100644
index 0000000..8ae955a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/leak_check_at_exit-2.c
@@ -0,0 +1,18 @@
+// Test for the leak_check_at_exit flag.
+
+/* { dg-do run } */
+/* { dg-options "-fno-builtin" } */
+/* { dg-set-target-env-var LSAN_OPTIONS "verbosity=1:leak_check_at_exit=0" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "leak_check_at_exit=0" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int printf (const char *format, ...);
+
+int main(int argc, char *argv[]) {
+ printf("printf to break optimization\n");
+ return 0;
+}
+
+/* { dg-prune-output "(Leak|Address)Sanitizer: .*" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/link_turned_off.c b/gcc/testsuite/c-c++-common/lsan/link_turned_off.c
new file mode 100644
index 0000000..9e7e428
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/link_turned_off.c
@@ -0,0 +1,21 @@
+// Test for disabling LSan at link-time.
+
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "use_stacks=0:use_registers=0" } */
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int __lsan_is_turned_off() {
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ volatile int *x = (volatile int *) malloc(sizeof(int));
+ *x = 42;
+ return 0;
+}
+
+/* { dg-prune-output "SUMMARY: (Leak|Address)Sanitizer: 4 byte\\(s\\) leaked in 1 allocation\\(s\\)" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/pointer_to_self.c b/gcc/testsuite/c-c++-common/lsan/pointer_to_self.c
new file mode 100644
index 0000000..5cfa5a4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/pointer_to_self.c
@@ -0,0 +1,18 @@
+// Regression test: pointers to self should not confuse LSan into thinking the
+// object is indirectly leaked. Only external pointers count.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ void *p = malloc(1337);
+ *((void **) p) = p;
+ fprintf(stderr, "Test alloc: %p.\n", p);
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/suppressions_default.c b/gcc/testsuite/c-c++-common/lsan/suppressions_default.c
new file mode 100644
index 0000000..8d41e75
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/suppressions_default.c
@@ -0,0 +1,29 @@
+// Test for ScopedDisabler.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "use_stacks=0:use_registers=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C"
+#endif
+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;
+}
+/* { dg-output "Suppressions used:.*" } */
+/* { dg-output "1.*666 \\*LSanTestLeakingFunc\\*.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer: 1337 byte\\(s\\) leaked in 1 allocation\\(s\\).*" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/swapcontext-1.c b/gcc/testsuite/c-c++-common/lsan/swapcontext-1.c
new file mode 100644
index 0000000..e16ef15
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/swapcontext-1.c
@@ -0,0 +1,41 @@
+// We can't unwind stack if we're running coroutines on heap-allocated
+// memory. Make sure we don't report these leaks.
+
+/* { dg-do run } */
+/* { dg-shouldfail "lsan" } */
+
+#include <stdio.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+const int kStackSize = 1 << 20;
+
+void Child() {
+ int child_stack;
+ printf("Child: %p\n", &child_stack);
+ volatile int *leaked = (volatile int *) malloc(666 * sizeof(int));
+ leaked[0] = 1;
+ printf("leaked[0] = %d\n", leaked[0]);
+}
+
+int main(int argc, char *argv[]) {
+ char stack_memory[kStackSize + 1];
+ char *child_stack = stack_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;
+ }
+ return 0;
+}
+
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer: 2664 byte\\(s\\) leaked in 1 allocation\\(s\\)" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/swapcontext-2.c b/gcc/testsuite/c-c++-common/lsan/swapcontext-2.c
new file mode 100644
index 0000000..9b9bcd6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/swapcontext-2.c
@@ -0,0 +1,39 @@
+// We can't unwind stack if we're running coroutines on heap-allocated
+// memory. Make sure we don't report these leaks.
+
+/* { dg-do run } */
+
+#include <stdio.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+const int kStackSize = 1 << 20;
+
+void Child() {
+ int child_stack;
+ printf("Child: %p\n", &child_stack);
+ int *leaked = (int *) malloc(666 * sizeof(int));
+}
+
+int main(int argc, char *argv[]) {
+ char *heap_memory = (char *) malloc(kStackSize + 1);
+ char *child_stack = 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;
+ }
+ free(heap_memory);
+ return 0;
+}
+
+/* { dg-prune-output "SUMMARY: (Leak|Address)Sanitizer: 2664 byte\\(s\\) leaked in 1 allocation\\(s\\)" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/use_after_return.c b/gcc/testsuite/c-c++-common/lsan/use_after_return.c
new file mode 100644
index 0000000..9373e48
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/use_after_return.c
@@ -0,0 +1,22 @@
+// Test that fake stack (introduced by ASan's use-after-return mode) is included
+// in the root set.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "detect_stack_use_after_return=1" } */
+/* { dg-shouldfail "lsan" } */
+
+#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);
+}
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 1337 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/use_globals_initialized.c b/gcc/testsuite/c-c++-common/lsan/use_globals_initialized.c
new file mode 100644
index 0000000..f6647c8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/use_globals_initialized.c
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0:use_globals=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#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;
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 1337 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/use_globals_uninitialized.c b/gcc/testsuite/c-c++-common/lsan/use_globals_uninitialized.c
new file mode 100644
index 0000000..538c546
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/use_globals_uninitialized.c
@@ -0,0 +1,19 @@
+// Test that uninitialized globals are included in the root set.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0:use_globals=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#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;
+}
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 1337 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/use_stacks.c b/gcc/testsuite/c-c++-common/lsan/use_stacks.c
new file mode 100644
index 0000000..abf0aee
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/use_stacks.c
@@ -0,0 +1,19 @@
+// Test that stack of main thread is included in the root set.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#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);
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 1337 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/use_tls_static.c b/gcc/testsuite/c-c++-common/lsan/use_tls_static.c
new file mode 100644
index 0000000..dd27883
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/use_tls_static.c
@@ -0,0 +1,20 @@
+// Test that statically allocated TLS space is included in the root set.
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0:use_tls=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#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;
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 1337 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/c-c++-common/lsan/use_unaligned.c b/gcc/testsuite/c-c++-common/lsan/use_unaligned.c
new file mode 100644
index 0000000..1aff6a9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/lsan/use_unaligned.c
@@ -0,0 +1,24 @@
+// Test that unaligned pointers are detected correctly.
+
+/* { dg-do run } */
+/* { dg-set-target-env-var LSAN_OPTIONS "report_objects=1:use_stacks=0:use_registers=0:use_unaligned=0" } */
+/* { dg-shouldfail "lsan" } */
+
+#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;
+}
+
+/* { dg-output "Test alloc: .*" } */
+/* { dg-output "Directly leaked 1337 byte object at .*" } */
+/* { dg-output "LeakSanitizer: detected memory leaks.*" } */
+/* { dg-output "SUMMARY: (Leak|Address)Sanitizer" } */
diff --git a/gcc/testsuite/g++.dg/lsan/lsan.exp b/gcc/testsuite/g++.dg/lsan/lsan.exp
new file mode 100644
index 0000000..f531a44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lsan/lsan.exp
@@ -0,0 +1,38 @@
+# Copyright (C) 2012-2014 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib g++-dg.exp
+load_lib lsan-dg.exp
+
+if ![check_effective_target_fleak_sanitizer] {
+ return
+}
+
+# Initialize `dg'.
+dg-init
+if [lsan_init] {
+
+# Main loop.
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C $srcdir/c-c++-common/lsan/*.c]] ""
+
+}
+
+# All done.
+lsan_finish
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/lsan/lsan.exp b/gcc/testsuite/gcc.dg/lsan/lsan.exp
new file mode 100644
index 0000000..d5f5749
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsan/lsan.exp
@@ -0,0 +1,40 @@
+# Copyright (C) 2012-2014 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib lsan-dg.exp
+
+if ![check_effective_target_fleak_sanitizer] {
+ return
+}
+
+# Initialize `dg'.
+dg-init
+if [lsan_init] {
+
+# Main loop.
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/lsan/*.c]] ""
+
+}
+
+# All done.
+lsan_finish
+dg-finish
diff --git a/gcc/testsuite/lib/lsan-dg.exp b/gcc/testsuite/lib/lsan-dg.exp
new file mode 100644
index 0000000..143b71c
--- /dev/null
+++ b/gcc/testsuite/lib/lsan-dg.exp
@@ -0,0 +1,113 @@
+# Copyright (C) 2012-2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Return 1 if compilation with -fsanitize=leak is error-free for trivial
+# code, 0 otherwise.
+
+proc check_effective_target_fleak_sanitizer {} {
+ return [check_no_compiler_messages fleak_sanitizer object {
+ void foo (void) { }
+ } "-fsanitize=leak"]
+}
+
+#
+# lsan_link_flags -- compute library path and flags to find liblsan.
+# (originally from g++.exp)
+#
+
+proc lsan_link_flags { paths } {
+ global srcdir
+ global ld_library_path
+ global shlib_ext
+
+ set gccpath ${paths}
+ set flags ""
+
+ set shlib_ext [get_shlib_extension]
+
+ if { $gccpath != "" } {
+ if { [file exists "${gccpath}/libsanitizer/lsan/.libs/liblsan.a"]
+ || [file exists "${gccpath}/libsanitizer/lsan/.libs/liblsan.${shlib_ext}"] } {
+ append flags " -B${gccpath}/libsanitizer/ "
+ append flags " -B${gccpath}/libsanitizer/lsan/ "
+ append flags " -L${gccpath}/libsanitizer/lsan/.libs "
+ append ld_library_path ":${gccpath}/libsanitizer/lsan/.libs"
+ }
+ } else {
+ global tool_root_dir
+
+ set liblsan [lookfor_file ${tool_root_dir} liblsan]
+ if { $liblsan != "" } {
+ append flags "-L${liblsan} "
+ append ld_library_path ":${liblsan}"
+ }
+ }
+ set_ld_library_path_env_vars
+ return "$flags"
+}
+
+#
+# lsan_init -- called at the start of each subdir of tests
+#
+
+proc lsan_init { args } {
+ global TEST_ALWAYS_FLAGS
+ global ALWAYS_CXXFLAGS
+ global TOOL_OPTIONS
+ global lsan_saved_TEST_ALWAYS_FLAGS
+ set link_flags ""
+ if ![is_remote host] {
+ if [info exists TOOL_OPTIONS] {
+ set link_flags "[lsan_link_flags [get_multilibs ${TOOL_OPTIONS}]]"
+ } else {
+ set link_flags "[lsan_link_flags [get_multilibs]]"
+ }
+ }
+
+ if [info exists TEST_ALWAYS_FLAGS] {
+ set lsan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
+ }
+ if [info exists ALWAYS_CXXFLAGS] {
+ set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
+ set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=leak -g}" $ALWAYS_CXXFLAGS]
+ } else {
+ if [info exists TEST_ALWAYS_FLAGS] {
+ set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=leak -g $TEST_ALWAYS_FLAGS"
+ } else {
+ set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=leak -g"
+ }
+ }
+ if { $link_flags != "" } {
+ return 1
+ }
+ return 0
+}
+
+#
+# lsan_finish -- called at the start of each subdir of tests
+#
+
+proc lsan_finish { args } {
+ global TEST_ALWAYS_FLAGS
+ global lsan_saved_TEST_ALWAYS_FLAGS
+
+ if [info exists lsan_saved_TEST_ALWAYS_FLAGS] {
+ set TEST_ALWAYS_FLAGS $lsan_saved_TEST_ALWAYS_FLAGS
+ } else {
+ unset TEST_ALWAYS_FLAGS
+ }
+}
+