libdrgn: don't use OpenMP tasking

libomp (at least in LLVM 9 and 10) seems to have buggy OpenMP tasking
support. See commit 1cc3868955 ("CI: temporarily disable Clang") for
one example. OpenMP tasks aren't buying us much; they simplify DWARF
index updates in some places but complicate it in others. Let's ditch
tasks and go back to building an array of CUs to index similar to what
we did before commit f83bb7c71b ("libdrgn: move debugging information
tracking into drgn_debug_info"). There is no significant performance
difference.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2021-05-06 14:52:24 -07:00
parent 1cc3868955
commit e0921c5bdb
3 changed files with 168 additions and 155 deletions

View File

@ -981,8 +981,8 @@ drgn_debug_info_read_module(struct drgn_debug_info_load_state *load,
continue;
}
module->state = DRGN_DEBUG_INFO_MODULE_INDEXING;
drgn_dwarf_index_read_module(dindex_state, module);
return NULL;
return drgn_dwarf_index_read_module(dindex_state,
module);
}
}
/*
@ -1023,29 +1023,32 @@ drgn_debug_info_update_index(struct drgn_debug_info_load_state *load)
c_string_set_size(&dbinfo->module_names) +
load->new_modules.size))
return &drgn_enomem;
struct drgn_dwarf_index_update_state dindex_state;
drgn_dwarf_index_update_begin(&dindex_state, &dbinfo->dindex);
/*
* In OpenMP 5.0, this could be "#pragma omp parallel master taskloop"
* (added in GCC 9 and Clang 10).
*/
#pragma omp parallel
#pragma omp master
#pragma omp taskloop
if (!drgn_dwarf_index_update_state_init(&dindex_state, &dbinfo->dindex))
return &drgn_enomem;
struct drgn_error *err = NULL;
#pragma omp parallel for schedule(dynamic)
for (size_t i = 0; i < load->new_modules.size; i++) {
if (drgn_dwarf_index_update_cancelled(&dindex_state))
if (err)
continue;
struct drgn_error *module_err =
drgn_debug_info_read_module(load, &dindex_state,
load->new_modules.data[i]);
if (module_err)
drgn_dwarf_index_update_cancel(&dindex_state, module_err);
if (module_err) {
#pragma omp critical(drgn_debug_info_update_index_error)
if (err)
drgn_error_destroy(module_err);
else
err = module_err;
}
}
struct drgn_error *err = drgn_dwarf_index_update_end(&dindex_state);
if (err)
return err;
drgn_debug_info_free_modules(dbinfo, true, false);
return NULL;
if (!err)
err = drgn_dwarf_index_update(&dindex_state);
drgn_dwarf_index_update_state_deinit(&dindex_state);
if (!err)
drgn_debug_info_free_modules(dbinfo, true, false);
return err;
}
struct drgn_error *

View File

