libdrgn: embed DWARF index in DWARF info cache

This commit is contained in:
Omar Sandoval 2019-05-01 07:29:02 -07:00
parent 2ed8e3148c
commit 640b1c011d
5 changed files with 239 additions and 254 deletions

View File

@ -8,7 +8,6 @@
#include <gelf.h>
#include <inttypes.h>
#include <libelf.h>
#include <omp.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@ -18,7 +17,6 @@
#include "internal.h"
#include "dwarf_index.h"
#include "hash_table.h"
#include "read.h"
#include "siphash.h"
@ -115,7 +113,7 @@ struct file_name_table {
};
struct compilation_unit {
struct debug_file *file;
struct drgn_dwarf_index_file *file;
const char *ptr;
uint64_t unit_length;
uint16_t version;
@ -124,7 +122,7 @@ struct compilation_unit {
bool is_64_bit;
};
struct debug_file {
struct drgn_dwarf_index_file {
Elf_Data *sections[NUM_SECTIONS];
/* Other byte order. */
bool bswap;
@ -138,7 +136,7 @@ struct debug_file {
Elf *elf;
Dwarf *dwarf;
Elf_Data *rela_sections[NUM_SECTIONS];
struct debug_file *next;
struct drgn_dwarf_index_file *next;
};
static inline const char *section_ptr(Elf_Data *data, size_t offset)
@ -159,36 +157,28 @@ static inline const char *section_end(Elf_Data *data)
* 64-bit collision is unlikely enough, especially when also considering the
* name and tag.
*/
struct die_entry {
struct drgn_dwarf_index_die {
uint64_t tag;
uint64_t file_name_hash;
/*
* The next DIE with the same name (as an index into
* dwarf_index_shard::entries), or SIZE_MAX if this is the last DIE.
* drgn_dwarf_index_shard::dies), or SIZE_MAX if this is the last DIE.
*/
size_t next;
struct debug_file *file;
struct drgn_dwarf_index_file *file;
uint64_t offset;
};
/*
* The key is the DIE name. The value is the first DIE with that name (as an
* index into dwarf_index_shard::entries).
* index into drgn_dwarf_index_shard::dies).
*/
DEFINE_HASH_MAP(die_map, struct string, size_t, string_hash, string_eq)
DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_index_die_map, struct string, size_t,
string_hash, string_eq)
struct dwarf_index_shard {
omp_lock_t lock;
struct die_map map;
/*
* We store all entries in a shard as a single array, which is more
* cache friendly.
*/
struct die_entry *entries;
size_t num_entries, entries_capacity;
};
#define SHARD_BITS 8
DEFINE_HASH_MAP_FUNCTIONS(drgn_dwarf_index_file_map, const char *,
struct drgn_dwarf_index_file *, c_string_hash,
c_string_eq)
static inline size_t hash_pair_to_shard(struct hash_pair hp)
{
@ -196,23 +186,11 @@ static inline size_t hash_pair_to_shard(struct hash_pair hp)
* The 8 most significant bits of the hash are used as the F14 tag, so
* we don't want to use those for sharding.
*/
return ((hp.first >> (8 * sizeof(size_t) - 8 - SHARD_BITS)) &
(((size_t)1 << SHARD_BITS) - 1));
return ((hp.first >>
(8 * sizeof(size_t) - 8 - DRGN_DWARF_INDEX_SHARD_BITS)) &
(((size_t)1 << DRGN_DWARF_INDEX_SHARD_BITS) - 1));
}
DEFINE_HASH_MAP(debug_file_map, const char *, struct debug_file *,
c_string_hash, c_string_eq)
struct drgn_dwarf_index {
/* DRGN_DWARF_INDEX_* flags passed to drgn_dwarf_index_create(). */
int flags;
struct debug_file_map files;
struct debug_file *opened_first, *opened_last;
struct debug_file *indexed_first, *indexed_last;
/* The index is sharded to reduce lock contention. */
struct dwarf_index_shard shards[1 << SHARD_BITS];
};
static inline struct drgn_error *drgn_eof(void)
{
return drgn_error_create(DRGN_ERROR_DWARF_FORMAT,
@ -274,60 +252,56 @@ static void free_shards(struct drgn_dwarf_index *dindex, size_t n)
size_t i;
for (i = 0; i < n; i++) {
free(dindex->shards[i].entries);
die_map_deinit(&dindex->shards[i].map);
free(dindex->shards[i].dies);
drgn_dwarf_index_die_map_deinit(&dindex->shards[i].map);
omp_destroy_lock(&dindex->shards[i].lock);
}
}
struct drgn_error *
drgn_dwarf_index_create(int flags, struct drgn_dwarf_index **ret)
struct drgn_error *drgn_dwarf_index_init(struct drgn_dwarf_index *dindex,
enum drgn_dwarf_index_flags flags)
{
static const size_t initial_shard_capacity = max(1024 >> SHARD_BITS, 1);
static const size_t initial_shard_capacity =
max(1024 >> DRGN_DWARF_INDEX_SHARD_BITS, 1);
struct drgn_error *err;
struct drgn_dwarf_index *dindex;
size_t i;
if (flags & ~DRGN_DWARF_INDEX_ALL) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"invalid flags");
}
dindex = malloc(sizeof(*dindex));
if (!dindex)
return &drgn_enomem;
dindex->flags = flags;
debug_file_map_init(&dindex->files);
drgn_dwarf_index_file_map_init(&dindex->files);
dindex->opened_first = dindex->opened_last = NULL;
dindex->indexed_first = dindex->indexed_last = NULL;
for (i = 0; i < ARRAY_SIZE(dindex->shards); i++) {
struct dwarf_index_shard *shard = &dindex->shards[i];
struct drgn_dwarf_index_shard *shard = &dindex->shards[i];
omp_init_lock(&shard->lock);
die_map_init(&shard->map);
drgn_dwarf_index_die_map_init(&shard->map);
shard->num_entries = 0;
shard->entries_capacity = initial_shard_capacity;
shard->entries = malloc_array(initial_shard_capacity,
sizeof(*shard->entries));
if (!shard->entries ||
!die_map_reserve(&shard->map, initial_shard_capacity)) {
shard->dies = malloc_array(initial_shard_capacity,
sizeof(*shard->dies));
if (!shard->dies ||
!drgn_dwarf_index_die_map_reserve(&shard->map,
initial_shard_capacity)) {
free_shards(dindex, i + 1);
err = &drgn_enomem;
goto err;
}
}
*ret = dindex;
return NULL;
err:
debug_file_map_deinit(&dindex->files);
free(dindex);
drgn_dwarf_index_file_map_deinit(&dindex->files);
return err;
}
static void free_files(struct drgn_dwarf_index *dindex,
struct debug_file *files)
struct drgn_dwarf_index_file *files)
{
struct debug_file *file, *next;
struct drgn_dwarf_index_file *file, *next;
file = files;
while (file) {
@ -336,7 +310,8 @@ static void free_files(struct drgn_dwarf_index *dindex,
if (file->path) {
elf_end(file->elf);
close(file->fd);
debug_file_map_delete(&dindex->files, &file->path);
drgn_dwarf_index_file_map_delete(&dindex->files,
&file->path);
free((char *)file->path);
}
free(file);
@ -344,18 +319,17 @@ static void free_files(struct drgn_dwarf_index *dindex,
}
}
void drgn_dwarf_index_destroy(struct drgn_dwarf_index *dindex)
void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex)
{
if (dindex) {
free_shards(dindex, ARRAY_SIZE(dindex->shards));
free_files(dindex, dindex->opened_first);
free_files(dindex, dindex->indexed_first);
debug_file_map_deinit(&dindex->files);
free(dindex);
drgn_dwarf_index_file_map_deinit(&dindex->files);
}
}
static struct drgn_error *read_sections(struct debug_file *file)
static struct drgn_error *read_sections(struct drgn_dwarf_index_file *file)
{
struct drgn_error *err;
GElf_Ehdr ehdr_mem, *ehdr;
@ -461,15 +435,15 @@ struct drgn_error *drgn_dwarf_index_open(struct drgn_dwarf_index *dindex,
struct drgn_error *err;
const char *key;
struct hash_pair hp;
struct debug_file_map_pos pos;
struct debug_file *file;
struct drgn_dwarf_index_file_map_pos pos;
struct drgn_dwarf_index_file *file;
key = realpath(path, NULL);
if (!key)
return drgn_error_create_os(errno, path, "realpath");
hp = debug_file_map_hash(&path);
pos = debug_file_map_search_pos(&dindex->files, &key, hp);
hp = drgn_dwarf_index_file_map_hash(&path);
pos = drgn_dwarf_index_file_map_search_pos(&dindex->files, &key, hp);
if (pos.item) {
free((char *)key);
file = pos.item->value;
@ -498,8 +472,8 @@ struct drgn_error *drgn_dwarf_index_open(struct drgn_dwarf_index *dindex,
goto err_fd;
}
pos = debug_file_map_insert_searched_pos(&dindex->files, &key, &file,
hp);
pos = drgn_dwarf_index_file_map_insert_searched_pos(&dindex->files, &key,
&file, hp);
if (!pos.item) {
err = &drgn_enomem;
goto err_elf;
@ -520,7 +494,7 @@ out:
return NULL;
err_hash:
debug_file_map_delete_pos(&dindex->files, pos, hp);
drgn_dwarf_index_file_map_delete_pos(&dindex->files, pos, hp);
err_elf:
elf_end(file->elf);
err_fd:
@ -536,7 +510,7 @@ struct drgn_error *drgn_dwarf_index_open_elf(struct drgn_dwarf_index *dindex,
Elf *elf)
{
struct drgn_error *err;
struct debug_file *file;
struct drgn_dwarf_index_file *file;
file = calloc(1, sizeof(*file));
if (!file)
@ -613,9 +587,9 @@ static struct drgn_error *apply_relocation(Elf_Data *section,
return NULL;
}
static size_t count_relocations(struct debug_file *files)
static size_t count_relocations(struct drgn_dwarf_index_file *files)
{
struct debug_file *file = files;
struct drgn_dwarf_index_file *file = files;
size_t count = 0;
size_t i;
@ -632,7 +606,7 @@ static size_t count_relocations(struct debug_file *files)
return count;
}
static struct drgn_error *apply_relocations(struct debug_file *files)
static struct drgn_error *apply_relocations(struct drgn_dwarf_index_file *files)
{
struct drgn_error *err = NULL;
size_t total_num_relocs;
@ -640,7 +614,7 @@ static struct drgn_error *apply_relocations(struct debug_file *files)
total_num_relocs = count_relocations(files);
#pragma omp parallel
{
struct debug_file *file;
struct drgn_dwarf_index_file *file;
size_t section_idx = 0, reloc_idx = 0;
size_t i;
bool first = true;
@ -758,7 +732,7 @@ static struct drgn_error *read_compilation_unit_header(const char *ptr,
return NULL;
}
static struct drgn_error *read_cus(struct debug_file *file,
static struct drgn_error *read_cus(struct drgn_dwarf_index_file *file,
struct compilation_unit **cus,
size_t *num_cus, size_t *cus_capacity)
{
@ -1104,7 +1078,7 @@ static struct drgn_error *read_abbrev_table(int flags, const char *ptr,
return NULL;
}
static struct drgn_error *skip_lnp_header(struct debug_file *file,
static struct drgn_error *skip_lnp_header(struct drgn_dwarf_index_file *file,
const char **ptr, const char *end)
{
uint32_t tmp;
@ -1177,7 +1151,7 @@ static struct drgn_error *read_file_name_table(struct drgn_dwarf_index *dindex,
*/
static const uint64_t siphash_key[2];
struct drgn_error *err;
struct debug_file *file = cu->file;
struct drgn_dwarf_index_file *file = cu->file;
Elf_Data *debug_line = file->sections[SECTION_DEBUG_LINE];
const char *ptr = section_ptr(debug_line, stmt_list);
const char *end = section_end(debug_line);
@ -1269,34 +1243,36 @@ out:
return err;
}
static bool append_die_entry(struct dwarf_index_shard *shard, uint64_t tag,
uint64_t file_name_hash, struct debug_file *file,
static bool append_die_entry(struct drgn_dwarf_index_shard *shard, uint64_t tag,
uint64_t file_name_hash,
struct drgn_dwarf_index_file *file,
uint64_t offset)
{
struct die_entry *entry;
struct drgn_dwarf_index_die *die;
if (shard->num_entries >= shard->entries_capacity) {
size_t new_capacity;
new_capacity = shard->entries_capacity * 2;
if (!resize_array(&shard->entries, new_capacity))
if (!resize_array(&shard->dies, new_capacity))
return false;
shard->entries_capacity = new_capacity;
}
entry = &shard->entries[shard->num_entries++];
entry->tag = tag;
entry->file_name_hash = file_name_hash;
entry->file = file;
entry->offset = offset;
entry->next = SIZE_MAX;
die = &shard->dies[shard->num_entries++];
die->tag = tag;
die->file_name_hash = file_name_hash;
die->file = file;
die->offset = offset;
die->next = SIZE_MAX;
return true;
}
static struct drgn_error *index_die(struct drgn_dwarf_index *dindex,
const char *name, uint64_t tag,
uint64_t file_name_hash,
struct debug_file *file, uint64_t offset)
struct drgn_dwarf_index_file *file,
uint64_t offset)
{
struct drgn_error *err;
struct string key = {
@ -1304,14 +1280,14 @@ static struct drgn_error *index_die(struct drgn_dwarf_index *dindex,
.len = strlen(name),
};
struct hash_pair hp;
struct dwarf_index_shard *shard;
struct drgn_dwarf_index_shard *shard;
size_t *value, index;
struct die_entry *entry;
struct drgn_dwarf_index_die *die;
hp = die_map_hash(&key);
hp = drgn_dwarf_index_die_map_hash(&key);
shard = &dindex->shards[hash_pair_to_shard(hp)];
omp_set_lock(&shard->lock);
value = die_map_search_hashed(&shard->map, &key, hp);
value = drgn_dwarf_index_die_map_search_hashed(&shard->map, &key, hp);
if (!value) {
if (!append_die_entry(shard, tag, file_name_hash, file,
offset)) {
@ -1319,32 +1295,33 @@ static struct drgn_error *index_die(struct drgn_dwarf_index *dindex,
goto out;
}
index = shard->num_entries - 1;
if (die_map_insert_searched(&shard->map, &key, &index, hp))
if (drgn_dwarf_index_die_map_insert_searched(&shard->map, &key,
&index, hp))
err = NULL;
else
err = &drgn_enomem;
goto out;
}
entry = &shard->entries[*value];
die = &shard->dies[*value];
for (;;) {
if (entry->tag == tag &&
entry->file_name_hash == file_name_hash) {
if (die->tag == tag &&
die->file_name_hash == file_name_hash) {
err = NULL;
goto out;
}
if (entry->next == SIZE_MAX)
if (die->next == SIZE_MAX)
break;
entry = &shard->entries[entry->next];
die = &shard->dies[die->next];
}
index = entry - shard->entries;
index = die - shard->dies;
if (!append_die_entry(shard, tag, file_name_hash, file, offset)) {
err = &drgn_enomem;
goto out;
}
shard->entries[index].next = shard->num_entries - 1;
shard->dies[index].next = shard->num_entries - 1;
err = NULL;
out:
omp_unset_lock(&shard->lock);
@ -1543,7 +1520,7 @@ static struct drgn_error *index_cu(struct drgn_dwarf_index *dindex,
struct drgn_error *err;
struct abbrev_table abbrev_table = {};
struct file_name_table file_name_table = {};
struct debug_file *file = cu->file;
struct drgn_dwarf_index_file *file = cu->file;
Elf_Data *debug_abbrev = file->sections[SECTION_DEBUG_ABBREV];
const char *debug_abbrev_end = section_end(debug_abbrev);
const char *ptr = &cu->ptr[cu->is_64_bit ? 23 : 11];
@ -1685,9 +1662,9 @@ static struct drgn_error *index_cus(struct drgn_dwarf_index *dindex,
}
static void unindex_files(struct drgn_dwarf_index *dindex,
struct debug_file *files)
struct drgn_dwarf_index_file *files)
{
struct debug_file *file;
struct drgn_dwarf_index_file *file;
size_t i;
/* First, mark all of the files that failed. */
@ -1697,37 +1674,38 @@ static void unindex_files(struct drgn_dwarf_index *dindex,
file = file->next;
} while (file);
/* Then, delete all of the entries pointing to those files. */
/* Then, delete all of the dies pointing to those files. */
for (i = 0; i < ARRAY_SIZE(dindex->shards); i++) {
struct dwarf_index_shard *shard = &dindex->shards[i];
struct die_map_pos pos;
struct drgn_dwarf_index_shard *shard = &dindex->shards[i];
struct drgn_dwarf_index_die_map_pos pos;
/*
* Because we're deleting everything that was added since the
* last update, we can just shrink the entries array to the
* first entry that was added for this update.
* last update, we can just shrink the dies array to the first
* entry that was added for this update.
*/
while (shard->num_entries) {
struct die_entry *entry;
struct drgn_dwarf_index_die *die;
entry = &shard->entries[shard->num_entries - 1];
if (entry->file->failed)
die = &shard->dies[shard->num_entries - 1];
if (die->file->failed)
shard->num_entries--;
else
break;
}
/*
* We also need to delete those entries in the map. Note that
* any entries chained on the entries we delete must have also
* been added for this update, so there's no need to preserve
* them.
* We also need to delete those dies in the map. Note that any
* dies chained on the dies we delete must have also been added
* for this update, so there's no need to preserve them.
*/
pos = die_map_first_pos(&shard->map);
pos = drgn_dwarf_index_die_map_first_pos(&shard->map);
while (pos.item) {
if (pos.item->value >= shard->num_entries)
die_map_delete(&shard->map, &pos.item->key);
die_map_next_pos(&pos);
if (pos.item->value >= shard->num_entries) {
drgn_dwarf_index_die_map_delete(&shard->map,
&pos.item->key);
}
drgn_dwarf_index_die_map_next_pos(&pos);
}
}
}
@ -1735,7 +1713,7 @@ static void unindex_files(struct drgn_dwarf_index *dindex,
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex)
{
struct drgn_error *err;
struct debug_file *first, *last, *file;
struct drgn_dwarf_index_file *first, *last, *file;
struct compilation_unit *cus = NULL;
size_t num_cus = 0, cus_capacity = 0;
@ -1795,13 +1773,14 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
.len = name_len,
};
struct hash_pair hp;
struct dwarf_index_shard *shard;
struct drgn_dwarf_index_shard *shard;
size_t *value;
hp = die_map_hash(&key);
hp = drgn_dwarf_index_die_map_hash(&key);
it->shard = hash_pair_to_shard(hp);
shard = &dindex->shards[it->shard];
value = die_map_search_hashed(&shard->map, &key, hp);
value = drgn_dwarf_index_die_map_search_hashed(&shard->map,
&key, hp);
it->index = value ? *value : SIZE_MAX;
it->any_name = false;
} else {
@ -1819,14 +1798,14 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
static inline bool
drgn_dwarf_index_iterator_matches_tag(struct drgn_dwarf_index_iterator *it,
struct die_entry *entry)
struct drgn_dwarf_index_die *die)
{
size_t i;
if (it->num_tags == 0)
return true;
for (i = 0; i < it->num_tags; i++) {
if (entry->tag == it->tags[i])
if (die->tag == it->tags[i])
return true;
}
return false;
@ -1837,18 +1816,18 @@ drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
Dwarf_Die *die)
{
struct drgn_dwarf_index *dindex = it->dindex;
struct die_entry *entry;
struct debug_file *file;
struct drgn_dwarf_index_die *index_die;
struct drgn_dwarf_index_file *file;
if (it->any_name) {
for (;;) {
struct dwarf_index_shard *shard;
struct drgn_dwarf_index_shard *shard;
if (it->shard >= ARRAY_SIZE(dindex->shards))
return &drgn_stop;
shard = &dindex->shards[it->shard];
entry = &shard->entries[it->index];
index_die = &shard->dies[it->index];
if (++it->index >= shard->num_entries) {
it->index = 0;
@ -1858,27 +1837,29 @@ drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
}
}
if (drgn_dwarf_index_iterator_matches_tag(it, entry))
if (drgn_dwarf_index_iterator_matches_tag(it,
index_die))
break;
}
} else {
for (;;) {
struct dwarf_index_shard *shard;
struct drgn_dwarf_index_shard *shard;
if (it->index == SIZE_MAX)
return &drgn_stop;
shard = &dindex->shards[it->shard];
entry = &shard->entries[it->index];
index_die = &shard->dies[it->index];
it->index = entry->next;
it->index = index_die->next;
if (drgn_dwarf_index_iterator_matches_tag(it, entry))
if (drgn_dwarf_index_iterator_matches_tag(it,
index_die))
break;
}
}
file = entry->file;
file = index_die->file;
if (!file->dwarf) {
file->dwarf = dwarf_begin_elf(file->elf,
DWARF_C_READ,
@ -1886,7 +1867,7 @@ drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
if (!file->dwarf)
return drgn_error_libdw();
}
if (!dwarf_offdie(file->dwarf, entry->offset, die))
if (!dwarf_offdie(file->dwarf, index_die->offset, die))
return drgn_error_libdw();
return NULL;
}

View File

@ -14,10 +14,12 @@
#include <elfutils/libdw.h>
#include <libelf.h>
#include <omp.h>
#include <stddef.h>
#include <stdint.h>
#include "drgn.h"
#include "hash_table.h"
/**
* @ingroup Internals
@ -41,30 +43,8 @@
* @{
*/
/**
* @struct drgn_dwarf_index
*
* Fast index of DWARF debugging information.
*
* This interface indexes DWARF debugging information by name and tag,
* deduplicating information which exists in multiple compilation units or
* 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;
enum {
/** Flags for a @ref drgn_dwarf_index_flags. */
enum drgn_dwarf_index_flags {
/**
* Index global type information. This excludes incomplete types (i.e.,
* types with @c DW_AT_declaration).
@ -83,27 +63,75 @@ enum {
DRGN_DWARF_INDEX_ALL = (1 << 4) - 1,
};
/**
* Allocate a new, empty DWARF index.
*
* @param[in] flags Bitmask of <tt>DRGN_DWARF_INDEX_*</tt> flags indicating what
* to index.
* @param[out] ret Returned index.
* @return @c NULL on success or non-@c NULL on error, in which case the
* contents of @c dindex are undefined.
*/
struct drgn_error *drgn_dwarf_index_create(int flags,
struct drgn_dwarf_index **ret);
struct drgn_dwarf_index_die;
struct drgn_dwarf_index_file;
DEFINE_HASH_MAP_TYPES(drgn_dwarf_index_file_map, const char *,
struct drgn_dwarf_index_file *)
DEFINE_HASH_MAP_TYPES(drgn_dwarf_index_die_map, struct string, size_t)
struct drgn_dwarf_index_shard {
/** @privatesection */
omp_lock_t lock;
struct drgn_dwarf_index_die_map map;
/*
* We store all entries in a shard as a single array, which is more
* cache friendly.
*/
struct drgn_dwarf_index_die *dies;
size_t num_entries, entries_capacity;
};
#define DRGN_DWARF_INDEX_SHARD_BITS 8
/**
* Free all of the resources used by a DWARF index.
* Fast index of DWARF debugging information.
*
* This interface indexes DWARF debugging information by name and tag,
* deduplicating information which exists in multiple compilation units or
* 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 */
/* DRGN_DWARF_INDEX_* flags passed to drgn_dwarf_index_create(). */
enum drgn_dwarf_index_flags flags;
struct drgn_dwarf_index_file_map 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. */
struct drgn_dwarf_index_shard shards[1 << DRGN_DWARF_INDEX_SHARD_BITS];
};
/**
* Initialize a @ref drgn_dwarf_index.
*
* @param[in] flags Bitmask of @ref drgn_dwarf_index_flags indicating what to
* index.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *drgn_dwarf_index_init(struct drgn_dwarf_index *dindex,
enum drgn_dwarf_index_flags flags);
/**
* Deinitialize a @ref drgn_dwarf_index.
*
* After this is called, anything belonging to the index should no longer be
* accessed.
*
* @param[in] dindex Index to free.
*/
void drgn_dwarf_index_destroy(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.

View File

@ -362,8 +362,8 @@ drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die die;
struct drgn_qualified_type qualified_type;
drgn_dwarf_index_iterator_init(&it, dicache->dindex, name, strlen(name),
&tag, 1);
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name,
strlen(name), &tag, 1);
/*
* 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.
@ -1389,7 +1389,7 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
DRGN_UNREACHABLE();
}
drgn_dwarf_index_iterator_init(&it, dicache->dindex, name, name_len,
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name, name_len,
&tag, 1);
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) {
if (die_matches_filename(&die, filename)) {
@ -1503,8 +1503,8 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
if (flags & DRGN_FIND_OBJECT_VARIABLE)
tags[num_tags++] = DW_TAG_variable;
drgn_dwarf_index_iterator_init(&it, dicache->dindex, name, strlen(name),
tags, num_tags);
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name,
strlen(name), tags, num_tags);
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) {
if (!die_matches_filename(&die, filename))
continue;
@ -1539,19 +1539,27 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
struct drgn_error *
drgn_dwarf_info_cache_create(struct drgn_type_index *tindex,
struct drgn_dwarf_index *dindex,
struct drgn_dwarf_info_cache **ret)
{
struct drgn_error *err;
struct drgn_dwarf_info_cache *dicache;
dicache = malloc(sizeof(*dicache));
if (!dicache)
return &drgn_enomem;
dicache->tindex = tindex;
err = drgn_dwarf_index_init(&dicache->dindex,
DRGN_DWARF_INDEX_TYPES |
DRGN_DWARF_INDEX_VARIABLES |
DRGN_DWARF_INDEX_ENUMERATORS |
DRGN_DWARF_INDEX_FUNCTIONS);
if (err) {
free(dicache);
return err;
}
dwarf_type_map_init(&dicache->map);
dwarf_type_map_init(&dicache->cant_be_incomplete_array_map);
dicache->dindex = dindex;
dicache->depth = 0;
dicache->tindex = tindex;
dicache->prog = NULL;
dicache->relocation_hook = NULL;
*ret = dicache;
@ -1571,5 +1579,6 @@ void drgn_dwarf_info_cache_destroy(struct drgn_dwarf_info_cache *dicache)
drgn_dwarf_type_free(&pos.item->value);
dwarf_type_map_deinit(&dicache->cant_be_incomplete_array_map);
dwarf_type_map_deinit(&dicache->map);
drgn_dwarf_index_deinit(&dicache->dindex);
free(dicache);
}

View File

@ -57,10 +57,8 @@ struct drgn_symbol;
* drgn_dwarf_symbol_find().
*/
struct drgn_dwarf_info_cache {
/** Type index. */
struct drgn_type_index *tindex;
/** Index of DWARF debugging information. */
struct drgn_dwarf_index *dindex;
struct drgn_dwarf_index dindex;
/**
* Cache of parsed types.
*
@ -77,6 +75,8 @@ struct drgn_dwarf_info_cache {
struct dwarf_type_map cant_be_incomplete_array_map;
/** Current parsing recursion depth. */
int depth;
/** Type index. */
struct drgn_type_index *tindex;
/** Program to pass to @c relocation_hook(). */
struct drgn_program *prog;
/**
@ -104,7 +104,6 @@ struct drgn_dwarf_info_cache {
/** Create a @ref drgn_dwarf_info_cache. */
struct drgn_error *
drgn_dwarf_info_cache_create(struct drgn_type_index *tindex,
struct drgn_dwarf_index *dindex,
struct drgn_dwarf_info_cache **ret);
/** Destroy a @ref drgn_dwarf_info_cache. */

View File

@ -1059,11 +1059,6 @@ static void cleanup_fd(void *arg)
close((intptr_t)arg);
}
static void cleanup_dwarf_index(void *arg)
{
drgn_dwarf_index_destroy(arg);
}
static void cleanup_dwarf_info_cache(void *arg)
{
drgn_dwarf_info_cache_destroy(arg);
@ -1076,11 +1071,6 @@ static void cleanup_file_mappings(void *arg)
free_file_mappings(prog->mappings, prog->num_mappings);
}
#define PROGRAM_DWARF_INDEX_FLAGS (DRGN_DWARF_INDEX_TYPES | \
DRGN_DWARF_INDEX_VARIABLES | \
DRGN_DWARF_INDEX_ENUMERATORS | \
DRGN_DWARF_INDEX_FUNCTIONS)
static Elf_Type note_header_type(GElf_Phdr *phdr)
{
#if _ELFUTILS_PREREQ(0, 175)
@ -1107,7 +1097,6 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
bool have_nt_taskstruct = false, have_vmcoreinfo = false;
bool have_non_zero_phys_addr = false, is_proc_kcore;
struct drgn_memory_reader *reader;
struct drgn_dwarf_index *dindex;
struct drgn_type_index *tindex;
struct drgn_symbol_index *sindex;
struct drgn_dwarf_info_cache *dicache;
@ -1318,40 +1307,38 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
goto out_mappings;
}
err = drgn_dwarf_index_create(PROGRAM_DWARF_INDEX_FLAGS, &dindex);
if (err)
goto out_mappings;
if (have_vmcoreinfo)
err = open_kernel_files(dindex, vmcoreinfo.osrelease, verbose);
else
err = open_userspace_files(dindex, mappings, num_mappings);
if (err)
goto out_dindex;
err = drgn_dwarf_index_update(dindex);
if (err)
goto out_dindex;
err = drgn_type_index_create(is_64_bit ? 8 : 4, &tindex);
if (err)
goto out_dindex;
goto out_mappings;
err = drgn_symbol_index_create(&sindex);
if (err)
goto out_tindex;
err = drgn_dwarf_info_cache_create(tindex, dindex, &dicache);
err = drgn_dwarf_info_cache_create(tindex, &dicache);
if (err)
goto out_sindex;
if (have_vmcoreinfo) {
err = open_kernel_files(&dicache->dindex, vmcoreinfo.osrelease,
verbose);
} else {
err = open_userspace_files(&dicache->dindex, mappings,
num_mappings);
}
if (err)
goto out_dicache;
err = drgn_dwarf_index_update(&dicache->dindex);
if (err)
goto out_dicache;
err = drgn_type_index_add_finder(tindex, drgn_dwarf_type_find, dicache);
if (err)
goto out_dtcache;
goto out_dicache;
err = drgn_symbol_index_add_finder(sindex, drgn_dwarf_symbol_find,
dicache);
if (err)
goto out_dtcache;
goto out_dicache;
drgn_program_init(prog, reader, tindex, sindex);
prog->little_endian = is_little_endian;
@ -1361,12 +1348,9 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
err = drgn_program_add_cleanup(prog, free, file_segments);
if (err)
goto out_cleanup_fd;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_index, dindex);
if (err)
goto out_cleanup_file_segments;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_info_cache, dicache);
if (err)
goto out_cleanup_dindex;
goto out_cleanup_file_segments;
dicache->prog = prog;
if (have_vmcoreinfo) {
prog->flags |= DRGN_PROGRAM_IS_LINUX_KERNEL;
@ -1379,28 +1363,24 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
err = drgn_program_add_cleanup(prog, cleanup_file_mappings,
prog);
if (err)
goto out_cleanup_dtcache;
goto out_cleanup_dicache;
}
return NULL;
out_cleanup_dtcache:
out_cleanup_dicache:
drgn_program_remove_cleanup(prog, cleanup_dwarf_info_cache, dicache);
out_cleanup_dindex:
drgn_program_remove_cleanup(prog, cleanup_dwarf_index, dindex);
out_cleanup_file_segments:
drgn_program_remove_cleanup(prog, free, file_segments);
out_cleanup_fd:
drgn_program_remove_cleanup(prog, cleanup_fd, (void *)(intptr_t)fd);
out_program:
drgn_program_deinit(prog);
out_dtcache:
out_dicache:
drgn_dwarf_info_cache_destroy(dicache);
out_sindex:
drgn_symbol_index_destroy(sindex);
out_tindex:
drgn_type_index_destroy(tindex);
out_dindex:
drgn_dwarf_index_destroy(dindex);
out_mappings:
free_file_mappings(mappings, num_mappings);
free(file_segments);
@ -1481,7 +1461,6 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
struct file_mapping *mappings = NULL;
size_t num_mappings = 0;
struct drgn_memory_reader *reader;
struct drgn_dwarf_index *dindex;
struct drgn_type_index *tindex;
struct drgn_symbol_index *sindex;
struct drgn_dwarf_info_cache *dicache;
@ -1515,36 +1494,32 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
if (err)
goto out_mappings;
err = drgn_dwarf_index_create(PROGRAM_DWARF_INDEX_FLAGS, &dindex);
if (err)
goto out_mappings;
err = open_userspace_files(dindex, mappings, num_mappings);
if (err)
goto out_dindex;
err = drgn_dwarf_index_update(dindex);
if (err)
goto out_dindex;
err = drgn_type_index_create(sizeof(void *), &tindex);
if (err)
goto out_dindex;
goto out_mappings;
err = drgn_symbol_index_create(&sindex);
if (err)
goto out_tindex;
err = drgn_dwarf_info_cache_create(tindex, dindex, &dicache);
err = drgn_dwarf_info_cache_create(tindex, &dicache);
if (err)
goto out_sindex;
err = open_userspace_files(&dicache->dindex, mappings, num_mappings);
if (err)
goto out_dicache;
err = drgn_dwarf_index_update(&dicache->dindex);
if (err)
goto out_dicache;
err = drgn_type_index_add_finder(tindex, drgn_dwarf_type_find, dicache);
if (err)
goto out_dtcache;
goto out_dicache;
err = drgn_symbol_index_add_finder(sindex, drgn_dwarf_symbol_find,
dicache);
if (err)
goto out_dtcache;
goto out_dicache;
drgn_program_init(prog, reader, tindex, sindex);
prog->little_endian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__;
@ -1558,35 +1533,28 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
err = drgn_program_add_cleanup(prog, free, file_segment);
if (err)
goto out_cleanup_fd;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_index, dindex);
if (err)
goto out_cleanup_file_segment;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_info_cache, dicache);
if (err)
goto out_cleanup_dindex;
goto out_cleanup_file_segment;
err = drgn_program_add_cleanup(prog, cleanup_file_mappings, prog);
if (err)
goto out_cleanup_dtcache;
goto out_cleanup_dicache;
return NULL;
out_cleanup_dtcache:
out_cleanup_dicache:
drgn_program_remove_cleanup(prog, cleanup_dwarf_info_cache, dicache);
out_cleanup_dindex:
drgn_program_remove_cleanup(prog, cleanup_dwarf_index, dindex);
out_cleanup_file_segment:
drgn_program_remove_cleanup(prog, free, file_segment);
out_cleanup_fd:
drgn_program_remove_cleanup(prog, cleanup_fd, (void *)(intptr_t)fd);
out_program:
drgn_program_deinit(prog);
out_dtcache:
out_dicache:
drgn_dwarf_info_cache_destroy(dicache);
out_sindex:
drgn_symbol_index_destroy(sindex);
out_tindex:
drgn_type_index_destroy(tindex);
out_dindex:
drgn_dwarf_index_destroy(dindex);
out_mappings:
free_file_mappings(mappings, num_mappings);
out_reader: