mirror of
https://github.com/ianlancetaylor/libbacktrace.git
synced 2026-09-07 12:17:06 +08:00
libbacktrace: support returning discriminator field
Based on patch from Andi Kleen. * backtrace.h (backtrace_create_state): Rewrite comment to change threaded parameter to flags. (backtrace_full_callback, backtrace_syminfo_callback): Document behavior change if moredata flag is set. (struct backtrace_moredata): Define. * backtrace-supported.h.in (BACKTRACE_SUPPORTS_MOREDATA): Define. * internal.h (struct backtrace_state): Add moredata field. (BACKTRACE_MOREDATA_VERSION): Define. * state.c (backtrace_create_state): Change threaded parameter to flags. Set state moredata field based on flags. * dwarf.c (struct line): Add disc field. (struct function): Add caller_disc field. (call_callback): New static function. (add_line): Add disc parameter. Change all callers. Store disc in new line value. (read_line_program): Track discriminator value. (read_line_info): Initialize disc field in final line entry. (read_function_entry): Handle DW_AT_GNU_discriminator. (report_inlined_functions): Add disc parameter. Change all callers. Call call_callback rather than calling callback directly. (dwarf_lookup_pc): Handle inlined discriminators. Call call_callback rather than calling callback directly. (dwarf_fileline): Call call_callback rather than calling callback directly. * elf.c (elf_syminfo): Pass moredata if requested. * macho.c (macho_syminfo): Likewise. * pecoff.c (coff_syminfo): Likewise. * xcoff.c (xcoff_syminfo): Likewise. (xcoff_lookup_pc): Likewise. * backtrace.c: #include <string.h>. (unwind): Pass moredata if requested. * unknown.c: #include <string.h>. (unknown_fileline): Pass moredata if requested. * fileline.c (backtrace_syminfo_to_full_callback): Add comment about moredata. * mdtest.c: New test file. * Makefile.am (mdtest_SOURCES): Define. (mdtest_CFLAGS, mdtest_LDFLAGS, mdtest_LDADD): Define. (BUILDTESTS): Add mdtest. * Makefile.in: Regenerate.
This commit is contained in:
@@ -346,6 +346,13 @@ endif HAVE_OBJCOPY_DEBUGLINK
|
||||
|
||||
endif HAVE_DWZ
|
||||
|
||||
mdtest_SOURCES = mdtest.c testlib.c
|
||||
mdtest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -O
|
||||
mdtest_LDFLAGS = $(libbacktrace_testing_ldflags)
|
||||
mdtest_LDADD = libbacktrace.la
|
||||
|
||||
BUILDTESTS += mdtest
|
||||
|
||||
stest_SOURCES = stest.c
|
||||
stest_CFLAGS = $(libbacktrace_TEST_CFLAGS)
|
||||
stest_LDFLAGS = $(libbacktrace_testing_ldflags)
|
||||
|
||||
+44
-9
@@ -139,7 +139,7 @@ TESTS = $(am__append_4) $(MAKETESTS) $(am__EXEEXT_17)
|
||||
@HAVE_BUILDID_TRUE@@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_8 = b3test
|
||||
@HAVE_BUILDID_TRUE@@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_9 = b3test_dwz_buildid b3test_dwz_buildidfull
|
||||
@HAVE_ELF_TRUE@@NATIVE_TRUE@am__append_10 = btest_lto
|
||||
@NATIVE_TRUE@am__append_11 = btest_alloc stest stest_alloc
|
||||
@NATIVE_TRUE@am__append_11 = btest_alloc mdtest stest stest_alloc
|
||||
@HAVE_DWZ_TRUE@@NATIVE_TRUE@am__append_12 = btest_dwz
|
||||
@HAVE_DWZ_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_13 = btest_dwz_gnudebuglink
|
||||
@HAVE_ELF_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_14 = -lz
|
||||
@@ -260,8 +260,8 @@ libbacktrace_noformat_la_OBJECTS = \
|
||||
@NATIVE_TRUE@ test_unknown$(EXEEXT) unittest$(EXEEXT) \
|
||||
@NATIVE_TRUE@ unittest_alloc$(EXEEXT) btest$(EXEEXT)
|
||||
@HAVE_ELF_TRUE@@NATIVE_TRUE@am__EXEEXT_6 = btest_lto$(EXEEXT)
|
||||
@NATIVE_TRUE@am__EXEEXT_7 = btest_alloc$(EXEEXT) stest$(EXEEXT) \
|
||||
@NATIVE_TRUE@ stest_alloc$(EXEEXT)
|
||||
@NATIVE_TRUE@am__EXEEXT_7 = btest_alloc$(EXEEXT) mdtest$(EXEEXT) \
|
||||
@NATIVE_TRUE@ stest$(EXEEXT) stest_alloc$(EXEEXT)
|
||||
@HAVE_ELF_TRUE@@NATIVE_TRUE@am__EXEEXT_8 = ztest$(EXEEXT) \
|
||||
@HAVE_ELF_TRUE@@NATIVE_TRUE@ ztest_alloc$(EXEEXT) \
|
||||
@HAVE_ELF_TRUE@@NATIVE_TRUE@ zstdtest$(EXEEXT) \
|
||||
@@ -421,6 +421,13 @@ m2test_OBJECTS = $(am_m2test_OBJECTS)
|
||||
m2test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(m2test_CFLAGS) $(CFLAGS) \
|
||||
$(m2test_LDFLAGS) $(LDFLAGS) -o $@
|
||||
@NATIVE_TRUE@am_mdtest_OBJECTS = mdtest-mdtest.$(OBJEXT) \
|
||||
@NATIVE_TRUE@ mdtest-testlib.$(OBJEXT)
|
||||
mdtest_OBJECTS = $(am_mdtest_OBJECTS)
|
||||
@NATIVE_TRUE@mdtest_DEPENDENCIES = libbacktrace.la
|
||||
mdtest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(mdtest_CFLAGS) $(CFLAGS) \
|
||||
$(mdtest_LDFLAGS) $(LDFLAGS) -o $@
|
||||
@NATIVE_TRUE@am_mtest_OBJECTS = mtest-mtest.$(OBJEXT) \
|
||||
@NATIVE_TRUE@ mtest-testlib.$(OBJEXT)
|
||||
mtest_OBJECTS = $(am_mtest_OBJECTS)
|
||||
@@ -645,12 +652,13 @@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \
|
||||
$(ctestg_alloc_SOURCES) $(ctestzstd_SOURCES) \
|
||||
$(ctestzstd_alloc_SOURCES) $(dwarf5_SOURCES) \
|
||||
$(dwarf5_alloc_SOURCES) $(edtest_SOURCES) \
|
||||
$(edtest_alloc_SOURCES) $(m2test_SOURCES) $(mtest_SOURCES) \
|
||||
$(stest_SOURCES) $(stest_alloc_SOURCES) $(test_elf_32_SOURCES) \
|
||||
$(test_elf_64_SOURCES) $(test_macho_SOURCES) \
|
||||
$(test_pecoff_SOURCES) $(test_unknown_SOURCES) \
|
||||
$(test_xcoff_32_SOURCES) $(test_xcoff_64_SOURCES) \
|
||||
$(ttest_SOURCES) $(ttest_alloc_SOURCES) $(unittest_SOURCES) \
|
||||
$(edtest_alloc_SOURCES) $(m2test_SOURCES) $(mdtest_SOURCES) \
|
||||
$(mtest_SOURCES) $(stest_SOURCES) $(stest_alloc_SOURCES) \
|
||||
$(test_elf_32_SOURCES) $(test_elf_64_SOURCES) \
|
||||
$(test_macho_SOURCES) $(test_pecoff_SOURCES) \
|
||||
$(test_unknown_SOURCES) $(test_xcoff_32_SOURCES) \
|
||||
$(test_xcoff_64_SOURCES) $(ttest_SOURCES) \
|
||||
$(ttest_alloc_SOURCES) $(unittest_SOURCES) \
|
||||
$(unittest_alloc_SOURCES) $(xztest_SOURCES) \
|
||||
$(xztest_alloc_SOURCES) $(zstdtest_SOURCES) \
|
||||
$(zstdtest_alloc_SOURCES) $(ztest_SOURCES) \
|
||||
@@ -1144,6 +1152,10 @@ libbacktrace_testing_ldflags = -no-install
|
||||
@NATIVE_TRUE@btest_alloc_CFLAGS = $(libbacktrace_TEST_CFLAGS)
|
||||
@NATIVE_TRUE@btest_alloc_LDFLAGS = $(libbacktrace_testing_ldflags)
|
||||
@NATIVE_TRUE@btest_alloc_LDADD = libbacktrace_alloc.la
|
||||
@NATIVE_TRUE@mdtest_SOURCES = mdtest.c testlib.c
|
||||
@NATIVE_TRUE@mdtest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -O
|
||||
@NATIVE_TRUE@mdtest_LDFLAGS = $(libbacktrace_testing_ldflags)
|
||||
@NATIVE_TRUE@mdtest_LDADD = libbacktrace.la
|
||||
@NATIVE_TRUE@stest_SOURCES = stest.c
|
||||
@NATIVE_TRUE@stest_CFLAGS = $(libbacktrace_TEST_CFLAGS)
|
||||
@NATIVE_TRUE@stest_LDFLAGS = $(libbacktrace_testing_ldflags)
|
||||
@@ -1445,6 +1457,10 @@ m2test$(EXEEXT): $(m2test_OBJECTS) $(m2test_DEPENDENCIES) $(EXTRA_m2test_DEPENDE
|
||||
@rm -f m2test$(EXEEXT)
|
||||
$(AM_V_CCLD)$(m2test_LINK) $(m2test_OBJECTS) $(m2test_LDADD) $(LIBS)
|
||||
|
||||
mdtest$(EXEEXT): $(mdtest_OBJECTS) $(mdtest_DEPENDENCIES) $(EXTRA_mdtest_DEPENDENCIES)
|
||||
@rm -f mdtest$(EXEEXT)
|
||||
$(AM_V_CCLD)$(mdtest_LINK) $(mdtest_OBJECTS) $(mdtest_LDADD) $(LIBS)
|
||||
|
||||
mtest$(EXEEXT): $(mtest_OBJECTS) $(mtest_DEPENDENCIES) $(EXTRA_mtest_DEPENDENCIES)
|
||||
@rm -f mtest$(EXEEXT)
|
||||
$(AM_V_CCLD)$(mtest_LINK) $(mtest_OBJECTS) $(mtest_LDADD) $(LIBS)
|
||||
@@ -1756,6 +1772,18 @@ m2test-testlib.o: testlib.c
|
||||
m2test-testlib.obj: testlib.c
|
||||
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(m2test_CFLAGS) $(CFLAGS) -c -o m2test-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi`
|
||||
|
||||
mdtest-mdtest.o: mdtest.c
|
||||
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-mdtest.o `test -f 'mdtest.c' || echo '$(srcdir)/'`mdtest.c
|
||||
|
||||
mdtest-mdtest.obj: mdtest.c
|
||||
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-mdtest.obj `if test -f 'mdtest.c'; then $(CYGPATH_W) 'mdtest.c'; else $(CYGPATH_W) '$(srcdir)/mdtest.c'; fi`
|
||||
|
||||
mdtest-testlib.o: testlib.c
|
||||
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c
|
||||
|
||||
mdtest-testlib.obj: testlib.c
|
||||
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi`
|
||||
|
||||
mtest-mtest.o: mtest.c
|
||||
$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mtest_CFLAGS) $(CFLAGS) -c -o mtest-mtest.o `test -f 'mtest.c' || echo '$(srcdir)/'`mtest.c
|
||||
|
||||
@@ -2375,6 +2403,13 @@ btest_alloc.log: btest_alloc$(EXEEXT)
|
||||
--log-file $$b.log --trs-file $$b.trs \
|
||||
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
|
||||
"$$tst" $(AM_TESTS_FD_REDIRECT)
|
||||
mdtest.log: mdtest$(EXEEXT)
|
||||
@p='mdtest$(EXEEXT)'; \
|
||||
b='mdtest'; \
|
||||
$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
|
||||
--log-file $$b.log --trs-file $$b.trs \
|
||||
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
|
||||
"$$tst" $(AM_TESTS_FD_REDIRECT)
|
||||
stest.log: stest$(EXEEXT)
|
||||
@p='stest$(EXEEXT)'; \
|
||||
b='stest'; \
|
||||
|
||||
@@ -35,7 +35,6 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
be #include'd to see whether the backtrace library will be able to
|
||||
get a backtrace and produce symbolic information. */
|
||||
|
||||
|
||||
/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
|
||||
should work, 0 if it will not. Libraries may #include this to make
|
||||
other arrangements. */
|
||||
@@ -60,7 +59,12 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#define BACKTRACE_SUPPORTS_THREADS @BACKTRACE_SUPPORTS_THREADS@
|
||||
|
||||
/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
|
||||
/* BACKTRACE_SUPPORTS_DATA will be #define'd as 1 if the backtrace_syminfo
|
||||
will work for variables. It will always work for functions. */
|
||||
|
||||
#define BACKTRACE_SUPPORTS_DATA @BACKTRACE_SUPPORTS_DATA@
|
||||
|
||||
/* BACKTRACE_SUPPORTS_MOREDATA will be #define'd as 1 if
|
||||
backtrace_create_state supports setting the MOREDATA flag. */
|
||||
|
||||
#define BACKTRACE_SUPPORTS_MOREDATA 1
|
||||
|
||||
+17
-3
@@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "unwind.h"
|
||||
@@ -85,11 +86,24 @@ unwind (struct _Unwind_Context *context, void *vdata)
|
||||
if (!ip_before_insn)
|
||||
--pc;
|
||||
|
||||
if (!bdata->can_alloc)
|
||||
bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
|
||||
else
|
||||
if (bdata->can_alloc)
|
||||
bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
|
||||
bdata->error_callback, bdata->data);
|
||||
else
|
||||
{
|
||||
if (!bdata->state->moredata)
|
||||
bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
|
||||
else
|
||||
{
|
||||
struct backtrace_moredata md;
|
||||
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = BACKTRACE_MOREDATA_VERSION;
|
||||
md.backtrace_data = bdata->data;
|
||||
bdata->ret = bdata->callback (&md, pc, NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (bdata->ret != 0)
|
||||
return _URC_END_OF_STACK;
|
||||
|
||||
|
||||
+77
-21
@@ -68,15 +68,41 @@ typedef void (*backtrace_error_callback) (void *data, const char *msg,
|
||||
|
||||
/* Create state information for the backtrace routines. This must be
|
||||
called before any of the other routines, and its return value must
|
||||
be passed to all of the other routines. FILENAME is the path name
|
||||
of the executable file; if it is NULL the library will try
|
||||
system-specific path names. If not NULL, FILENAME must point to a
|
||||
permanent buffer. If THREADED is non-zero the state may be
|
||||
accessed by multiple threads simultaneously, and the library will
|
||||
use appropriate atomic operations. If THREADED is zero the state
|
||||
may only be accessed by one thread at a time. This returns a state
|
||||
pointer on success, NULL on error. If an error occurs, this will
|
||||
call the ERROR_CALLBACK routine.
|
||||
be passed to all of the other routines.
|
||||
|
||||
FILENAME is the path name of the executable file; if it is NULL the
|
||||
library will try system-specific path names. If not NULL, FILENAME
|
||||
must point to a permanent buffer.
|
||||
|
||||
FLAGS passes flags as bits in an int value:
|
||||
1: THREADED
|
||||
2: MOREDATA
|
||||
|
||||
If (FLAGS & 1) != 0 the THREADED flag is set. If this flag is set,
|
||||
the state may be accessed by multiple threads simultaneously, and the
|
||||
library will use appropriate atomic operations. If THREADED is not
|
||||
set the state may only be accessed by one thread at a time.
|
||||
|
||||
If (FLAGS & 2) != 0 the MOREDATA flag is set. If this flag is set,
|
||||
then backtrace_full_callback and backtrace_symbol_callback will not
|
||||
pass the DATA argument as the user-specified DATA value, but will
|
||||
instead pass it as a pointer to a backtrace_moredata struct. This is
|
||||
a backward compatible approach to getting more data from the various
|
||||
backtrace functions.
|
||||
|
||||
Historical note: in previous versions (before July, 2026) the FLAGS
|
||||
argument was named THREADED, and passing non-zero for THREADED was
|
||||
documented as doing what setting the THREADED flag does today. In
|
||||
practice all callers passed either 0 or 1, so the new semantics of
|
||||
the flag do not affect users of old versions of the library.
|
||||
However, code that passes the MOREDATA flag must ensure that it is
|
||||
using a new version of the library. The backtrace-supported.h file
|
||||
will #define BACKTRACE_SUPPORTS_MOREDATA as 1 for versions of
|
||||
libbacktrace that support the MOREDATA flag.
|
||||
|
||||
The backtrace_create_state function returns a state pointer on
|
||||
success, NULL on error. If an error occurs, it will call the
|
||||
ERROR_CALLBACK routine before returning.
|
||||
|
||||
Calling this function allocates resources that cannot be freed.
|
||||
There is no backtrace_free_state function. The state is used to
|
||||
@@ -89,13 +115,15 @@ extern struct backtrace_state *backtrace_create_state (
|
||||
backtrace_error_callback error_callback, void *data);
|
||||
|
||||
/* The type of the callback argument to the backtrace_full function.
|
||||
DATA is the argument passed to backtrace_full. PC is the program
|
||||
counter. FILENAME is the name of the file containing PC, or NULL
|
||||
if not available. LINENO is the line number in FILENAME containing
|
||||
PC, or 0 if not available. FUNCTION is the name of the function
|
||||
containing PC, or NULL if not available. This should return 0 to
|
||||
continuing tracing. The FILENAME and FUNCTION buffers may become
|
||||
invalid after this function returns. */
|
||||
DATA is either the argument passed to backtrace_full (if the MOREDATA
|
||||
flag was not set when calling backtrace_create_state) or a pointer to
|
||||
a backtrace_moredata struct. PC is the program counter. FILENAME is
|
||||
the name of the file containing PC, or NULL if not available. LINENO
|
||||
is the line number in FILENAME containing PC, or 0 if not available.
|
||||
FUNCTION is the name of the function containing PC, or NULL if not
|
||||
available. This should return 0 to continuing tracing. The FILENAME
|
||||
and FUNCTION buffers may become invalid after this function
|
||||
returns. */
|
||||
|
||||
typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
|
||||
const char *filename, int lineno,
|
||||
@@ -156,11 +184,14 @@ extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* The type of the callback argument to backtrace_syminfo. DATA and
|
||||
PC are the arguments passed to backtrace_syminfo. SYMNAME is the
|
||||
name of the symbol for the corresponding code. SYMVAL is the
|
||||
value and SYMSIZE is the size of the symbol. SYMNAME will be NULL
|
||||
if no error occurred but the symbol could not be found. */
|
||||
/* The type of the callback argument to backtrace_syminfo. DATA is
|
||||
either the argument passed to backtrace_full (if the MOREDATA flag
|
||||
was not set when calling backtrace_create_state) or a pointer to a
|
||||
backtrace_moredata struct. PC is the the argument passed to
|
||||
backtrace_syminfo. SYMNAME is the name of the symbol for the
|
||||
corresponding code. SYMVAL is the value and SYMSIZE is the size of
|
||||
the symbol. SYMNAME will be NULL if no error occurred but the symbol
|
||||
could not be found. */
|
||||
|
||||
typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
|
||||
const char *symname,
|
||||
@@ -182,6 +213,31 @@ extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* The type of the value that the DATA argument passed to
|
||||
backtrace_full_callback or backtrace_syminfo_callback points to if
|
||||
the MOREDATA flag is set in the call to backtrace_create_state.
|
||||
|
||||
The backtrace_moredata value will only be valid for the lifetime of
|
||||
the callback; the callback may copy the data out but must not save
|
||||
the pointer it receives. */
|
||||
|
||||
struct backtrace_moredata
|
||||
{
|
||||
/* The version of this struct. The current expectation is that the
|
||||
version number will be the number of fields in the struct. It's
|
||||
possible that future versions of libbacktrace will add new fields
|
||||
and increment the version number accordingly. There is no plan to
|
||||
remove fields from this struct. Thus the current value of the
|
||||
version field will be 3. */
|
||||
int backtrace_version;
|
||||
/* The DATA value passed to whatever function is calling the callback
|
||||
(backtrace_full, backtrace_pcinfo, or backtrace_syminfo). */
|
||||
void *backtrace_data;
|
||||
/* The DWARF discriminator. This is zero if there is none. See
|
||||
https://wiki.dwarfstd.org/Path_Discriminators.md. */
|
||||
unsigned int backtrace_discriminator;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End extern "C". */
|
||||
#endif
|
||||
|
||||
@@ -568,6 +568,8 @@ struct line
|
||||
const char *filename;
|
||||
/* Line number. */
|
||||
int lineno;
|
||||
/* Discriminator. */
|
||||
int disc;
|
||||
/* Index of the object in the original array read from the DWARF
|
||||
section, before it has been sorted. The index makes it possible
|
||||
to use Quicksort and maintain stability. */
|
||||
@@ -597,6 +599,9 @@ struct function
|
||||
/* If this is an inlined function, the line number of the call
|
||||
site. */
|
||||
int caller_lineno;
|
||||
/* If this is an inlined function, the discriminator of the call
|
||||
site. */
|
||||
unsigned int caller_disc;
|
||||
/* Map PC ranges to inlined functions. */
|
||||
struct function_addrs *function_addrs;
|
||||
size_t function_addrs_count;
|
||||
@@ -1060,6 +1065,28 @@ read_initial_length (struct dwarf_buf *buf, int *is_dwarf64)
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Call the callback function, which differs based on the moredata state
|
||||
flag. */
|
||||
|
||||
static int
|
||||
call_callback (struct backtrace_state *state, backtrace_full_callback callback,
|
||||
void *data, uintptr_t pc, const char *filename, int lineno,
|
||||
const char *function, unsigned int disc)
|
||||
{
|
||||
if (!state->moredata)
|
||||
return callback (data, pc, filename, lineno, function);
|
||||
else
|
||||
{
|
||||
struct backtrace_moredata md;
|
||||
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = BACKTRACE_MOREDATA_VERSION;
|
||||
md.backtrace_data = data;
|
||||
md.backtrace_discriminator = disc;
|
||||
return callback ((void *) &md, pc, filename, lineno, function);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free an abbreviations structure. */
|
||||
|
||||
static void
|
||||
@@ -2818,7 +2845,7 @@ build_address_map (struct backtrace_state *state,
|
||||
|
||||
static int
|
||||
add_line (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
uintptr_t pc, const char *filename, int lineno,
|
||||
uintptr_t pc, const char *filename, int lineno, int disc,
|
||||
backtrace_error_callback error_callback, void *data,
|
||||
struct line_vector *vec)
|
||||
{
|
||||
@@ -2830,7 +2857,13 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
{
|
||||
ln = (struct line *) vec->vec.base + (vec->count - 1);
|
||||
if (pc == ln->pc && filename == ln->filename && lineno == ln->lineno)
|
||||
{
|
||||
/* We only care about the discriminator if moredata is true. */
|
||||
if (!state->moredata)
|
||||
return 1;
|
||||
if (disc == ln->disc)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
ln = ((struct line *)
|
||||
@@ -2845,6 +2878,7 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
|
||||
ln->filename = filename;
|
||||
ln->lineno = lineno;
|
||||
ln->disc = disc;
|
||||
ln->idx = vec->count;
|
||||
|
||||
++vec->count;
|
||||
@@ -3263,6 +3297,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
const char *reset_filename;
|
||||
const char *filename;
|
||||
int lineno;
|
||||
unsigned int disc;
|
||||
|
||||
address = 0;
|
||||
op_index = 0;
|
||||
@@ -3272,6 +3307,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
reset_filename = "";
|
||||
filename = reset_filename;
|
||||
lineno = 1;
|
||||
disc = 0;
|
||||
while (line_buf->left > 0)
|
||||
{
|
||||
unsigned int op;
|
||||
@@ -3288,8 +3324,9 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
/ hdr->max_ops_per_insn);
|
||||
op_index = (op_index + advance) % hdr->max_ops_per_insn;
|
||||
lineno += hdr->line_base + (int) (op % hdr->line_range);
|
||||
add_line (state, ddata, address, filename, lineno,
|
||||
add_line (state, ddata, address, filename, lineno, disc,
|
||||
line_buf->error_callback, line_buf->data, vec);
|
||||
disc = 0;
|
||||
}
|
||||
else if (op == DW_LNS_extended_op)
|
||||
{
|
||||
@@ -3307,6 +3344,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
op_index = 0;
|
||||
filename = reset_filename;
|
||||
lineno = 1;
|
||||
disc = 0;
|
||||
break;
|
||||
case DW_LNE_set_address:
|
||||
address = read_address (line_buf, hdr->addrsize);
|
||||
@@ -3362,8 +3400,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
}
|
||||
break;
|
||||
case DW_LNE_set_discriminator:
|
||||
/* We don't care about discriminators. */
|
||||
read_uleb128 (line_buf);
|
||||
disc = read_uleb128 (line_buf);
|
||||
break;
|
||||
default:
|
||||
if (!advance (line_buf, len - 1))
|
||||
@@ -3376,8 +3413,9 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
switch (op)
|
||||
{
|
||||
case DW_LNS_copy:
|
||||
add_line (state, ddata, address, filename, lineno,
|
||||
add_line (state, ddata, address, filename, lineno, disc,
|
||||
line_buf->error_callback, line_buf->data, vec);
|
||||
disc = 0;
|
||||
break;
|
||||
case DW_LNS_advance_pc:
|
||||
{
|
||||
@@ -3517,6 +3555,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
ln->pc = (uintptr_t) -1;
|
||||
ln->filename = NULL;
|
||||
ln->lineno = 0;
|
||||
ln->disc = 0;
|
||||
ln->idx = 0;
|
||||
|
||||
if (!backtrace_vector_release (state, &vec.vec, error_callback, data))
|
||||
@@ -3846,6 +3885,11 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
}
|
||||
break;
|
||||
|
||||
case DW_AT_GNU_discriminator:
|
||||
if (val.encoding == ATTR_VAL_UINT)
|
||||
function->caller_disc = val.u.uint;
|
||||
break;
|
||||
|
||||
case DW_AT_call_line:
|
||||
if (val.encoding == ATTR_VAL_UINT)
|
||||
function->caller_lineno = val.u.uint;
|
||||
@@ -4086,13 +4130,15 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
}
|
||||
|
||||
/* See if PC is inlined in FUNCTION. If it is, print out the inlined
|
||||
information, and update FILENAME and LINENO for the caller.
|
||||
information, and update FILENAME, LINENO, and DISC for the caller.
|
||||
Returns whatever CALLBACK returns, or 0 to keep going. */
|
||||
|
||||
static int
|
||||
report_inlined_functions (uintptr_t pc, struct function *function,
|
||||
report_inlined_functions (struct backtrace_state *state, uintptr_t pc,
|
||||
struct function *function,
|
||||
backtrace_full_callback callback, void *data,
|
||||
const char **filename, int *lineno)
|
||||
const char **filename, int *lineno,
|
||||
unsigned int *disc)
|
||||
{
|
||||
struct function_addrs *p;
|
||||
struct function_addrs *match;
|
||||
@@ -4144,20 +4190,22 @@ report_inlined_functions (uintptr_t pc, struct function *function,
|
||||
inlined = match->function;
|
||||
|
||||
/* Report any calls inlined into this one. */
|
||||
ret = report_inlined_functions (pc, inlined, callback, data,
|
||||
filename, lineno);
|
||||
ret = report_inlined_functions (state, pc, inlined, callback, data,
|
||||
filename, lineno, disc);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* Report this inlined call. */
|
||||
ret = callback (data, pc, *filename, *lineno, inlined->name);
|
||||
ret = call_callback (state, callback, data, pc, *filename, *lineno,
|
||||
inlined->name, *disc);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* Our caller will report the caller of the inlined function; tell
|
||||
it the appropriate filename and line number. */
|
||||
it the appropriate filename, line number, and discriminator. */
|
||||
*filename = inlined->caller_filename;
|
||||
*lineno = inlined->caller_lineno;
|
||||
*disc = inlined->caller_disc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -4184,6 +4232,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
struct function *function;
|
||||
const char *filename;
|
||||
int lineno;
|
||||
unsigned int disc;
|
||||
int ret;
|
||||
|
||||
*found = 1;
|
||||
@@ -4325,7 +4374,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
if (new_data)
|
||||
return dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
|
||||
data, found);
|
||||
return callback (data, pc, NULL, 0, NULL);
|
||||
return call_callback (state, callback, data, pc, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/* Search for PC within this unit. */
|
||||
@@ -4372,13 +4421,15 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
entry->u->abs_filename = filename;
|
||||
}
|
||||
|
||||
return callback (data, pc, entry->u->abs_filename, 0, NULL);
|
||||
return call_callback (state, callback, data, pc, entry->u->abs_filename,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
/* Search for function name within this unit. */
|
||||
|
||||
if (entry->u->function_addrs_count == 0)
|
||||
return callback (data, pc, ln->filename, ln->lineno, NULL);
|
||||
return call_callback (state, callback, data, pc, ln->filename, ln->lineno,
|
||||
NULL, ln->disc);
|
||||
|
||||
p = ((struct function_addrs *)
|
||||
bsearch (&pc, entry->u->function_addrs,
|
||||
@@ -4386,7 +4437,8 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
sizeof (struct function_addrs),
|
||||
function_addrs_search));
|
||||
if (p == NULL)
|
||||
return callback (data, pc, ln->filename, ln->lineno, NULL);
|
||||
return call_callback (state, callback, data, pc, ln->filename, ln->lineno,
|
||||
NULL, ln->disc);
|
||||
|
||||
/* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
|
||||
sorted by low, so if pc > p->low we are at the end of a range of
|
||||
@@ -4410,19 +4462,22 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
|
||||
--p;
|
||||
}
|
||||
if (fmatch == NULL)
|
||||
return callback (data, pc, ln->filename, ln->lineno, NULL);
|
||||
return call_callback (state, callback, data, pc, ln->filename, ln->lineno,
|
||||
NULL, ln->disc);
|
||||
|
||||
function = fmatch->function;
|
||||
|
||||
filename = ln->filename;
|
||||
lineno = ln->lineno;
|
||||
disc = ln->disc;
|
||||
|
||||
ret = report_inlined_functions (pc, function, callback, data,
|
||||
&filename, &lineno);
|
||||
ret = report_inlined_functions (state, pc, function, callback, data,
|
||||
&filename, &lineno, &disc);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return callback (data, pc, filename, lineno, function->name);
|
||||
return call_callback (state, callback, data, pc, filename, lineno,
|
||||
function->name, disc);
|
||||
}
|
||||
|
||||
|
||||
@@ -4472,7 +4527,7 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
|
||||
|
||||
/* FIXME: See if any libraries have been dlopen'ed. */
|
||||
|
||||
return callback (data, pc, NULL, 0, NULL);
|
||||
return call_callback (state, callback, data, pc, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/* Initialize our data structures from the DWARF debug info for a
|
||||
|
||||
@@ -767,6 +767,8 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
{
|
||||
struct elf_syminfo_data *edata;
|
||||
struct elf_symbol *sym = NULL;
|
||||
void *mdata;
|
||||
struct backtrace_moredata md;
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
@@ -802,10 +804,20 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
}
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (data, addr, NULL, 0, 0);
|
||||
if (!state->moredata)
|
||||
mdata = data;
|
||||
else
|
||||
callback (data, addr, sym->name, sym->address, sym->size);
|
||||
{
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = BACKTRACE_MOREDATA_VERSION;
|
||||
md.backtrace_data = data;
|
||||
mdata = (void *) &md;
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (mdata, addr, NULL, 0, 0);
|
||||
else
|
||||
callback (mdata, addr, sym->name, sym->address, sym->size);
|
||||
}
|
||||
|
||||
/* Return whether FILENAME is a symlink. */
|
||||
|
||||
@@ -425,6 +425,9 @@ backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
|
||||
{
|
||||
struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
|
||||
|
||||
/* If STATE->MOREDATA is set, then data will point to a
|
||||
backtrace_moredata struct, which is what full_callback expects. */
|
||||
|
||||
bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname);
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ struct backtrace_state
|
||||
const char *filename;
|
||||
/* Non-zero if threaded. */
|
||||
int threaded;
|
||||
/* Non-zero if passing additional data. */
|
||||
int moredata;
|
||||
/* The master lock for fileline_fn, fileline_data, syminfo_fn,
|
||||
syminfo_data, fileline_initialization_failed and everything the
|
||||
data pointers point to. */
|
||||
@@ -425,4 +427,7 @@ extern int backtrace_uncompress_lzma (struct backtrace_state *,
|
||||
unsigned char **uncompressed,
|
||||
size_t *uncompressed_size);
|
||||
|
||||
/* Current expected backtrace_moredata version. */
|
||||
#define BACKTRACE_MOREDATA_VERSION (3)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -711,6 +711,8 @@ macho_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
{
|
||||
struct macho_syminfo_data *sdata;
|
||||
struct macho_symbol *sym;
|
||||
void *mdata;
|
||||
struct backtrace_moredata md;
|
||||
|
||||
sym = NULL;
|
||||
if (!state->threaded)
|
||||
@@ -747,10 +749,20 @@ macho_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
}
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (data, addr, NULL, 0, 0);
|
||||
if (!state->moredata)
|
||||
mdata = data;
|
||||
else
|
||||
callback (data, addr, sym->name, sym->address, 0);
|
||||
{
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = BACKTRACE_MOREDATA_VERSION;
|
||||
md.backtrace_data = data;
|
||||
mdata = (void *) &md;
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (mdata, addr, NULL, 0, 0);
|
||||
else
|
||||
callback (mdata, addr, sym->name, sym->address, 0);
|
||||
}
|
||||
|
||||
/* Look through a fat file to find the relevant executable. Returns 1
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
/* mdtest.c -- Test for libbacktrace library with moredata flag
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor.
|
||||
|
||||
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. */
|
||||
|
||||
/* This program tests the externally visible interfaces of the
|
||||
libbacktrace library. */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "filenames.h"
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "backtrace-supported.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
/* backtrace_full_callback with moredata flag. */
|
||||
|
||||
static int
|
||||
md_callback_one (void *vdata, uintptr_t pc, const char *filename, int lineno,
|
||||
const char *function)
|
||||
{
|
||||
struct backtrace_moredata *md = (struct backtrace_moredata *) vdata;
|
||||
struct bdata *data = (struct bdata *) md->backtrace_data;
|
||||
|
||||
if (md->backtrace_version != BACKTRACE_MOREDATA_VERSION)
|
||||
{
|
||||
fprintf (stderr, "md_callback_one: wrong moredata version: got %u, want %u\n", md->backtrace_version, BACKTRACE_MOREDATA_VERSION);
|
||||
data->failed = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return callback_one ((void *) data, pc, filename, lineno, function);
|
||||
}
|
||||
|
||||
/* backtrace_syminfo_callback with moredata flag. */
|
||||
|
||||
static void
|
||||
md_callback_three (void *vdata, uintptr_t pc, const char *symname,
|
||||
uintptr_t symval, uintptr_t symsize)
|
||||
{
|
||||
struct backtrace_moredata *md = (struct backtrace_moredata *) vdata;
|
||||
struct symdata *data = (struct symdata *) md->backtrace_data;
|
||||
|
||||
if (md->backtrace_version != BACKTRACE_MOREDATA_VERSION)
|
||||
{
|
||||
fprintf (stderr, "md_callback_one: wrong moredata version: got %u, want %u\n", md->backtrace_version, BACKTRACE_MOREDATA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
callback_three ((void *) data, pc, symname, symval, symsize);
|
||||
}
|
||||
|
||||
/* Test the backtrace function with non-inlined functions. */
|
||||
|
||||
static int test1 (void) __attribute__ ((noinline, noclone, optnone, unused));
|
||||
static int f2 (int) __attribute__ ((noinline, noclone));
|
||||
static int f3 (int, int) __attribute__ ((noinline, noclone));
|
||||
|
||||
static int
|
||||
test1 (void)
|
||||
{
|
||||
/* Returning a value here and elsewhere avoids a tailcall which
|
||||
would mess up the backtrace. */
|
||||
return f2 (__LINE__) + 1;
|
||||
}
|
||||
|
||||
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, md_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", "mdtest.c", &data.failed);
|
||||
check ("test1", 1, all, f2line, "f2", "mdtest.c", &data.failed);
|
||||
check ("test1", 2, all, f1line, "test1", "mdtest.c", &data.failed);
|
||||
|
||||
printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
|
||||
|
||||
if (data.failed)
|
||||
++failures;
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
/* Test the backtrace function with inlined functions. */
|
||||
|
||||
static inline int test2 (void) __attribute__ ((always_inline, unused));
|
||||
static inline int f12 (int) __attribute__ ((always_inline));
|
||||
static inline int f13 (int, int) __attribute__ ((always_inline));
|
||||
|
||||
static inline int
|
||||
test2 (void)
|
||||
{
|
||||
return f12 (__LINE__) + 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
f12 (int f1line)
|
||||
{
|
||||
return f13 (f1line, __LINE__) + 2;
|
||||
}
|
||||
|
||||
static inline int
|
||||
f13 (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, md_callback_one, error_callback_one, &data);
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
fprintf (stderr, "test2: unexpected return value %d\n", i);
|
||||
data.failed = 1;
|
||||
}
|
||||
|
||||
check ("test2", 0, all, f3line, "f13", "mdtest.c", &data.failed);
|
||||
check ("test2", 1, all, f2line, "f12", "mdtest.c", &data.failed);
|
||||
check ("test2", 2, all, f1line, "test2", "mdtest.c", &data.failed);
|
||||
|
||||
printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
|
||||
|
||||
if (data.failed)
|
||||
++failures;
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
static int test5 (void) __attribute__ ((unused));
|
||||
|
||||
int global = 1;
|
||||
|
||||
static int
|
||||
test5 (void)
|
||||
{
|
||||
struct symdata symdata;
|
||||
int i;
|
||||
uintptr_t addr = (uintptr_t) &global;
|
||||
|
||||
if (sizeof (global) > 1)
|
||||
addr += 1;
|
||||
|
||||
symdata.name = NULL;
|
||||
symdata.val = 0;
|
||||
symdata.size = 0;
|
||||
symdata.failed = 0;
|
||||
|
||||
i = backtrace_syminfo (state, addr, md_callback_three,
|
||||
error_callback_three, &symdata);
|
||||
if (i == 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"test5: unexpected return value from backtrace_syminfo %d\n",
|
||||
i);
|
||||
symdata.failed = 1;
|
||||
}
|
||||
|
||||
if (!symdata.failed)
|
||||
{
|
||||
if (symdata.name == NULL)
|
||||
{
|
||||
fprintf (stderr, "test5: NULL syminfo name\n");
|
||||
symdata.failed = 1;
|
||||
}
|
||||
else if (!(strncmp (symdata.name, "global", 6) == 0
|
||||
&& (symdata.name[6] == '\0'|| symdata.name[6] == '.')))
|
||||
{
|
||||
fprintf (stderr,
|
||||
"test5: unexpected syminfo name got %s expected %s\n",
|
||||
symdata.name, "global");
|
||||
symdata.failed = 1;
|
||||
}
|
||||
else if (symdata.val != (uintptr_t) &global)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"test5: unexpected syminfo value got %lx expected %lx\n",
|
||||
(unsigned long) symdata.val,
|
||||
(unsigned long) (uintptr_t) &global);
|
||||
symdata.failed = 1;
|
||||
}
|
||||
else if (symdata.size != sizeof (global) && symdata.size != 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"test5: unexpected syminfo size got %lx expected %lx\n",
|
||||
(unsigned long) symdata.size,
|
||||
(unsigned long) sizeof (global));
|
||||
symdata.failed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("%s: backtrace_syminfo variable\n",
|
||||
symdata.failed ? "FAIL" : "PASS");
|
||||
|
||||
if (symdata.failed)
|
||||
++failures;
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
/* Run all the tests. */
|
||||
|
||||
int
|
||||
main (int argc ATTRIBUTE_UNUSED, char **argv)
|
||||
{
|
||||
state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS | 2,
|
||||
error_callback_create, NULL);
|
||||
|
||||
#if BACKTRACE_SUPPORTED
|
||||
test1 ();
|
||||
test2 ();
|
||||
#if BACKTRACE_SUPPORTS_DATA
|
||||
test5 ();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
@@ -587,6 +587,8 @@ coff_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
{
|
||||
struct coff_syminfo_data *sdata;
|
||||
struct coff_symbol *sym = NULL;
|
||||
void *mdata;
|
||||
struct backtrace_moredata md;
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
@@ -622,10 +624,20 @@ coff_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
}
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (data, addr, NULL, 0, 0);
|
||||
if (!state->moredata)
|
||||
mdata = data;
|
||||
else
|
||||
callback (data, addr, sym->name, sym->address, 0);
|
||||
{
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = 3;
|
||||
md.backtrace_data = data;
|
||||
mdata = (void *) &md;
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (mdata, addr, NULL, 0, 0);
|
||||
else
|
||||
callback (mdata, addr, sym->name, sym->address, 0);
|
||||
}
|
||||
|
||||
/* Add the backtrace data for one PE/COFF file. Returns 1 on success,
|
||||
|
||||
@@ -43,13 +43,23 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
other routines. */
|
||||
|
||||
struct backtrace_state *
|
||||
backtrace_create_state (const char *filename, int threaded,
|
||||
backtrace_create_state (const char *filename, int flags,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
int threaded;
|
||||
int moredata;
|
||||
struct backtrace_state init_state;
|
||||
struct backtrace_state *state;
|
||||
|
||||
threaded = (flags & 1) != 0;
|
||||
moredata = (flags & 2) != 0;
|
||||
if ((flags & ~3) != 0)
|
||||
{
|
||||
error_callback (data, "backtrace_create_state: unsupported flag", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef HAVE_SYNC_FUNCTIONS
|
||||
if (threaded)
|
||||
{
|
||||
@@ -61,6 +71,7 @@ backtrace_create_state (const char *filename, int threaded,
|
||||
memset (&init_state, 0, sizeof init_state);
|
||||
init_state.filename = filename;
|
||||
init_state.threaded = threaded;
|
||||
init_state.moredata = moredata;
|
||||
|
||||
state = ((struct backtrace_state *)
|
||||
backtrace_alloc (&init_state, sizeof *state, error_callback, data));
|
||||
|
||||
@@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
@@ -40,13 +41,23 @@ POSSIBILITY OF SUCH DAMAGE. */
|
||||
/* A trivial routine that always fails to find fileline data. */
|
||||
|
||||
static int
|
||||
unknown_fileline (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
uintptr_t pc, backtrace_full_callback callback,
|
||||
unknown_fileline (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
|
||||
void *data)
|
||||
|
||||
{
|
||||
if (!state->moredata)
|
||||
return callback (data, pc, NULL, 0, NULL);
|
||||
else
|
||||
{
|
||||
struct backtrace_moredata md;
|
||||
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = 3;
|
||||
md.backtrace_data = data;
|
||||
return callback ((void *) &md, pc, NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the backtrace data when we don't know how to read the
|
||||
|
||||
@@ -512,6 +512,8 @@ xcoff_syminfo (struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr,
|
||||
struct xcoff_syminfo_data *edata;
|
||||
struct xcoff_symbol *sym = NULL;
|
||||
const char *name;
|
||||
void *mdata;
|
||||
struct backtrace_moredata md;
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
@@ -547,15 +549,25 @@ xcoff_syminfo (struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr,
|
||||
}
|
||||
}
|
||||
|
||||
if (!state->moredata)
|
||||
mdata = data;
|
||||
else
|
||||
{
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = 3;
|
||||
md.backtrace_data = data;
|
||||
mdata = (void *) &md;
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (data, addr, NULL, 0, 0);
|
||||
callback (mdata, addr, NULL, 0, 0);
|
||||
else
|
||||
{
|
||||
name = sym->name;
|
||||
/* AIX prepends a '.' to function entry points, remove it. */
|
||||
if (name && *name == '.')
|
||||
++name;
|
||||
callback (data, addr, name, sym->address, sym->size);
|
||||
callback (mdata, addr, name, sym->address, sym->size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,7 +735,7 @@ xcoff_incl_search (const void *vkey, const void *ventry)
|
||||
0 if not. */
|
||||
|
||||
static int
|
||||
xcoff_lookup_pc (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
xcoff_lookup_pc (struct backtrace_state *state,
|
||||
struct xcoff_fileline_data *fdata, uintptr_t pc,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
|
||||
@@ -805,7 +817,18 @@ xcoff_lookup_pc (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
/* AIX prepends a '.' to function entry points, remove it. */
|
||||
if (function != NULL && *function == '.')
|
||||
++function;
|
||||
|
||||
if (!state->moredata)
|
||||
return callback (data, pc, filename, lnno, function);
|
||||
else
|
||||
{
|
||||
struct backtrace_moredata md;
|
||||
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = 3;
|
||||
md.backtrace_data = data;
|
||||
return callback ((void *) &md, pc, filename, lnno, function);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the file/line information for a PC using the XCOFF lineno
|
||||
@@ -855,7 +878,17 @@ xcoff_fileline (struct backtrace_state *state, uintptr_t pc,
|
||||
|
||||
/* FIXME: See if any libraries have been dlopen'ed. */
|
||||
|
||||
if (!state->moredata)
|
||||
return callback (data, pc, NULL, 0, NULL);
|
||||
else
|
||||
{
|
||||
struct backtrace_moredata md;
|
||||
|
||||
memset (&md, 0, sizeof md);
|
||||
md.backtrace_version = 3;
|
||||
md.backtrace_data = data;
|
||||
return callback ((void *) data, pc, NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the function vector info for xcoff_fileline. */
|
||||
|
||||
Reference in New Issue
Block a user