Changes for v3:
- Fix initialization of elf in sysprof_init_dwfl
(previously triggered -Wmaybe-uninitialized).
* * *
Remove the code from src/stacktrace.c that is now covered by
libdwfl/dwfl_perf_frame.c and dwfl_perf_sample_getframes.
* src/stacktrace.c (show_memory_reads): Remove this verbose option as
the relevant code is inside libdwfl now.
(struct __sample_arg): Remove, handled by libdwfl/dwfl_perf_frame.c.
(sample_next_thread): Ditto.
(sample_getthread): Ditto.
(copy_word_64): Ditto.
(copy_word_32): Ditto.
(elf_memory_read): Ditto.
(sample_memory_read): Ditto.
(sample_set_initial_registers): Ditto.
(sample_detach): Ditto.
(sample_thread_callbacks): Ditto.
(sysprof_find_dwfl): Now also return the Elf* so that it can be
passed to dwfl_perf_sample_getframes. Don't create sample_arg. Do
record sp in sui->last_sp. Don't dwfl_attach_state,
dwfl_perf_sample_getframes handles that now.
(sysprof_unwind_cb): Adapt to sysprof_find_dwfl changes,
now invoke dwfl_perf_sample_getframes instead of
dwfl_getthread_frames.
(parse_opt): Remove show_memory_reads.
(main): Remove show_memory_reads.
---
src/stacktrace.c | 230 +++++++----------------------------------------
1 file changed, 30 insertions(+), 200 deletions(-)
diff --git a/src/stacktrace.c b/src/stacktrace.c
index 89e1866e..b9675db6 100644
--- a/src/stacktrace.c
+++ b/src/stacktrace.c
@@ -96,8 +96,7 @@
#include ELFUTILS_HEADER(ebl)
/* #include ELFUTILS_HEADER(dwfl) */
#include "../libdwfl/libdwflP.h"
-/* XXX: Private header needed for find_procfile, sysprof_find_dwfl,
- sample_set_initial_registers. */
+/* XXX: Private header needed for find_procfile. */
/*************************************
* Includes: sysprof data structures *
@@ -176,11 +175,13 @@ static int processing_mode = MODE_NAIVE;
static int input_format;
static int output_format = FORMAT_SYSPROF;
+/* XXX Used to decide regs_mask for dwfl_perf_sample_getframes. */
+Ebl *default_ebl = NULL;
+
/* non-printable argp options. */
#define OPT_DEBUG 0x100
/* Diagnostic options. */
-static bool show_memory_reads = false;
static bool show_frames = false;
static bool show_samples = false;
static bool show_failures = false;
@@ -190,9 +191,6 @@ static bool show_summary = true;
#define ELFUTILS_STACKTRACE_VERBOSE_ENV_VAR "ELFUTILS_STACKTRACE_VERBOSE"
/* Valid values that turn on diagnostics: 'true', 'verbose', 'debug', '1',
'2'. */
-/* Enables detailed tracing of simulated memory reads: */
-/* #define DEBUG_MEMORY_READ */
-
/* Enables even more diagnostics on modules: */
/* #define DEBUG_MODULES */
@@ -469,144 +467,6 @@ sysprof_reader_getframes (SysprofReader *reader,
#endif /* HAVE_SYSPROF_HEADERS */
-/*******************************************
- * Memory read interface for stack samples *
- *******************************************/
-
-/* TODO: elfutils (internal) libraries use libNN_set_errno and DWFL_E_WHATEVER;
- this code fails silently in sample_getthread. */
-
-struct __sample_arg
-{
- int tid;
- Dwarf_Addr base_addr;
- uint64_t size;
- uint8_t *data;
- uint64_t n_regs;
- uint64_t abi; /* PERF_SAMPLE_REGS_ABI_{32,64} */
- Dwarf_Addr pc;
- Dwarf_Addr sp;
- Dwarf_Addr *regs;
-};
-
-/* The next few functions imitate the corefile interface for a single
- stack sample, with very restricted access to registers and memory. */
-
-/* Just yield the single thread id matching the sample. */
-static pid_t
-sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
- void **thread_argp)
-{
- struct __sample_arg *sample_arg = (struct __sample_arg *)dwfl_arg;
- if (*thread_argp == NULL)
- {
- *thread_argp = (void *)0xea7b3375;
- return sample_arg->tid;
- }
- else
- return 0;
-}
-
-/* Just check that the thread id matches the sample. */
-static bool
-sample_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
- void *dwfl_arg, void **thread_argp)
-{
- struct __sample_arg *sample_arg = (struct __sample_arg *)dwfl_arg;
- *thread_argp = (void *)sample_arg;
- if (sample_arg->tid != tid)
- {
- return false;
- }
- return true;
-}
-
-#define copy_word_64(result, d) \
- if ((((uintptr_t) (d)) & (sizeof (uint64_t) - 1)) == 0) \
- *(result) = *(uint64_t *)(d); \
- else \
- memcpy ((result), (d), sizeof (uint64_t));
-
-#define copy_word_32(result, d) \
- if ((((uintptr_t) (d)) & (sizeof (uint32_t) - 1)) == 0) \
- *(result) = *(uint32_t *)(d); \
- else \
- memcpy ((result), (d), sizeof (uint32_t));
-
-#define copy_word(result, d, abi) \
- if ((abi) == PERF_SAMPLE_REGS_ABI_64) \
- { copy_word_64((result), (d)); } \
- else if ((abi) == PERF_SAMPLE_REGS_ABI_32) \
- { copy_word_32((result), (d)); } \
- else \
- *(result) = 0;
-
-static bool
-elf_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
-{
- struct __sample_arg *sample_arg = (struct __sample_arg *)arg;
- Dwfl_Module *mod = dwfl_addrmodule(dwfl, addr);
- Dwarf_Addr bias;
- Elf_Scn *section = dwfl_module_address_section(mod, &addr, &bias);
-
- if (!section)
- return false;
-
- Elf_Data *data = elf_getdata(section, NULL);
- if (data && data->d_buf && data->d_size > addr) {
- uint8_t *d = ((uint8_t *)data->d_buf) + addr;
- copy_word(result, d, sample_arg->abi);
- return true;
- }
- return false;
-}
-
-static bool
-sample_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
-{
- struct __sample_arg *sample_arg = (struct __sample_arg *)arg;
- if (show_memory_reads)
- fprintf(stderr, "* memory_read addr=%lx+(%lx) size=%lx\n",
- sample_arg->base_addr, addr - sample_arg->base_addr,
sample_arg->size);
- /* Imitate read_cached_memory() with the stack sample data as the cache. */
- if (addr < sample_arg->base_addr || addr - sample_arg->base_addr >=
sample_arg->size)
- return elf_memory_read(dwfl, addr, result, arg);
- uint8_t *d = &sample_arg->data[addr - sample_arg->base_addr];
- copy_word(result, d, sample_arg->abi);
- return true;
-}
-
-static bool
-sample_set_initial_registers (Dwfl_Thread *thread, void *arg)
-{
- struct __sample_arg *sample_arg = (struct __sample_arg *)arg;
- dwfl_thread_state_register_pc (thread, sample_arg->pc);
- Dwfl_Process *process = thread->process;
- Ebl *ebl = process->ebl;
- /* XXX Sysprof provides exactly the required registers for unwinding: */
- uint64_t regs_mask = ebl_perf_frame_regs_mask (ebl);
- return ebl_set_initial_registers_sample
- (ebl, sample_arg->regs, sample_arg->n_regs, regs_mask, sample_arg->abi,
- dwfl_set_initial_registers_thread, thread);
-}
-
-static void
-sample_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
-{
- struct __sample_arg *sample_arg = (struct __sample_arg *)dwfl_arg;
- free (sample_arg);
-}
-
-static const Dwfl_Thread_Callbacks sample_thread_callbacks =
-{
- sample_next_thread,
- sample_getthread,
- sample_memory_read,
- sample_set_initial_registers,
- sample_detach,
- NULL, /* sample_thread_detach */
-};
-
/****************************************************
* Dwfl and statistics table for multiple processes *
****************************************************/
@@ -996,33 +856,30 @@ sysprof_init_dwfl (Dwfl_Process_Tracker *cb_tracker,
Dwfl *
sysprof_find_dwfl (struct sysprof_unwind_info *sui,
SysprofCaptureStackUser *ev,
- SysprofCaptureUserRegs *regs)
+ SysprofCaptureUserRegs *regs,
+ Elf **out_elf)
{
pid_t pid = ev->frame.pid;
- /* TODO: Need to generalize this code beyond x86 architectures. */
/* XXX: Note that sysprof requesting the x86_64 register file from
perf_events will result in an array of 17 regs even for 32-bit
applications. */
-#define EXPECTED_REGS 17
- if (regs->n_regs < EXPECTED_REGS) /* XXX expecting everything except FLAGS */
+ if (regs->n_regs < ebl_frame_nregs(default_ebl)) /* XXX expecting everything
except FLAGS */
{
if (show_failures)
- fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %d\n"),
- regs->n_regs, EXPECTED_REGS);
+ fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %ld\n"),
+ regs->n_regs, ebl_frame_nregs(default_ebl));
return NULL;
}
+ Elf *elf = NULL;
Dwfl *dwfl = dwfl_process_tracker_find_pid (tracker, pid, sysprof_init_dwfl,
NULL);
- struct __sample_arg *sample_arg;
bool cached = false;
if (dwfl != NULL && dwfl->process != NULL)
{
- sample_arg = dwfl->process->callbacks_arg; /* XXX requires libdwflP.h */
cached = true;
goto reuse;
}
- Elf *elf = NULL;
int elf_fd = -1;
int err = find_procfile (dwfl, &pid, &elf, &elf_fd);
if (err < 0)
@@ -1033,53 +890,22 @@ sysprof_find_dwfl (struct sysprof_unwind_info *sui,
return NULL;
}
- sample_arg = malloc (sizeof *sample_arg);
- if (sample_arg == NULL)
- {
- if (elf != NULL) {
- elf_end (elf);
- close (elf_fd);
- }
- free (sample_arg);
- return NULL;
- }
-
reuse:
- sample_arg->tid = ev->tid;
- sample_arg->size = ev->size;
- sample_arg->data = (uint8_t *)&ev->data;
- sample_arg->regs = regs->regs;
- sample_arg->n_regs = (uint64_t)regs->n_regs;
- sample_arg->abi = (uint64_t)regs->abi;
- /* TODO: Need to generalize this code beyond x86 architectures. */
- /* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h enum
perf_event_x86_regs: */
- sample_arg->sp = regs->regs[7];
- sample_arg->pc = regs->regs[8];
- sample_arg->base_addr = sample_arg->sp;
- sui->last_sp = sample_arg->base_addr;
- sui->last_base = sample_arg->base_addr;
+ /* TODO: Generalize to other architectures than x86_64. */
+ sui->last_sp = regs->regs[7];
+ sui->last_base = sui->last_sp;
if (show_frames) {
- bool is_abi32 = (sample_arg->abi == PERF_SAMPLE_REGS_ABI_32);
+ bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
fprintf(stderr, "sysprof_find_dwfl pid %lld%s: size=%ld%s pc=%lx
sp=%lx+(%lx)\n",
(long long) pid, cached ? " (cached)" : "",
- sample_arg->size, is_abi32 ? " (32-bit)" : "",
- sample_arg->pc, sample_arg->base_addr,
- sample_arg->sp - sample_arg->base_addr);
+ ev->size, is_abi32 ? " (32-bit)" : "",
+ regs->regs[8], sui->last_base, (long)0);
}
- if (!cached && ! dwfl_attach_state (dwfl, elf, pid,
&sample_thread_callbacks, sample_arg))
- {
- if (show_failures)
- fprintf(stderr, "dwfl_attach_state pid %lld: %s\n",
- (long long)pid, dwfl_errmsg(-1));
- elf_end (elf);
- close (elf_fd);
- free (sample_arg);
- return NULL;
- }
if (!cached)
pid_store_dwfl (pid, dwfl);
+ *out_elf = elf;
return dwfl;
}
@@ -1222,7 +1048,8 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
SysprofCaptureUserRegs *regs = (SysprofCaptureUserRegs *)tail_ptr;
if (show_frames)
fprintf(stderr, "\n"); /* extra newline for padding */
- Dwfl *dwfl = sysprof_find_dwfl (sui, ev, regs);
+ Elf *elf = NULL;
+ Dwfl *dwfl = sysprof_find_dwfl (sui, ev, regs, &elf);
if (dwfl == NULL)
{
if (show_summary)
@@ -1242,11 +1069,16 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void
*arg)
sui->last_dwfl = dwfl;
#endif
sui->last_pid = frame->pid;
- int rc = dwfl_getthread_frames (dwfl, ev->tid, sysprof_unwind_frame_cb, sui);
+ uint64_t regs_mask = ebl_perf_frame_regs_mask (default_ebl);
+ int rc = dwfl_perf_sample_getframes (dwfl, elf, frame->pid, ev->tid,
+ (uint8_t *)&ev->data, ev->size,
+ regs->regs, regs->n_regs,
+ regs_mask, regs->abi,
+ sysprof_unwind_frame_cb, sui);
if (rc < 0)
{
if (show_failures)
- fprintf(stderr, "dwfl_getthread_frames pid %lld: %s\n",
+ fprintf(stderr, "dwfl_perf_sample_getframes pid %lld: %s\n",
(long long)frame->pid, dwfl_errmsg(-1));
}
if (show_samples)
@@ -1362,9 +1194,6 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
break;
case OPT_DEBUG:
-#ifdef DEBUG_MEMORY_READ
- show_memory_reads = true;
-#endif
show_frames = true;
FALLTHROUGH;
case 'v':
@@ -1456,9 +1285,6 @@
https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu
else if (strcmp(env_verbose, "debug") == 0
|| strcmp(env_verbose, "2") == 0)
{
-#ifdef DEBUG_MEMORY_READ
- show_memory_reads = true;
-#endif
show_frames = true;
show_samples = true;
show_failures = true;
@@ -1524,6 +1350,10 @@
https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu
{
if (!dwfltab_init())
error (EXIT_BAD, errno, N_("Could not initialize Dwfl table"));
+
+ /* TODO: Generalize to other architectures. */
+ default_ebl = ebl_openbackend_machine(EM_X86_64);
+
struct sysprof_unwind_info sui;
sui.output_fd = output_fd;
sui.reader = reader;
--
2.47.0