Skip to content

Commit

Permalink
libdrgn: use libdwfl
Browse files Browse the repository at this point in the history
libdwfl is the elfutils "DWARF frontend library". It has high-level
functionality for looking up symbols, walking stack traces, etc. In
order to use this functionality, we need to report our debugging
information through libdwfl. For userspace programs, libdwfl has a much
better implementation than drgn for automatically finding debug
information from a core dump or PID. However, for the kernel, libdwfl
has a few issues:

- It only supports finding debug information for the running kernel, not
  vmcores.
- It determines the vmlinux address range by reading /proc/kallsyms,
  which is slow (~70ms on my machine).
- If separate debug information isn't available for a kernel module, it
  finds it by walking /lib/modules/$(uname -r)/kernel; this is repeated
  for every module.
- It doesn't find kernel modules with names containing both dashes and
  underscores (e.g., aes-x86_64).

Luckily, drgn already solved all of these problems, and with some
effort, we can keep doing it ourselves and report it to libdwfl.

The conversion replaces a bunch of code for dealing with userspace core
dump notes, /proc/$pid/maps, and relocations.
  • Loading branch information
osandov committed Jul 15, 2019
1 parent a9a2cb7 commit e5874ad
Show file tree
Hide file tree
Showing 12 changed files with 1,568 additions and 1,469 deletions.
849 changes: 405 additions & 444 deletions libdrgn/dwarf_index.c

Large diffs are not rendered by default.

144 changes: 73 additions & 71 deletions libdrgn/dwarf_index.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
#ifndef DRGN_DWARF_INDEX_H
#define DRGN_DWARF_INDEX_H

#include <elfutils/libdw.h>
#include <elfutils/libdwfl.h>
#include <libelf.h>
#include <omp.h>
#include <stddef.h>
#include <stdint.h>

#include "drgn.h"
#include "hash_table.h"
#include "string_builder.h"
#include "vector.h"

/**
Expand All @@ -44,38 +45,53 @@
* @{
*/

enum {
SECTION_SYMTAB,
SECTION_DEBUG_ABBREV,
SECTION_DEBUG_INFO,
SECTION_DEBUG_LINE,
SECTION_DEBUG_STR,
DRGN_DWARF_INDEX_NUM_SECTIONS,
};

struct drgn_dwarf_index_file {
Elf_Data *sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
bool failed;
/**
* drgn-specific data for the @c Dwfl_Module userdata pointer.
*
* For a newly created userdata, @c indexed is @c false and @c err is @c NULL.
* They are updated by @ref drgn_dwarf_index_update(). @c err may be set with
* @ref drgn_dwfl_module_userdata_set_error() before @ref
* drgn_dwarf_index_update() is called to skip indexing for that module; the
* error message will be added to the @ref DRGN_ERROR_MISSING_DEBUG_INFO error.
*
* @sa drgn_dwfl_find_elf(), drgn_dwfl_section_address()
*/
struct drgn_dwfl_module_userdata {
/** Whether the module is indexed in a @ref drgn_dwarf_index. */
bool indexed;
/** File descriptor of @ref drgn_dwfl_module_userdata::elf. */
int fd;
/*
* If this is NULL, then we didn't open the file and don't own the Elf
* handle.
*/
const char *path;
/** Error encountered while indexing. */
struct drgn_error *err;
/** Path of @ref drgn_dwfl_module_userdata::elf. */
char *path;
/** ELF handle to use. */
Elf *elf;
Dwarf *dwarf;
Elf_Data *rela_sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
struct drgn_dwarf_index_file *next;
};

static inline const char *
drgn_dwarf_index_file_to_key(struct drgn_dwarf_index_file * const *entry)
struct drgn_dwfl_module_userdata *drgn_dwfl_module_userdata_create(void);

void
drgn_dwfl_module_userdata_destroy(struct drgn_dwfl_module_userdata *userdata);

/* This takes ownership of err. */
void
drgn_dwfl_module_userdata_set_error(struct drgn_dwfl_module_userdata *userdata,
const char *message,
struct drgn_error *err);

extern const Dwfl_Callbacks drgn_dwfl_callbacks;

/** Get the @ref drgn_dwfl_module_userdata for a @c Dwfl_Module. */
static inline struct drgn_dwfl_module_userdata *
drgn_dwfl_module_userdata(Dwfl_Module *module)
{
return (*entry)->path;
void **userdatap;

dwfl_module_info(module, &userdatap, NULL, NULL, NULL, NULL, NULL,
NULL);
return *userdatap;
}
DEFINE_HASH_TABLE_TYPE(drgn_dwarf_index_file_table,
struct drgn_dwarf_index_file *,
drgn_dwarf_index_file_to_key)

struct drgn_dwarf_index_die;
DEFINE_HASH_MAP_TYPE(drgn_dwarf_index_die_map, struct string, size_t)
Expand All @@ -102,23 +118,14 @@ struct drgn_dwarf_index_shard {
* files. It is much faster for this task than other generic DWARF parsing
* libraries.
*
* A new DWARF index is created by @ref drgn_dwarf_index_create(). It is freed
* by @ref drgn_dwarf_index_destroy().
*
* Indexing happens in two steps: the files to index are opened using @ref
* drgn_dwarf_index_open(), then they all are parsed and indexed by @ref
* drgn_dwarf_index_update(). The update step is parallelized across CPUs, so it
* is most efficient to open as many files as possible before indexing them all
* at once in parallel.
*
* Searches in the index are done with a @ref drgn_dwarf_index_iterator.
*/
struct drgn_dwarf_index {
/** @privatesection */
struct drgn_dwarf_index_file_table files;
struct drgn_dwarf_index_file *opened_first, *opened_last;
struct drgn_dwarf_index_file *indexed_first, *indexed_last;
/* The index is sharded to reduce lock contention. */
/**
* Index shards.
*
* This is sharded to reduce lock contention.
*/
struct drgn_dwarf_index_shard shards[1 << DRGN_DWARF_INDEX_SHARD_BITS];
};

Expand All @@ -134,44 +141,36 @@ void drgn_dwarf_index_init(struct drgn_dwarf_index *dindex);
void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex);

/**
* Open a file and add it to a DWARF index.
* Index new DWARF information.
*
* This function does the first part of indexing a file: it opens the file,
* reads or maps it, and checks that it contains the required debugging
* information. However, it does not actually parse the debugging information.
* To do so, call drgn_dwarf_index_update() once all of the files to index have
* been opened.
* This parses and indexes the debugging information for all modules in @p dwfl
* that have not yet been indexed.
*
* If this fails, the file is not opened, but previously opened files are not
* affected.
* On success, @ref drgn_dwfl_module_userdata::indexed is set to @c true for all
* modules that we were able to index, and @ref drgn_dwfl_module_userdata::err
* is set to non-@c NULL for all other modules.
*
* If debug information was not available for one or more modules, a @ref
* DRGN_ERROR_MISSING_DEBUG_INFO error is returned.
*
* On any other error, no new debugging information is indexed.
*
* @param[in] dindex DWARF index.
* @param[in] path Path to open.
* @param[out] elf If not @c NULL, the opened ELF file. It is valid until @ref
* drgn_dwarf_index_destroy() is called.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *drgn_dwarf_index_open(struct drgn_dwarf_index *dindex,
const char *path, Elf **elf);

/** Close any files which haven't been indexed yet. */
void drgn_dwarf_index_close_unindexed(struct drgn_dwarf_index *dindex);
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex,
Dwfl *dwfl);

