mirror of
https://github.com/ianlancetaylor/libbacktrace.git
synced 2026-09-07 12:17:06 +08:00
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:
+4
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user