@ -19,6 +19,16 @@
#include "siphash.h"
#include "util.h"
struct drgn_dwarf_index_pending_cu {
struct drgn_debug_info_module *module;
const char *buf;
size_t len;
bool is_64_bit;
enum drgn_debug_info_scn scn;
};
DEFINE_VECTOR_FUNCTIONS(drgn_dwarf_index_pending_cu_vector)
/*
* The DWARF abbreviation table gets translated into a series of instructions.
* An instruction <= INSN_MAX_SKIP indicates a number of bytes to be skipped
@ -220,22 +230,26 @@ void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex)
drgn_dwarf_index_namespace_deinit(&dindex->global);
}
void drgn_dwarf_index_update_begin(struct drgn_dwarf_index_update_state *state,
bool
drgn_dwarf_index_update_state_init(struct drgn_dwarf_index_update_state *state,
struct drgn_dwarf_index *dindex)
{
state->dindex = dindex;
state->old_cus_size = dindex->cus.size;
state->err = NULL;
state->max_threads = omp_get_max_threads();
state->cus = malloc_array(state->max_threads, sizeof(*state->cus));
if (!state->cus)
return false;
for (size_t i = 0; i < state->max_threads; i++)
drgn_dwarf_index_pending_cu_vector_init(&state->cus[i]);
return true;
}
void drgn_dwarf_index_update_cancel(struct drgn_dwarf_index_update_state *state,
struct drgn_error *err)
void
drgn_dwarf_index_update_state_deinit(struct drgn_dwarf_index_update_state *state)
{
#pragma omp critical(drgn_dwarf_index_update_cancel)
if (state->err)
drgn_error_destroy(err);
else
state->err = err;
for (size_t i = 0; i < state->max_threads; i++)
drgn_dwarf_index_pending_cu_vector_deinit(&state->cus[i]);
free(state->cus);
}
static struct drgn_error *dw_form_to_insn(struct drgn_dwarf_index_cu *cu,
@ -1144,81 +1158,62 @@ skip:
return NULL;
}
static void drgn_dwarf_index_read_cus(struct drgn_dwarf_index_update_state *state,
struct drgn_debug_info_module *module,
enum drgn_debug_info_scn scn)
static struct drgn_error *
drgn_dwarf_index_read_cus(struct drgn_dwarf_index_update_state *state,
struct drgn_debug_info_module *module,
enum drgn_debug_info_scn scn)
{
struct drgn_dwarf_index_pending_cu_vector *cus =
&state->cus[omp_get_thread_num()];
struct drgn_error *err;
struct drgn_debug_info_buffer buffer;
drgn_debug_info_buffer_init(&buffer, module, scn);
while (binary_buffer_has_next(&buffer.bb)) {
const char *cu_buf = buffer.bb.pos;
struct drgn_dwarf_index_pending_cu *cu =
drgn_dwarf_index_pending_cu_vector_append_entry(cus);
if (!cu)
return &drgn_enomem;
cu->module = module;
cu->buf = buffer.bb.pos;
uint32_t unit_length32;
if ((err = binary_buffer_next_u32(&buffer.bb, &unit_length32)))
goto err;
bool is_64_bit = unit_length32 == UINT32_C(0xffffffff);
if (is_64_bit) {
return err;
cu->is_64_bit = unit_length32 == UINT32_C(0xffffffff);
if (cu->is_64_bit) {
uint64_t unit_length64;
if ((err = binary_buffer_next_u64(&buffer.bb,
&unit_length64)))
goto err;
return err;
if (unit_length64 > SIZE_MAX) {
err = binary_buffer_error(&buffer.bb,
"unit length is too large");
goto err;
return binary_buffer_error(&buffer.bb,
"unit length is too large");
}
if ((err = binary_buffer_skip(&buffer.bb,
unit_length64)))
goto err;
return err;
} else {
if ((err = binary_buffer_skip(&buffer.bb,
unit_length32)))
goto err;
}
size_t cu_len = buffer.bb.pos - cu_buf;
#pragma omp task
{
struct drgn_dwarf_index_cu cu = {
.module = module,
.buf = cu_buf,
.len = cu_len,
.is_64_bit = is_64_bit,
.is_type_unit = scn == DRGN_SCN_DEBUG_TYPES,
};
struct drgn_dwarf_index_cu_buffer cu_buffer;
drgn_dwarf_index_cu_buffer_init(&cu_buffer, &cu);
struct drgn_error *cu_err = read_cu(&cu_buffer);
if (cu_err)
goto cu_err;
cu_err = index_cu_first_pass(state->dindex, &cu_buffer);
if (cu_err)
goto cu_err;
#pragma omp critical(drgn_dwarf_index_cus)
if (!drgn_dwarf_index_cu_vector_append(&state->dindex->cus,
&cu))
cu_err = &drgn_enomem;
if (cu_err) {
cu_err:
drgn_dwarf_index_cu_deinit(&cu);
drgn_dwarf_index_update_cancel(state, cu_err);
}
return err;
}
cu->len = buffer.bb.pos - cu->buf;
cu->scn = scn;
}
return;
err:
drgn_dwarf_index_update_cancel(state, err);
return NULL;
}
void drgn_dwarf_index_read_module(struct drgn_dwarf_index_update_state *state,
struct drgn_debug_info_module *module)
struct drgn_error *
drgn_dwarf_index_read_module(struct drgn_dwarf_index_update_state *state,
struct drgn_debug_info_module *module)
{
drgn_dwarf_index_read_cus(state, module, DRGN_SCN_DEBUG_INFO);
if (module->scn_data[DRGN_SCN_DEBUG_TYPES])
drgn_dwarf_index_read_cus(state, module, DRGN_SCN_DEBUG_TYPES);
struct drgn_error *err;
err = drgn_dwarf_index_read_cus(state, module, DRGN_SCN_DEBUG_INFO);
if (!err && module->scn_data[DRGN_SCN_DEBUG_TYPES]) {
err = drgn_dwarf_index_read_cus(state, module,
DRGN_SCN_DEBUG_TYPES);
}
return err;
}
bool
@ -1691,16 +1686,56 @@ static void drgn_dwarf_index_rollback(struct drgn_dwarf_index *dindex)
}
struct drgn_error *
drgn_dwarf_index_update_end(struct drgn_dwarf_index_update_state *state)
drgn_dwarf_index_update(struct drgn_dwarf_index_update_state *state)
{
struct drgn_dwarf_index *dindex = state->dindex;
if (state->err)
size_t old_cus_size = dindex->cus.size;
size_t new_cus_size = old_cus_size;
for (size_t i = 0; i < state->max_threads; i++)
new_cus_size += state->cus[i].size;
if (!drgn_dwarf_index_cu_vector_reserve(&dindex->cus, new_cus_size))
return &drgn_enomem;
for (size_t i = 0; i < state->max_threads; i++) {
for (size_t j = 0; j < state->cus[i].size; j++) {
struct drgn_dwarf_index_pending_cu *pending_cu =
&state->cus[i].data[j];
dindex->cus.data[dindex->cus.size++] = (struct drgn_dwarf_index_cu){
.module = pending_cu->module,
.buf = pending_cu->buf,
.len = pending_cu->len,
.is_64_bit = pending_cu->is_64_bit,
.is_type_unit =
pending_cu->scn == DRGN_SCN_DEBUG_TYPES,
};
}
}
struct drgn_error *err = NULL;
#pragma omp parallel for schedule(dynamic)
for (size_t i = old_cus_size; i < dindex->cus.size; i++) {
if (err)
continue;
struct drgn_dwarf_index_cu *cu = &dindex->cus.data[i];
struct drgn_dwarf_index_cu_buffer cu_buffer;
drgn_dwarf_index_cu_buffer_init(&cu_buffer, cu);
struct drgn_error *cu_err = read_cu(&cu_buffer);
if (!cu_err)
cu_err = index_cu_first_pass(state->dindex, &cu_buffer);
if (cu_err) {
#pragma omp critical(drgn_dwarf_index_update_end_error)
if (err)
drgn_error_destroy(cu_err);
else
err = cu_err;
}
}
if (err)
goto err;
#pragma omp parallel for schedule(dynamic)
for (size_t i = state->old_cus_size; i < dindex->cus.size; i++) {
if (drgn_dwarf_index_update_cancelled(state))
for (size_t i = old_cus_size; i < dindex->cus.size; i++) {
if (err)
continue;
struct drgn_dwarf_index_cu *cu = &dindex->cus.data[i];
struct drgn_dwarf_index_cu_buffer buffer;
@ -1710,20 +1745,22 @@ drgn_dwarf_index_update_end(struct drgn_dwarf_index_update_state *state)
buffer.bb.pos += cu->is_64_bit ? 16 : 12;
struct drgn_error *cu_err =
index_cu_second_pass(&dindex->global, &buffer);
if (cu_err)
drgn_dwarf_index_update_cancel(state, cu_err);
if (cu_err) {
#pragma omp critical(drgn_dwarf_index_update_end_error)
if (err)
drgn_error_destroy(cu_err);
else
err = cu_err;
}
}
if (state->err) {
drgn_dwarf_index_rollback(state->dindex);
goto err;
}
return NULL;
if (err) {
drgn_dwarf_index_rollback(dindex);
err:
for (size_t i = state->old_cus_size; i < dindex->cus.size; i++)
drgn_dwarf_index_cu_deinit(&dindex->cus.data[i]);
dindex->cus.size = state->old_cus_size;
return state->err;
for (size_t i = old_cus_size; i < dindex->cus.size; i++)
drgn_dwarf_index_cu_deinit(&dindex->cus.data[i]);
dindex->cus.size = old_cus_size;
}
return err;
}
static struct drgn_error *index_namespace(struct drgn_dwarf_index_namespace *ns)

View File

@ -25,6 +25,14 @@ typedef struct {} omp_lock_t;
#define omp_destroy_lock(lock) do {} while (0)
#define omp_set_lock(lock) do {} while (0)
#define omp_unset_lock(lock) do {} while (0)
static inline int omp_get_thread_num(void)
{
return 0;
}
static inline int omp_get_max_threads(void)
{
return 1;
}
#endif
#include "hash_table.h"
@ -183,78 +191,43 @@ void drgn_dwarf_index_init(struct drgn_dwarf_index *dindex);
*/
void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex);
DEFINE_VECTOR_TYPE(drgn_dwarf_index_pending_cu_vector,
struct drgn_dwarf_index_pending_cu)
/** State tracked while updating a @ref drgn_dwarf_index. */
struct drgn_dwarf_index_update_state {
struct drgn_dwarf_index *dindex;
size_t old_cus_size;
struct drgn_error *err;
/** Per-thread arrays of CUs to be indexed. */
struct drgn_dwarf_index_pending_cu_vector *cus;
size_t max_threads;
};
/**
* Prepare to update a @ref drgn_dwarf_index.
* Initialize state for updating a @ref drgn_dwarf_index.
*
* @param[out] state Initialized update state. Must be passed to @ref
* drgn_dwarf_index_update_end().
* @return @c true on success, @c false on failure to allocate memory.
*/
void drgn_dwarf_index_update_begin(struct drgn_dwarf_index_update_state *state,
bool
drgn_dwarf_index_update_state_init(struct drgn_dwarf_index_update_state *state,
struct drgn_dwarf_index *dindex);
/** Deinitialize state for updating a @ref drgn_dwarf_index. */
void
drgn_dwarf_index_update_state_deinit(struct drgn_dwarf_index_update_state *state);
/** Read a module for updating a @ref drgn_dwarf_index. */
struct drgn_error *
drgn_dwarf_index_read_module(struct drgn_dwarf_index_update_state *state,
struct drgn_debug_info_module *module);
/**
* Finish updating a @ref drgn_dwarf_index.
* Update a @ref drgn_dwarf_index.
*
* This should be called once all of the tasks created by @ref
* drgn_dwarf_index_read_module() have completed (even if the update was
* cancelled).
*
* If the update was not cancelled, this finishes indexing all modules reported
* by @ref drgn_dwarf_index_read_module(). If it was cancelled or there is an
* error while indexing, this rolls back the index and removes the newly
* reported modules.
*
* @return @c NULL on success, non-@c NULL if the update was cancelled or there
* was another error.
* This should be called once all modules have been read with @ref
* drgn_dwarf_index_read_module() to finish indexing those modules.
*/
struct drgn_error *
drgn_dwarf_index_update_end(struct drgn_dwarf_index_update_state *state);
/**
* Cancel an update of a @ref drgn_dwarf_index.
*
* This should be called if there is a fatal error and the update must be
* aborted.
*
* @param[in] err Error to report. This will be returned from @ref
* drgn_dwarf_index_update_end(). If an error has already been reported, this
* error is destroyed.
*/
void drgn_dwarf_index_update_cancel(struct drgn_dwarf_index_update_state *state,
struct drgn_error *err);
/**
* Return whether an update of a @ref drgn_dwarf_index has been cancelled by
* @ref drgn_dwarf_index_update_cancel().
*
* Because updating is parallelized, this allows tasks other than the one that
* encountered the error to "fail fast".
*/
static inline bool
drgn_dwarf_index_update_cancelled(struct drgn_dwarf_index_update_state *state)
{
/*
* No need for omp critical/omp atomic since this is a best-effort
* optimization.
*/
return state->err != NULL;
}
/**
* Read a module for updating a @ref drgn_dwarf_index.
*
* This creates OpenMP tasks to begin indexing the module. It may cancel the
* update.
*/
void drgn_dwarf_index_read_module(struct drgn_dwarf_index_update_state *state,
struct drgn_debug_info_module *module);
drgn_dwarf_index_update(struct drgn_dwarf_index_update_state *state);
/**
* Iterator over DWARF debugging information.