/**
* Index newly opened files.
*
* This function does the second part of indexing a file: it applies ELF
* relocations, then parses and indexes the debugging information in all of the
* files opened by @ref drgn_dwarf_index_open() since the last call to @ref
* drgn_dwarf_index_update() or @ref drgn_dwarf_index_create().
*
* If this fails, no new debugging information is indexed and all opened files
* which were not already indexed are closed.
* Remove all @c Dwfl_Modules that aren't indexed (see @ref
* drgn_dwfl_module_userdata::indexed) from @p dwfl.
*
* @param[in] dindex DWARF index.
* @return @c NULL on success, non-@c NULL on error.
* This should be called if @ref drgn_dwarf_index_update() returned an error or
* if modules were reported and @ref drgn_dwarf_index_update() was not called.
*/
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex);
void drgn_remove_unindexed_dwfl_modules(Dwfl *dwfl);

/** Remove all @Dwfl_Modules from @p dwfl. */
void drgn_remove_all_dwfl_modules(Dwfl *dwfl);

/**
* Iterator over DWARF debugging information.
Expand Down Expand Up @@ -215,14 +214,17 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
*
* @param[in] it DWARF index iterator.
* @param[out] die_ret Returned DIE.
* @param[out] bias_ret Returned difference between addresses in the loaded
* module and addresses in the debugging information. This may be @c NULL if it
* is not needed.
* @return @c NULL on success, non-@c NULL on error. In particular, when there
* are no more matching DIEs, @p die_ret is not modified and an error with code
* @ref DRGN_ERROR_STOP is returned; this @ref DRGN_ERROR_STOP error does not
* have to be passed to @ref drgn_error_destroy().
*/
struct drgn_error *
drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
Dwarf_Die *die_ret);
Dwarf_Die *die_ret, uint64_t *bias_ret);

/** @} */

Expand Down
38 changes: 13 additions & 25 deletions libdrgn/dwarf_info_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,14 @@ drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache,
* Find a matching DIE. Note that drgn_dwarf_index does not contain DIEs
* with DW_AT_declaration, so this will always be a complete type.
*/
err = drgn_dwarf_index_iterator_next(&it, &die);
err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
if (err)
return err;
/*
* Look for another matching DIE. If there is one, then we can't be sure
* which type this is, so leave it incomplete rather than guessing.
*/
err = drgn_dwarf_index_iterator_next(&it, &die);
err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
if (!err)
return &drgn_stop;
else if (err->code != DRGN_ERROR_STOP)
Expand Down Expand Up @@ -1387,7 +1387,7 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,

drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name, name_len,
&tag, 1);
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) {
while (!(err = drgn_dwarf_index_iterator_next(&it, &die, NULL))) {
if (die_matches_filename(&die, filename)) {
err = drgn_type_from_dwarf(dicache, &die, ret);
if (err)
Expand All @@ -1408,8 +1408,8 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,

static struct drgn_error *
drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die, const char *name,
struct drgn_symbol *ret)
Dwarf_Die *die, uint64_t bias,
const char *name, struct drgn_symbol *ret)
{
struct drgn_error *err;
struct drgn_qualified_type qualified_type;
Expand All @@ -1427,20 +1427,14 @@ drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
"could not find address of '%s'",
name);
}
ret->address = low_pc;
ret->address = low_pc + bias;
ret->little_endian = dwarf_die_is_little_endian(die);
if (dicache->relocation_hook) {
err = dicache->relocation_hook(name, die, ret,
dicache->relocation_arg);
if (err)
return err;
}
return NULL;
}

static struct drgn_error *
drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die, const char *name,
Dwarf_Die *die, uint64_t bias, const char *name,
struct drgn_symbol *ret)
{
struct drgn_error *err;
Expand Down Expand Up @@ -1470,14 +1464,8 @@ drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
return drgn_error_create(DRGN_ERROR_DWARF_FORMAT,
"DW_AT_location has unimplemented operation");
}
ret->address = loc[0].number;
ret->address = loc[0].number + bias;
ret->little_endian = dwarf_die_is_little_endian(die);
if (dicache->relocation_hook) {
err = dicache->relocation_hook(name, die, ret,
dicache->relocation_arg);
if (err)
return err;
}
return NULL;
}

Expand All @@ -1492,6 +1480,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
size_t num_tags;
struct drgn_dwarf_index_iterator it;
Dwarf_Die die;
uint64_t bias;

num_tags = 0;
if (flags & DRGN_FIND_OBJECT_CONSTANT)
Expand All @@ -1503,7 +1492,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,

drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name,
strlen(name), tags, num_tags);
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) {
while (!(err = drgn_dwarf_index_iterator_next(&it, &die, &bias))) {
if (!die_matches_filename(&die, filename))
continue;
switch (dwarf_tag(&die)) {
Expand All @@ -1521,10 +1510,11 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
}
case DW_TAG_subprogram:
return drgn_symbol_from_dwarf_subprogram(dicache, &die,
name, ret);
bias, name,
ret);
case DW_TAG_variable:
return drgn_symbol_from_dwarf_variable(dicache, &die,
name, ret);
bias, name, ret);
default:
DRGN_UNREACHABLE();
}
Expand All @@ -1549,8 +1539,6 @@ drgn_dwarf_info_cache_create(struct drgn_type_index *tindex,
dwarf_type_map_init(&dicache->cant_be_incomplete_array_map);
dicache->depth = 0;
dicache->tindex = tindex;
dicache->relocation_hook = NULL;
dicache->relocation_arg = NULL;
*ret = dicache;
return NULL;
}
Expand Down
22 changes: 0 additions & 22 deletions libdrgn/dwarf_info_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,6 @@ struct drgn_dwarf_info_cache {
int depth;
/** Type index. */
struct drgn_type_index *tindex;
/**
* Relocation callback.
*
* Objects in an ELF file are often relocated when they are loaded into
* a program (e.g., shared libraries or position-independent
* executables). This callback can be used to adjust the address which
* was found in the DWARF debugging information entry for the symbol.
*
* On entry, @p sym is fully initialized and not a constant. This
* should look at @c sym->address and modify it as appropriate.
*
* @param[in] prog @ref drgn_dwarf_symbol_index::prog.
* @param[in] name Name of the symbol.
* @param[in] die DWARF DIE of the symbol.
* @param[in,out] sym Symbol to relocate.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *(*relocation_hook)(const char *name, Dwarf_Die *die,
struct drgn_symbol *sym,
void *arg);
/* Argument to pass to @ref drgn_dwarf_info_cache::relocation_hook. */
void *relocation_arg;
};

/** Create a @ref drgn_dwarf_info_cache. */
Expand Down
13 changes: 7 additions & 6 deletions libdrgn/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+

#include <errno.h>
#include <elfutils/libdw.h>
#include <elfutils/libdwfl.h>
#include <libelf.h>
#include <stdarg.h>
#include <stdio.h>
Expand All @@ -22,11 +22,6 @@ struct drgn_error drgn_stop = {
.message = "stop iteration",
};

struct drgn_error drgn_not_elf = {
.code = DRGN_ERROR_ELF_FORMAT,
.message = "not an ELF file",
};

static struct drgn_error *drgn_error_create_nodup(enum drgn_error_code code,
char *message)
{
Expand Down Expand Up @@ -194,3 +189,9 @@ struct drgn_error *drgn_error_libdw(void)
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdw error: %s",
dwarf_errmsg(-1));
}

struct drgn_error *drgn_error_libdwfl(void)
{
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdwfl error: %s",
dwfl_errmsg(-1));
}
Loading

0 comments on commit e5874ad

Please sign in to comment.