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:
Ian Lance Taylor
2026-07-06 12:42:21 -07:00
parent 5f4a6d4941
commit 6f8310e238
15 changed files with 635 additions and 75 deletions
+7
View File
@@ -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
View File
@@ -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'; \
+6 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+77 -22
View File
@@ -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)
return 1;
{
/* 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
+15 -3
View File
@@ -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. */
+3
View File
@@ -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);
}
+5
View File
@@ -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
+15 -3
View File
@@ -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
+290
View File
@@ -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);
}
+15 -3
View File
@@ -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,
+12 -1
View File
@@ -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));
+14 -3
View File
@@ -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)
{
return callback (data, pc, NULL, 0, NULL);
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
+38 -5
View File
@@ -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;
return callback (data, pc, filename, lnno, 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. */
return callback (data, pc, NULL, 0, NULL);
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. */