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
+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