libbacktrace patch committed: Fix race on parallel initialization
Ian Lance Taylor
iant@golang.org
Mon Jun 12 03:25:00 GMT 2017
The code in libbacktrace had a race when doing parallel
initialization: if two threads start to initialize at the same time,
and one completes first, the other, while running in
backtrace_initialize, may see that the structure is initialized and
thus not change *fileline_fn. The caller will then set
state->fileline_fn to *fileline_fn, but since backtrace_initialize has
not changed it that will be NULL. The effect is that if the timing is
right the code can then call a NULL function pointer.
This patch fixes the problem by always initializing *fileline_fn in
backtrace_initialize. It adds a test in ttest.c that does 10
backtraces in parallel with an new state.
To make writing the test easier I copied the test support functions
out of btest.c into testlib.c. While doing that I eliminated some of
the duplication in edtest.c by making that use testlib.c as well.
Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed
to mainline.
Ian
-------------- next part --------------
Index: Makefile.am
===================================================================
--- Makefile.am (revision 249070)
+++ Makefile.am (working copy)
@@ -89,7 +89,7 @@ TESTS = $(check_PROGRAMS)
if NATIVE
-btest_SOURCES = btest.c
+btest_SOURCES = btest.c testlib.c
btest_CFLAGS = $(AM_CFLAGS) -g -O
btest_LDADD = libbacktrace.la
@@ -100,7 +100,7 @@ stest_LDADD = libbacktrace.la
check_PROGRAMS += stest
-edtest_SOURCES = edtest.c edtest2_build.c
+edtest_SOURCES = edtest.c edtest2_build.c testlib.c
edtest_LDADD = libbacktrace.la
check_PROGRAMS += edtest
@@ -111,6 +111,16 @@ gen_edtest2_build: $(srcdir)/edtest2.c
$(SHELL) $(srcdir)/../move-if-change tmp-edtest2_build.c edtest2_build.c
echo timestamp > $@
+if HAVE_PTHREAD
+
+check_PROGRAMS += ttest
+
+ttest_SOURCES = ttest.c testlib.c
+ttest_CFLAGS = -pthread
+ttest_LDADD = libbacktrace.la
+
+endif HAVE_PTHREAD
+
endif NATIVE
# We can't use automake's automatic dependency tracking, because it
Index: btest.c
===================================================================
--- btest.c (revision 249070)
+++ btest.c (working copy)
@@ -43,237 +43,7 @@ POSSIBILITY OF SUCH DAMAGE. */
#include "backtrace.h"
#include "backtrace-supported.h"
-/* Portable attribute syntax. Actually some of these tests probably
- won't work if the attributes are not recognized. */
-
-#ifndef GCC_VERSION
-# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
-#endif
-
-#if (GCC_VERSION < 2007)
-# define __attribute__(x)
-#endif
-
-#ifndef ATTRIBUTE_UNUSED
-# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
-#endif
-
-/* Used to collect backtrace info. */
-
-struct info
-{
- char *filename;
- int lineno;
- char *function;
-};
-
-/* Passed to backtrace callback function. */
-
-struct bdata
-{
- struct info *all;
- size_t index;
- size_t max;
- int failed;
-};
-
-/* Passed to backtrace_simple callback function. */
-
-struct sdata
-{
- uintptr_t *addrs;
- size_t index;
- size_t max;
- int failed;
-};
-
-/* Passed to backtrace_syminfo callback function. */
-
-struct symdata
-{
- const char *name;
- uintptr_t val, size;
- int failed;
-};
-
-/* The backtrace state. */
-
-static void *state;
-
-/* The number of failures. */
-
-static int failures;
-
-/* Return the base name in a path. */
-
-static const char *
-base (const char *p)
-{
- const char *last;
- const char *s;
-
- last = NULL;
- for (s = p; *s != '\0'; ++s)
- {
- if (IS_DIR_SEPARATOR (*s))
- last = s + 1;
- }
- return last != NULL ? last : p;
-}
-
-/* Check an entry in a struct info array. */
-
-static void
-check (const char *name, int index, const struct info *all, int want_lineno,
- const char *want_function, int *failed)
-{
- if (*failed)
- return;
- if (all[index].filename == NULL || all[index].function == NULL)
- {
- fprintf (stderr, "%s: [%d]: missing file name or function name\n",
- name, index);
- *failed = 1;
- return;
- }
- if (strcmp (base (all[index].filename), "btest.c") != 0)
- {
- fprintf (stderr, "%s: [%d]: got %s expected test.c\n", name, index,
- all[index].filename);
- *failed = 1;
- }
- if (all[index].lineno != want_lineno)
- {
- fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index,
- all[index].lineno, want_lineno);
- *failed = 1;
- }
- if (strcmp (all[index].function, want_function) != 0)
- {
- fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
- all[index].function, want_function);
- *failed = 1;
- }
-}
-
-/* The backtrace callback function. */
-
-static int
-callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
- const char *filename, int lineno, const char *function)
-{
- struct bdata *data = (struct bdata *) vdata;
- struct info *p;
-
- if (data->index >= data->max)
- {
- fprintf (stderr, "callback_one: callback called too many times\n");
- data->failed = 1;
- return 1;
- }
-
- p = &data->all[data->index];
- if (filename == NULL)
- p->filename = NULL;
- else
- {
- p->filename = strdup (filename);
- assert (p->filename != NULL);
- }
- p->lineno = lineno;
- if (function == NULL)
- p->function = NULL;
- else
- {
- p->function = strdup (function);
- assert (p->function != NULL);
- }
- ++data->index;
-
- return 0;
-}
-
-/* An error callback passed to backtrace. */
-
-static void
-error_callback_one (void *vdata, const char *msg, int errnum)
-{
- struct bdata *data = (struct bdata *) vdata;
-
- fprintf (stderr, "%s", msg);
- if (errnum > 0)
- fprintf (stderr, ": %s", strerror (errnum));
- fprintf (stderr, "\n");
- data->failed = 1;
-}
-
-/* The backtrace_simple callback function. */
-
-static int
-callback_two (void *vdata, uintptr_t pc)
-{
- struct sdata *data = (struct sdata *) vdata;
-
- if (data->index >= data->max)
- {
- fprintf (stderr, "callback_two: callback called too many times\n");
- data->failed = 1;
- return 1;
- }
-
- data->addrs[data->index] = pc;
- ++data->index;
-
- return 0;
-}
-
-/* An error callback passed to backtrace_simple. */
-
-static void
-error_callback_two (void *vdata, const char *msg, int errnum)
-{
- struct sdata *data = (struct sdata *) vdata;
-
- fprintf (stderr, "%s", msg);
- if (errnum > 0)
- fprintf (stderr, ": %s", strerror (errnum));
- fprintf (stderr, "\n");
- data->failed = 1;
-}
-
-/* The backtrace_syminfo callback function. */
-
-static void
-callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
- const char *symname, uintptr_t symval,
- uintptr_t symsize)
-{
- struct symdata *data = (struct symdata *) vdata;
-
- if (symname == NULL)
- data->name = NULL;
- else
- {
- data->name = strdup (symname);
- assert (data->name != NULL);
- }
- data->val = symval;
- data->size = symsize;
-}
-
-/* The backtrace_syminfo error callback function. */
-
-static void
-error_callback_three (void *vdata, const char *msg, int errnum)
-{
- struct symdata *data = (struct symdata *) vdata;
-
- fprintf (stderr, "%s", msg);
- if (errnum > 0)
- fprintf (stderr, ": %s", strerror (errnum));
- fprintf (stderr, "\n");
- data->failed = 1;
-}
+#include "testlib.h"
/* Test the backtrace function with non-inlined functions. */
@@ -325,9 +95,9 @@ f3 (int f1line, int f2line)
data.failed = 1;
}
- check ("test1", 0, all, f3line, "f3", &data.failed);
- check ("test1", 1, all, f2line, "f2", &data.failed);
- check ("test1", 2, all, f1line, "test1", &data.failed);
+ check ("test1", 0, all, f3line, "f3", "btest.c", &data.failed);
+ check ("test1", 1, all, f2line, "f2", "btest.c", &data.failed);
+ check ("test1", 2, all, f1line, "test1", "btest.c", &data.failed);
printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
@@ -377,9 +147,9 @@ f13 (int f1line, int f2line)
data.failed = 1;
}
- check ("test2", 0, all, f3line, "f13", &data.failed);
- check ("test2", 1, all, f2line, "f12", &data.failed);
- check ("test2", 2, all, f1line, "test2", &data.failed);
+ check ("test2", 0, all, f3line, "f13", "btest.c", &data.failed);
+ check ("test2", 1, all, f2line, "f12", "btest.c", &data.failed);
+ check ("test2", 2, all, f1line, "test2", "btest.c", &data.failed);
printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
@@ -462,9 +232,9 @@ f23 (int f1line, int f2line)
}
}
- check ("test3", 0, all, f3line, "f23", &bdata.failed);
- check ("test3", 1, all, f2line, "f22", &bdata.failed);
- check ("test3", 2, all, f1line, "test3", &bdata.failed);
+ check ("test3", 0, all, f3line, "f23", "btest.c", &bdata.failed);
+ check ("test3", 1, all, f2line, "f22", "btest.c", &bdata.failed);
+ check ("test3", 2, all, f1line, "test3", "btest.c", &bdata.failed);
if (bdata.failed)
data.failed = 1;
@@ -600,9 +370,9 @@ f33 (int f1line, int f2line)
bdata.failed = 1;
}
- check ("test4", 0, all, f3line, "f33", &bdata.failed);
- check ("test4", 1, all, f2line, "f32", &bdata.failed);
- check ("test4", 2, all, f1line, "test4", &bdata.failed);
+ check ("test4", 0, all, f3line, "f33", "btest.c", &bdata.failed);
+ check ("test4", 1, all, f2line, "f32", "btest.c", &bdata.failed);
+ check ("test4", 2, all, f1line, "test4", "btest.c", &bdata.failed);
if (bdata.failed)
data.failed = 1;
@@ -686,17 +456,6 @@ test5 (void)
return failures;
}
-static void
-error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg,
- int errnum)
-{
- fprintf (stderr, "%s", msg);
- if (errnum > 0)
- fprintf (stderr, ": %s", strerror (errnum));
- fprintf (stderr, "\n");
- exit (EXIT_FAILURE);
-}
-
/* Run all the tests. */
int
Index: configure.ac
===================================================================
--- configure.ac (revision 249070)
+++ configure.ac (working copy)
@@ -355,6 +355,23 @@ if test "$have_getexecname" = "yes"; the
AC_DEFINE(HAVE_GETEXECNAME, 1, [Define if getexecname is available.])
fi
+dnl Test whether the compiler supports the -pthread option.
+AC_CACHE_CHECK([whether -pthread is supported],
+[libgo_cv_lib_pthread],
+[CFLAGS_hold=$CFLAGS
+CFLAGS="$CFLAGS -pthread"
+AC_COMPILE_IFELSE([[int i;]],
+[libgo_cv_lib_pthread=yes],
+[libgo_cv_lib_pthread=no])
+CFLAGS=$CFLAGS_hold])
+PTHREAD_CFLAGS=
+if test "$libgo_cv_lib_pthread" = yes; then
+ PTHREAD_CFLAGS=-pthread
+fi
+AC_SUBST(PTHREAD_CFLAGS)
+
+AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes)
+
AC_CACHE_CHECK([whether tests can run],
[libbacktrace_cv_sys_native],
[AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])],
Index: edtest.c
===================================================================
--- edtest.c (revision 249070)
+++ edtest.c (working copy)
@@ -41,19 +41,7 @@ POSSIBILITY OF SUCH DAMAGE. */
#include "backtrace-supported.h"
#include "internal.h"
-#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__)
-# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
-#else
-# define IS_DIR_SEPARATOR(c) ((c) == '/')
-#endif
-
-/* The backtrace state. */
-
-static void *state;
-
-/* The number of failures. */
-
-int failures = 0;
+#include "testlib.h"
static int test1 (void) __attribute__ ((noinline, unused));
static int test1 (void) __attribute__ ((noinline, unused));
@@ -68,128 +56,6 @@ test1 (void)
return f2 (__LINE__) + 1;
}
-/* Used to collect backtrace info. */
-
-struct info
-{
- char *filename;
- int lineno;
- char *function;
-};
-
-/* Return the base name in a path. */
-
-static const char *
-base (const char *p)
-{
- const char *last;
- const char *s;
-
- last = NULL;
- for (s = p; *s != '\0'; ++s)
- {
- if (IS_DIR_SEPARATOR (*s))
- last = s + 1;
- }
- return last != NULL ? last : p;
-}
-
-/* Check an entry in a struct info array. */
-
-static void
-check (const char *name, int index, const struct info *all, int want_lineno,
- const char *want_function, const char *want_file, int *failed)
-{
- if (*failed)
- return;
- if (all[index].filename == NULL || all[index].function == NULL)
- {
- fprintf (stderr, "%s: [%d]: missing file name or function name\n",
- name, index);
- *failed = 1;
- return;
- }
- if (strcmp (base (all[index].filename), want_file) != 0)
- {
- fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
- all[index].filename, want_file);
- *failed = 1;
- }
- if (all[index].lineno != want_lineno)
- {
- fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index,
- all[index].lineno, want_lineno);
- *failed = 1;
- }
- if (strcmp (all[index].function, want_function) != 0)
- {
- fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
- all[index].function, want_function);
- *failed = 1;
- }
-}
-
-/* Passed to backtrace callback function. */
-
-struct bdata
-{
- struct info *all;
- size_t index;
- size_t max;
- int failed;
-};
-
-/* An error callback passed to backtrace. */
-
-static void
-error_callback_one (void *vdata, const char *msg, int errnum)
-{
- struct bdata *data = (struct bdata *) vdata;
-
- fprintf (stderr, "%s", msg);
- if (errnum > 0)
- fprintf (stderr, ": %s", strerror (errnum));
- fprintf (stderr, "\n");
- data->failed = 1;
-}
-
-/* The backtrace callback function. */
-
-static int
-callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
- const char *filename, int lineno, const char *function)
-{
- struct bdata *data = (struct bdata *) vdata;
- struct info *p;
-
- if (data->index >= data->max)
- {
- fprintf (stderr, "callback_one: callback called too many times\n");
- data->failed = 1;
- return 1;
- }
-
- p = &data->all[data->index];
- if (filename == NULL)
- p->filename = NULL;
- else
- {
- p->filename = strdup (filename);
- assert (p->filename != NULL);
- }
- p->lineno = lineno;
- if (function == NULL)
- p->function = NULL;
- else
- {
- p->function = strdup (function);
- assert (p->function != NULL);
- }
- ++data->index;
-
- return 0;
-}
-
int
f3 (int f1line, int f2line)
{
@@ -232,17 +98,6 @@ f3 (int f1line, int f2line)
return failures;
}
-static void
-error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg,
- int errnum)
-{
- fprintf (stderr, "%s", msg);
- if (errnum > 0)
- fprintf (stderr, ": %s", strerror (errnum));
- fprintf (stderr, "\n");
- exit (EXIT_FAILURE);
-}
-
int
main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
Index: elf.c
===================================================================
--- elf.c (revision 249070)
+++ elf.c (working copy)
@@ -962,18 +962,12 @@ backtrace_initialize (struct backtrace_s
}
if (!state->threaded)
- {
- if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug)
- *fileline_fn = elf_fileline_fn;
- }
+ *fileline_fn = state->fileline_fn;
else
- {
- fileline current_fn;
+ *fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
- current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
- if (current_fn == NULL || current_fn == elf_nodebug)
- *fileline_fn = elf_fileline_fn;
- }
+ if (*fileline_fn == NULL || *fileline_fn == elf_nodebug)
+ *fileline_fn = elf_fileline_fn;
return 1;
}
Index: testlib.c
===================================================================
--- testlib.c (revision 0)
+++ testlib.c (working copy)
@@ -0,0 +1,234 @@
+/* testlib.h -- test functions for libbacktrace library
+ Copyright (C) 2012-2017 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "filenames.h"
+
+#include "backtrace.h"
+
+#include "testlib.h"
+
+/* The backtrace state. */
+
+void *state;
+
+/* The number of failures. */
+
+int failures;
+
+/* Return the base name in a path. */
+
+const char *
+base (const char *p)
+{
+ const char *last;
+ const char *s;
+
+ last = NULL;
+ for (s = p; *s != '\0'; ++s)
+ {
+ if (IS_DIR_SEPARATOR (*s))
+ last = s + 1;
+ }
+ return last != NULL ? last : p;
+}
+
+/* Check an entry in a struct info array. */
+
+void
+check (const char *name, int index, const struct info *all, int want_lineno,
+ const char *want_function, const char *want_file, int *failed)
+{
+ if (*failed)
+ return;
+ if (all[index].filename == NULL || all[index].function == NULL)
+ {
+ fprintf (stderr, "%s: [%d]: missing file name or function name\n",
+ name, index);
+ *failed = 1;
+ return;
+ }
+ if (strcmp (base (all[index].filename), want_file) != 0)
+ {
+ fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
+ all[index].filename, want_file);
+ *failed = 1;
+ }
+ if (all[index].lineno != want_lineno)
+ {
+ fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index,
+ all[index].lineno, want_lineno);
+ *failed = 1;
+ }
+ if (strcmp (all[index].function, want_function) != 0)
+ {
+ fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
+ all[index].function, want_function);
+ *failed = 1;
+ }
+}
+
+/* The backtrace callback function. */
+
+int
+callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
+ const char *filename, int lineno, const char *function)
+{
+ struct bdata *data = (struct bdata *) vdata;
+ struct info *p;
+
+ if (data->index >= data->max)
+ {
+ fprintf (stderr, "callback_one: callback called too many times\n");
+ data->failed = 1;
+ return 1;
+ }
+
+ p = &data->all[data->index];
+ if (filename == NULL)
+ p->filename = NULL;
+ else
+ {
+ p->filename = strdup (filename);
+ assert (p->filename != NULL);
+ }
+ p->lineno = lineno;
+ if (function == NULL)
+ p->function = NULL;
+ else
+ {
+ p->function = strdup (function);
+ assert (p->function != NULL);
+ }
+ ++data->index;
+
+ return 0;
+}
+
+/* An error callback passed to backtrace. */
+
+void
+error_callback_one (void *vdata, const char *msg, int errnum)
+{
+ struct bdata *data = (struct bdata *) vdata;
+
+ fprintf (stderr, "%s", msg);
+ if (errnum > 0)
+ fprintf (stderr, ": %s", strerror (errnum));
+ fprintf (stderr, "\n");
+ data->failed = 1;
+}
+
+/* The backtrace_simple callback function. */
+
+int
+callback_two (void *vdata, uintptr_t pc)
+{
+ struct sdata *data = (struct sdata *) vdata;
+
+ if (data->index >= data->max)
+ {
+ fprintf (stderr, "callback_two: callback called too many times\n");
+ data->failed = 1;
+ return 1;
+ }
+
+ data->addrs[data->index] = pc;
+ ++data->index;
+
+ return 0;
+}
+
+/* An error callback passed to backtrace_simple. */
+
+void
+error_callback_two (void *vdata, const char *msg, int errnum)
+{
+ struct sdata *data = (struct sdata *) vdata;
+
+ fprintf (stderr, "%s", msg);
+ if (errnum > 0)
+ fprintf (stderr, ": %s", strerror (errnum));
+ fprintf (stderr, "\n");
+ data->failed = 1;
+}
+
+/* The backtrace_syminfo callback function. */
+
+void
+callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
+ const char *symname, uintptr_t symval,
+ uintptr_t symsize)
+{
+ struct symdata *data = (struct symdata *) vdata;
+
+ if (symname == NULL)
+ data->name = NULL;
+ else
+ {
+ data->name = strdup (symname);
+ assert (data->name != NULL);
+ }
+ data->val = symval;
+ data->size = symsize;
+}
+
+/* The backtrace_syminfo error callback function. */
+
+void
+error_callback_three (void *vdata, const char *msg, int errnum)
+{
+ struct symdata *data = (struct symdata *) vdata;
+
+ fprintf (stderr, "%s", msg);
+ if (errnum > 0)
+ fprintf (stderr, ": %s", strerror (errnum));
+ fprintf (stderr, "\n");
+ data->failed = 1;
+}
+
+/* The backtrace_create_state error callback function. */
+
+void
+error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg,
+ int errnum)
+{
+ fprintf (stderr, "%s", msg);
+ if (errnum > 0)
+ fprintf (stderr, ": %s", strerror (errnum));
+ fprintf (stderr, "\n");
+ exit (EXIT_FAILURE);
+}
Index: testlib.h
===================================================================
--- testlib.h (revision 0)
+++ testlib.h (working copy)
@@ -0,0 +1,110 @@
+/* testlib.h -- Header for test functions for libbacktrace library
+ Copyright (C) 2012-2017 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef LIBBACKTRACE_TESTLIB_H
+#define LIBBACKTRACE_TESTLIB_H
+
+/* Portable attribute syntax. Actually some of these tests probably
+ won't work if the attributes are not recognized. */
+
+#ifndef GCC_VERSION
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif
+
+#if (GCC_VERSION < 2007)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+/* Used to collect backtrace info. */
+
+struct info
+{
+ char *filename;
+ int lineno;
+ char *function;
+};
+
+/* Passed to backtrace callback function. */
+
+struct bdata
+{
+ struct info *all;
+ size_t index;
+ size_t max;
+ int failed;
+};
+
+/* Passed to backtrace_simple callback function. */
+
+struct sdata
+{
+ uintptr_t *addrs;
+ size_t index;
+ size_t max;
+ int failed;
+};
+
+/* Passed to backtrace_syminfo callback function. */
+
+struct symdata
+{
+ const char *name;
+ uintptr_t val, size;
+ int failed;
+};
+
+/* The backtrace state. */
+
+extern void *state;
+
+/* The number of failures. */
+
+extern int failures;
+
+extern const char *base (const char *p);
+extern void check (const char *name, int index, const struct info *all,
+ int want_lineno, const char *want_function,
+ const char *want_file, int *failed);
+extern int callback_one (void *, uintptr_t, const char *, int, const char *);
+extern void error_callback_one (void *, const char *, int);
+extern int callback_two (void *, uintptr_t);
+extern void error_callback_two (void *, const char *, int);
+extern void callback_three (void *, uintptr_t, const char *, uintptr_t,
+ uintptr_t);
+extern void error_callback_three (void *, const char *, int);
+extern void error_callback_create (void *, const char *, int);
+
+#endif /* !defined(LIBBACKTRACE_TESTLIB_H) */
Index: ttest.c
===================================================================
--- ttest.c (revision 0)
+++ ttest.c (working copy)
@@ -0,0 +1,161 @@
+/* ttest.c -- Test for libbacktrace library
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+/* Test using the libbacktrace library from multiple threads. */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pthread.h>
+
+#include "filenames.h"
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+
+#include "testlib.h"
+
+static int f2 (int) __attribute__ ((noinline));
+static int f3 (int, int) __attribute__ ((noinline));
+
+/* Test that a simple backtrace works. This is called via
+ pthread_create. It returns the number of failures, as void *. */
+
+static void *
+test1_thread (void *arg ATTRIBUTE_UNUSED)
+{
+ /* Returning a value here and elsewhere avoids a tailcall which
+ would mess up the backtrace. */
+ return (void *) (uintptr_t) (f2 (__LINE__) - 2);
+}
+
+static int
+f2 (int f1line)
+{
+ return f3 (f1line, __LINE__) + 2;
+}
+
+static int
+f3 (int f1line, int f2line)
+{
+ struct info all[20];
+ struct bdata data;
+ int f3line;
+ int i;
+
+ data.all = &all[0];
+ data.index = 0;
+ data.max = 20;
+ data.failed = 0;
+
+ f3line = __LINE__ + 1;
+ i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
+
+ if (i != 0)
+ {
+ fprintf (stderr, "test1: unexpected return value %d\n", i);
+ data.failed = 1;
+ }
+
+ if (data.index < 3)
+ {
+ fprintf (stderr,
+ "test1: not enough frames; got %zu, expected at least 3\n",
+ data.index);
+ data.failed = 1;
+ }
+
+ check ("test1", 0, all, f3line, "f3", "ttest.c", &data.failed);
+ check ("test1", 1, all, f2line, "f2", "ttest.c", &data.failed);
+ check ("test1", 2, all, f1line, "test1_thread", "ttest.c", &data.failed);
+
+ return data.failed;
+}
+
+/* Run the test with 10 threads simultaneously. */
+
+#define THREAD_COUNT 10
+
+static void test1 (void) __attribute__ ((unused));
+
+static void
+test1 (void)
+{
+ pthread_t atid[THREAD_COUNT];
+ int i;
+ int errnum;
+ int this_fail;
+ void *ret;
+
+ for (i = 0; i < THREAD_COUNT; i++)
+ {
+ errnum = pthread_create (&atid[i], NULL, test1_thread, NULL);
+ if (errnum != 0)
+ {
+ fprintf (stderr, "pthread_create %d: %s\n", i, strerror (errnum));
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ this_fail = 0;
+ for (i = 0; i < THREAD_COUNT; i++)
+ {
+ errnum = pthread_join (atid[i], &ret);
+ if (errnum != 0)
+ {
+ fprintf (stderr, "pthread_join %d: %s\n", i, strerror (errnum));
+ exit (EXIT_FAILURE);
+ }
+ this_fail += (int) (uintptr_t) ret;
+ }
+
+ printf ("%s: threaded backtrace_full noinline\n", this_fail > 0 ? "FAIL" : "PASS");
+
+ failures += this_fail;
+}
+
+int
+main (int argc ATTRIBUTE_UNUSED, char **argv)
+{
+ state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
+ error_callback_create, NULL);
+
+#if BACKTRACE_SUPPORTED
+#if BACKTRACE_SUPPORTS_THREADS
+ test1 ();
+#endif
+#endif
+
+ exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
+}
More information about the Gcc-patches
mailing list