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