Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide source code info for Symbol #180

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions _drgn.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,14 @@ class Symbol:
kind: SymbolKind
"""Kind of entity represented by this symbol."""

def source(self, long) -> Tuple[str, int, int]:
"""
Get the source code location of this function at the given offset.

:return: Location as a ``(filename, line, column)`` triple.
:raises LookupError: if the source code location is not available
"""

class SymbolBinding(enum.Enum):
"""
A ``SymbolBinding`` describes the linkage behavior and visibility of a
Expand Down
19 changes: 19 additions & 0 deletions libdrgn/debug_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,25 @@ static int drgn_dwfl_module_removed(Dwfl_Module *dwfl_module, void *userdatap,
return DWARF_CB_OK;
}

struct drgn_debug_info_module *
drgn_debug_info_module_byaddress(struct drgn_debug_info *dbinfo, uint64_t addr)
{

for (struct drgn_debug_info_module_table_iterator it =
drgn_debug_info_module_table_first(&dbinfo->modules); it.entry; ) {
struct drgn_debug_info_module *module = *it.entry;
do {
struct drgn_debug_info_module *next = module->next;
if (!next)
it = drgn_debug_info_module_table_next(it);
if (addr >= module->start && addr <= module->end)
return module;
module = next;
} while (module);
}
return NULL;
}
Comment on lines +306 to +323
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding this, you can look up the Dwfl_Module * for an address with dwfl_addrmodule(). If you need the struct drgn_debug_info_module *, too, you can get it with dwfl_module_info(). Here's an example:

if (prog->dbinfo) {
Dwfl_Module *dwfl_module = dwfl_addrmodule(prog->dbinfo->dwfl,
pc - !regs->interrupted);
if (dwfl_module) {
void **userdatap;
dwfl_module_info(dwfl_module, &userdatap, NULL, NULL,
NULL, NULL, NULL, NULL);
struct drgn_debug_info_module *module = *userdatap;


static void drgn_debug_info_free_modules(struct drgn_debug_info *dbinfo,
bool finish_indexing, bool free_all)
{
Expand Down
4 changes: 4 additions & 0 deletions libdrgn/debug_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ drgn_debug_info_buffer_init(struct drgn_debug_info_buffer *buffer,
buffer->scn = scn;
}

struct drgn_debug_info_module *
drgn_debug_info_module_byaddress(struct drgn_debug_info *dbinfo, uint64_t addr);


DEFINE_HASH_TABLE_TYPE(drgn_debug_info_module_table,
struct drgn_debug_info_module *)

Expand Down
9 changes: 9 additions & 0 deletions libdrgn/drgn.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -2675,6 +2675,15 @@ void drgn_symbol_destroy(struct drgn_symbol *sym);
*/
void drgn_symbols_destroy(struct drgn_symbol **syms, size_t count);

/** Get the source code file, line, and column of a @ref drgn_symbol */
struct drgn_error *drgn_symbol_source(struct drgn_symbol *sym,
struct drgn_program *prog,
unsigned long offset,
size_t size,
char *src_file_ret,
int *line_num_ret,
int *line_col_ret);

/**
* Get the name of a @ref drgn_symbol.
*
Expand Down
28 changes: 28 additions & 0 deletions libdrgn/python/symbol.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ static PyObject *Symbol_richcompare(Symbol *self, PyObject *other, int op)
Py_RETURN_BOOL(ret);
}

#define MAXPATHLEN 256

static PyObject *Symbol_source(Symbol *self, PyObject *arg)
{
char source[MAXPATHLEN];
Program *prgm = ((Symbol *)self)->prog;
struct drgn_program *p = &prgm->prog;
int ln_num = 0;
int ln_col = 0;
unsigned long offset = PyLong_AsLong(arg);

struct drgn_error *err;
err = drgn_symbol_source(((Symbol *)self)->sym, p,
offset, sizeof(source),
source, &ln_num, &ln_col);
if (err)
return set_drgn_error(err);

return Py_BuildValue("sii", source, ln_num, ln_col);
}

static PyObject *Symbol_get_name(Symbol *self, void *arg)
{
return PyUnicode_FromString(drgn_symbol_name(self->sym));
Expand Down Expand Up @@ -93,6 +114,12 @@ static PyObject *Symbol_repr(Symbol *self)

}

static PyMethodDef Symbol_methods[] = {
{"source", (PyCFunction)Symbol_source, METH_O,
drgn_Symbol_source_DOC},
{},
};

static PyGetSetDef Symbol_getset[] = {
{"name", (getter)Symbol_get_name, NULL, drgn_Symbol_name_DOC},
{"address", (getter)Symbol_get_address, NULL, drgn_Symbol_address_DOC},
Expand All @@ -111,5 +138,6 @@ PyTypeObject Symbol_type = {
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = drgn_Symbol_DOC,
.tp_richcompare = (richcmpfunc)Symbol_richcompare,
.tp_methods = Symbol_methods,
.tp_getset = Symbol_getset,
};
44 changes: 44 additions & 0 deletions libdrgn/symbol.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
#include <elf.h>
#include <stdlib.h>
#include <string.h>
#include <dwarf.h>
#include <elfutils/libdw.h>
#include <elfutils/libdwfl.h>

#include "debug_info.h"
#include "drgn.h"
#include "error.h"
#include "program.h"
#include "symbol.h"
#include "util.h"

Expand Down Expand Up @@ -40,6 +46,44 @@ void drgn_symbol_from_elf(const char *name, uint64_t address,
ret->kind = DRGN_SYMBOL_KIND_UNKNOWN;
}

LIBDRGN_PUBLIC struct drgn_error *
drgn_symbol_source(struct drgn_symbol *sym,
struct drgn_program *prog,
unsigned long offset,
size_t size,
char *src_file_ret,
int *line_num_ret,
int *line_col_ret)
{
if (sym->kind != DRGN_SYMBOL_KIND_FUNC)
return drgn_error_create(DRGN_ERROR_LOOKUP,
"symbol is not a function");

unsigned long sym_address = sym->address + offset;
struct drgn_debug_info_module *dim;
dim = drgn_debug_info_module_byaddress(prog->dbinfo, sym_address);
if (dim == NULL)
return drgn_error_create(DRGN_ERROR_LOOKUP,
"could not locate module from function address");


Dwfl_Line *line = dwfl_module_getsrc(dim->dwfl_module, sym_address);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dwfl_module_getsrc() doesn't work on binaries compiled by Clang, because Clang doesn't generate .debug_aranges by default and dwfl_module_getsrc() only checks .debug_aranges. I would really like this to work with Clang as well. I had to work around this same problem in add17a9. The difference here is that we don't have the CU containing the address yet. We need a function like:

struct drgn_error *
drgn_debug_info_module_find_dwarf_cu(struct drgn_debug_info_module *module,
				     uint64_t pc, uint64_t *bias_ret,
				     Dwarf_Die *cu_die_ret)

Which would be very similar to the beginning of drgn_debug_info_module_find_dwarf_scopes(), just without needing to iterate into children of the CU DIE. (Let me know if you need help factoring that out.) Then, you can do something similar to drgn_stack_frame_source():

drgn/libdrgn/stack_trace.c

Lines 312 to 331 in 330c71b

Dwarf_Addr bias;
if (!dwfl_module_getdwarf(dwfl_module, &bias))
return NULL;
pc.value = pc.value - bias - !regs->interrupted;
Dwarf_Die *scopes = trace->frames[frame].scopes;
size_t num_scopes = trace->frames[frame].num_scopes;
Dwarf_Die cu_die;
if (!dwarf_cu_die(scopes[num_scopes - 1].cu, &cu_die, NULL,
NULL, NULL, NULL, NULL, NULL))
return NULL;
Dwarf_Line *line = dwarf_getsrc_die(&cu_die, pc.value);
if (!line)
return NULL;
if (line_ret)
dwarf_lineno(line, line_ret);
if (column_ret)
dwarf_linecol(line, column_ret);
return dwarf_linesrc(line, NULL, NULL);

if (line == NULL)
return drgn_error_create(DRGN_ERROR_LOOKUP,
"could not obtain source code information");

const char *src;
int lineno, linecol;
if ((src = dwfl_lineinfo(line, &sym_address, &lineno, &linecol,
NULL, NULL)) != NULL) {
strncpy(src_file_ret, src, size);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather that we just strdup() this and leave it up to the caller to free() it instead of requiring a size.

*line_num_ret = lineno;
*line_col_ret = linecol;
}

return NULL;
}

LIBDRGN_PUBLIC const char *drgn_symbol_name(struct drgn_symbol *sym)
{
return sym->name;
Expand Down