libbacktrace: report decl_lineno with MOREDATA

Based on patch by Andi Kleen.

	* backtrace.h (struct backtrace_moredata): Add
	backtrace_decl_lineno field.
	* internal.h (BACKTRACE_MOREDATA_VERSION): Change to 4.
	* dwarf.c (struct function): Add decl_lineno field.
	(call_callback): Add decl_lineno parameter. Change all callers.
	(read_referenced_name_from_attr): Likewise.
	(read_referenced_name): Likewise.
	(report_inlined_functions): Likewise.
	(read_function_entry): Set decl_lineno field.
	(dwarf_lookup_pc): Handle decl_lineno across inlines.
	* mdtest.c (struct mdinfo): Define.
	(struct mddata): Define.
	(md_callback_one): Adjust for mddata.
	(md_callback_three): Likewise.
	(test1, f2): Reformat for simpler line number testing.
	(f3): Test that decl_lineno is correct.
	(f13): Adjust for mddata.
This commit is contained in:
Ian Lance Taylor
2026-08-31 12:42:36 -07:00
parent 6f8310e238
commit 6d913c4521
4 changed files with 142 additions and 62 deletions
+4 -1
View File
@@ -228,7 +228,7 @@ struct backtrace_moredata
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. */
version field will be 4. */
int backtrace_version;
/* The DATA value passed to whatever function is calling the callback
(backtrace_full, backtrace_pcinfo, or backtrace_syminfo). */
@@ -236,6 +236,9 @@ struct backtrace_moredata
/* The DWARF discriminator. This is zero if there is none. See
https://wiki.dwarfstd.org/Path_Discriminators.md. */
unsigned int backtrace_discriminator;
/* The line number of the start of the function declaration or
definition. This is 0 if not available. */
int backtrace_decl_lineno;
};
#ifdef __cplusplus
+49 -24
View File
@@ -602,6 +602,8 @@ struct function
/* If this is an inlined function, the discriminator of the call
site. */
unsigned int caller_disc;
/* The line number of the declaration. */
int decl_lineno;
/* Map PC ranges to inlined functions. */
struct function_addrs *function_addrs;
size_t function_addrs_count;
@@ -1071,7 +1073,7 @@ read_initial_length (struct dwarf_buf *buf, int *is_dwarf64)
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)
const char *function, unsigned int disc, int decl_lineno)
{
if (!state->moredata)
return callback (data, pc, filename, lineno, function);
@@ -1083,6 +1085,7 @@ call_callback (struct backtrace_state *state, backtrace_full_callback callback,
md.backtrace_version = BACKTRACE_MOREDATA_VERSION;
md.backtrace_data = data;
md.backtrace_discriminator = disc;
md.backtrace_decl_lineno = decl_lineno;
return callback ((void *) &md, pc, filename, lineno, function);
}
}
@@ -3579,15 +3582,17 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
static const char *read_referenced_name (struct dwarf_data *, struct unit *,
uint64_t, backtrace_error_callback,
void *);
void *, int *);
/* Read the name of a function from a DIE referenced by ATTR with VAL. */
/* Read the name of a function from a DIE referenced by ATTR with VAL.
This sets *DECL_LINENO to the decl_line attribute of the DIE if
available. */
static const char *
read_referenced_name_from_attr (struct dwarf_data *ddata, struct unit *u,
struct attr *attr, struct attr_val *val,
backtrace_error_callback error_callback,
void *data)
void *data, int *decl_lineno)
{
switch (attr->name)
{
@@ -3610,12 +3615,14 @@ read_referenced_name_from_attr (struct dwarf_data *ddata, struct unit *u,
return NULL;
uint64_t offset = val->u.uint - unit->low_offset;
return read_referenced_name (ddata, unit, offset, error_callback, data);
return read_referenced_name (ddata, unit, offset, error_callback, data,
decl_lineno);
}
if (val->encoding == ATTR_VAL_UINT
|| val->encoding == ATTR_VAL_REF_UNIT)
return read_referenced_name (ddata, u, val->u.uint, error_callback, data);
return read_referenced_name (ddata, u, val->u.uint, error_callback, data,
decl_lineno);
if (val->encoding == ATTR_VAL_REF_ALT_INFO)
{
@@ -3627,7 +3634,7 @@ read_referenced_name_from_attr (struct dwarf_data *ddata, struct unit *u,
uint64_t offset = val->u.uint - alt_unit->low_offset;
return read_referenced_name (ddata->altlink, alt_unit, offset,
error_callback, data);
error_callback, data, decl_lineno);
}
return NULL;
@@ -3635,12 +3642,13 @@ read_referenced_name_from_attr (struct dwarf_data *ddata, struct unit *u,
/* Read the name of a function from a DIE referenced by a
DW_AT_abstract_origin or DW_AT_specification tag. OFFSET is within
the same compilation unit. */
the same compilation unit. This sets *DECL_LINENO to the function's
decl_line attribute if available. */
static const char *
read_referenced_name (struct dwarf_data *ddata, struct unit *u,
uint64_t offset, backtrace_error_callback error_callback,
void *data)
void *data, int *decl_lineno)
{
struct dwarf_buf unit_buf;
uint64_t code;
@@ -3732,12 +3740,18 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u,
const char *name;
name = read_referenced_name_from_attr (ddata, u, &abbrev->attrs[i],
&val, error_callback, data);
&val, error_callback, data,
decl_lineno);
if (name != NULL)
ret = name;
}
break;
case DW_AT_decl_line:
if (val.encoding == ATTR_VAL_UINT)
*decl_lineno = (int) val.u.uint;
break;
default:
break;
}
@@ -3890,6 +3904,11 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
function->caller_disc = val.u.uint;
break;
case DW_AT_decl_line:
if (val.encoding == ATTR_VAL_UINT)
function->decl_lineno = (int) val.u.uint;
break;
case DW_AT_call_line:
if (val.encoding == ATTR_VAL_UINT)
function->caller_lineno = val.u.uint;
@@ -3907,7 +3926,8 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
name
= read_referenced_name_from_attr (ddata, u,
&abbrev->attrs[i], &val,
error_callback, data);
error_callback, data,
&function->decl_lineno);
if (name != NULL)
function->name = name;
}
@@ -4130,15 +4150,17 @@ 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, LINENO, and DISC for the caller.
Returns whatever CALLBACK returns, or 0 to keep going. */
information, and update FILENAME, LINENO, DISC, and DECL_LINENO for
the caller. Returns whatever CALLBACK returns, or 0 to keep
going. */
static int
report_inlined_functions (struct backtrace_state *state, uintptr_t pc,
struct function *function,
backtrace_full_callback callback, void *data,
const char **filename, int *lineno,
unsigned int *disc)
unsigned int *disc,
int *decl_lineno)
{
struct function_addrs *p;
struct function_addrs *match;
@@ -4191,13 +4213,13 @@ report_inlined_functions (struct backtrace_state *state, uintptr_t pc,
/* Report any calls inlined into this one. */
ret = report_inlined_functions (state, pc, inlined, callback, data,
filename, lineno, disc);
filename, lineno, disc, decl_lineno);
if (ret != 0)
return ret;
/* Report this inlined call. */
ret = call_callback (state, callback, data, pc, *filename, *lineno,
inlined->name, *disc);
inlined->name, *disc, *decl_lineno);
if (ret != 0)
return ret;
@@ -4206,6 +4228,7 @@ report_inlined_functions (struct backtrace_state *state, uintptr_t pc,
*filename = inlined->caller_filename;
*lineno = inlined->caller_lineno;
*disc = inlined->caller_disc;
*decl_lineno = inlined->decl_lineno;
return 0;
}
@@ -4233,6 +4256,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
const char *filename;
int lineno;
unsigned int disc;
int decl_lineno;
int ret;
*found = 1;
@@ -4374,7 +4398,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 call_callback (state, callback, data, pc, NULL, 0, NULL, 0);
return call_callback (state, callback, data, pc, NULL, 0, NULL, 0, 0);
}
/* Search for PC within this unit. */
@@ -4422,14 +4446,14 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
}
return call_callback (state, callback, data, pc, entry->u->abs_filename,
0, NULL, 0);
0, NULL, 0, 0);
}
/* Search for function name within this unit. */
if (entry->u->function_addrs_count == 0)
return call_callback (state, callback, data, pc, ln->filename, ln->lineno,
NULL, ln->disc);
NULL, ln->disc, 0);
p = ((struct function_addrs *)
bsearch (&pc, entry->u->function_addrs,
@@ -4438,7 +4462,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
function_addrs_search));
if (p == NULL)
return call_callback (state, callback, data, pc, ln->filename, ln->lineno,
NULL, ln->disc);
NULL, ln->disc, 0);
/* 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
@@ -4463,21 +4487,22 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
}
if (fmatch == NULL)
return call_callback (state, callback, data, pc, ln->filename, ln->lineno,
NULL, ln->disc);
NULL, ln->disc, 0);
function = fmatch->function;
filename = ln->filename;
lineno = ln->lineno;
disc = ln->disc;
decl_lineno = function->decl_lineno;
ret = report_inlined_functions (state, pc, function, callback, data,
&filename, &lineno, &disc);
&filename, &lineno, &disc, &decl_lineno);
if (ret != 0)
return ret;
return call_callback (state, callback, data, pc, filename, lineno,
function->name, disc);
function->name, disc, decl_lineno);
}
@@ -4527,7 +4552,7 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
/* FIXME: See if any libraries have been dlopen'ed. */
return call_callback (state, callback, data, pc, NULL, 0, NULL, 0);
return call_callback (state, callback, data, pc, NULL, 0, NULL, 0, 0);
}
/* Initialize our data structures from the DWARF debug info for a
+1 -1
View File
@@ -428,6 +428,6 @@ extern int backtrace_uncompress_lzma (struct backtrace_state *,
size_t *uncompressed_size);
/* Current expected backtrace_moredata version. */
#define BACKTRACE_MOREDATA_VERSION (3)
#define BACKTRACE_MOREDATA_VERSION (4)
#endif
+88 -36
View File
@@ -48,6 +48,22 @@ POSSIBILITY OF SUCH DAMAGE. */
#include "testlib.h"
/* Used to collect moredata info from backtraces. */
struct mdinfo
{
int decl_lineno;
};
/* We pass a pointer to this as the data field to the backtrace
functions. */
struct mddata
{
struct bdata bdata;
struct mdinfo *mdall;
};
/* backtrace_full_callback with moredata flag. */
static int
@@ -55,15 +71,21 @@ 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;
struct mddata *mddata = (struct mddata *) md->backtrace_data;
struct bdata *data = &mddata->bdata;
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);
fprintf (stderr,
"md_callback_one: wrong moredata version: got %u, want %u\n",
md->backtrace_version, BACKTRACE_MOREDATA_VERSION);
data->failed = 1;
return 1;
}
if (data->index < data->max)
mddata->mdall[data->index].decl_lineno = md->backtrace_decl_lineno;
return callback_one ((void *) data, pc, filename, lineno, function);
}
@@ -78,7 +100,19 @@ md_callback_three (void *vdata, uintptr_t pc, const char *symname,
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);
fprintf (stderr,
"md_callback_three: wrong moredata version: got %u, want %u\n",
md->backtrace_version, BACKTRACE_MOREDATA_VERSION);
data->failed = 1;
return;
}
if (md->backtrace_decl_lineno != 0)
{
fprintf (stderr,
"md_callback_three: unexpected non-zero decl_lineno %d\n",
md->backtrace_decl_lineno);
data->failed = 1;
return;
}
@@ -91,17 +125,11 @@ 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. */
static int test1 (void) {
return f2 (__LINE__) + 1;
}
static int
f2 (int f1line)
{
static int f2 (int f1line) {
return f3 (f1line, __LINE__) + 2;
}
@@ -109,14 +137,16 @@ static int
f3 (int f1line, int f2line)
{
struct info all[20];
struct bdata data;
struct mdinfo mdall[20];
struct mddata data;
int f3line;
int i;
data.all = &all[0];
data.index = 0;
data.max = 20;
data.failed = 0;
data.bdata.all = &all[0];
data.bdata.index = 0;
data.bdata.max = 20;
data.bdata.failed = 0;
data.mdall = &mdall[0];
f3line = __LINE__ + 1;
i = backtrace_full (state, 0, md_callback_one, error_callback_one, &data);
@@ -124,24 +154,44 @@ f3 (int f1line, int f2line)
if (i != 0)
{
fprintf (stderr, "test1: unexpected return value %d\n", i);
data.failed = 1;
data.bdata.failed = 1;
}
if (data.index < 3)
if (data.bdata.index < 3)
{
fprintf (stderr,
"test1: not enough frames; got %zu, expected at least 3\n",
data.index);
data.failed = 1;
data.bdata.index);
data.bdata.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);
check ("test1", 0, all, f3line, "f3", "mdtest.c", &data.bdata.failed);
check ("test1", 1, all, f2line, "f2", "mdtest.c", &data.bdata.failed);
check ("test1", 2, all, f1line, "test1", "mdtest.c", &data.bdata.failed);
printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
if (!data.bdata.failed)
{
/* This assumes a particular pattern for the definitions of f1 and
f2, above. */
if (data.mdall[1].decl_lineno != f2line - 1)
{
fprintf (stderr,
"test1: incorrect f2 decl_lineno; got %d, expected %d\n",
data.mdall[1].decl_lineno, f2line - 1);
data.bdata.failed = 1;
}
if (data.mdall[2].decl_lineno != f1line - 1)
{
fprintf (stderr,
"test1: incorrect f1 decl_lineno; got %d, expected %d\n",
data.mdall[2].decl_lineno, f1line - 1);
data.bdata.failed = 1;
}
}
if (data.failed)
printf ("%s: backtrace_full noinline\n", data.bdata.failed ? "FAIL" : "PASS");
if (data.bdata.failed)
++failures;
return failures;
@@ -169,14 +219,16 @@ static inline int
f13 (int f1line, int f2line)
{
struct info all[20];
struct bdata data;
struct mdinfo mdall[20];
struct mddata data;
int f3line;
int i;
data.all = &all[0];
data.index = 0;
data.max = 20;
data.failed = 0;
data.bdata.all = &all[0];
data.bdata.index = 0;
data.bdata.max = 20;
data.bdata.failed = 0;
data.mdall = &mdall[0];
f3line = __LINE__ + 1;
i = backtrace_full (state, 0, md_callback_one, error_callback_one, &data);
@@ -184,16 +236,16 @@ f13 (int f1line, int f2line)
if (i != 0)
{
fprintf (stderr, "test2: unexpected return value %d\n", i);
data.failed = 1;
data.bdata.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);
check ("test2", 0, all, f3line, "f13", "mdtest.c", &data.bdata.failed);
check ("test2", 1, all, f2line, "f12", "mdtest.c", &data.bdata.failed);
check ("test2", 2, all, f1line, "test2", "mdtest.c", &data.bdata.failed);
printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
printf ("%s: backtrace_full inline\n", data.bdata.failed ? "FAIL" : "PASS");
if (data.failed)
if (data.bdata.failed)
++failures;
return failures;