Hi Serhei,
On Thu, Jan 29, 2026 at 12:32 PM Serhei Makarov <[email protected]> wrote:
>
> In commit 3ce0d5ed, I missed the fact that
> dwflst_perf_sample_getframes needs to handle the case of an unattached
> Dwfl, when dwfl->process->ebl is not yet available to translate the
> registers. Thus, it can't be a straightforward wrapper of
> dwfl_sample_getframes, but should instead handle the attaching logic
> identically to that function.
>
> * libdwfl_stacktrace (dwflst_perf_sample_getframes): Implement
> attaching the Dwfl identically to dwflst_sample_getframes.
>
> Signed-off-by: Serhei Makarov <[email protected]>
> ---
> libdwfl_stacktrace/dwflst_sample_frame.c | 49 +++++++++++++++++++-----
> 1 file changed, 39 insertions(+), 10 deletions(-)
>
> diff --git a/libdwfl_stacktrace/dwflst_sample_frame.c
> b/libdwfl_stacktrace/dwflst_sample_frame.c
> index 090d3220..9f527579 100644
> --- a/libdwfl_stacktrace/dwflst_sample_frame.c
> +++ b/libdwfl_stacktrace/dwflst_sample_frame.c
> @@ -259,26 +259,55 @@ dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf,
> int (*callback) (Dwfl_Frame *state, void *arg),
> void *arg)
> {
> + /* TODO: Lock the dwfl to ensure attach_state does not interfere
> + with other dwfl_perf_sample_getframes calls. */
> +
> + struct sample_info *sample_arg;
> + bool attached = false;
> + if (dwfl->process != NULL)
> + {
> + sample_arg = dwfl->process->callbacks_arg;
> + attached = true;
> + }
> + else
> + {
> + sample_arg = malloc (sizeof *sample_arg);
> + if (sample_arg == NULL)
> + {
> + __libdwfl_seterrno(DWFL_E_NOMEM);
> + return -1;
> + }
> + }
> +
> + sample_arg->pid = pid;
> + sample_arg->tid = tid;
> + sample_arg->stack = (const uint8_t *)stack;
> + sample_arg->stack_size = stack_size;
> + sample_arg->regs = regs;
> + sample_arg->n_regs = n_regs;
> +
> + if (! attached
> + && ! INTUSE(dwfl_attach_state) (dwfl, elf, pid,
> + &sample_thread_callbacks, sample_arg))
> + return -1;
It looks like this return leaks sample_arg. You may need to also free
it for the error returns below.
Aaron
> +
> /* Select the regs_mapping based on architecture. This will be
> cached in ebl to avoid having to recompute the regs_mapping array
> when perf_regs_mask is consistent for the entire session: */
> - const int *regs_mapping;
> - size_t n_regs_mapping;
> Dwfl_Process *process = dwfl->process;
> Ebl *ebl = process->ebl;
> - /* XXX May want to check if abi matches ebl_get_elfclass(ebl). */
> if (!ebl_sample_perf_regs_mapping(ebl,
> perf_regs_mask, abi,
> - ®s_mapping, &n_regs_mapping))
> + &sample_arg->regs_mapping,
> &sample_arg->n_regs_mapping))
> {
> __libdwfl_seterrno(DWFL_E_LIBEBL_BAD);
> return -1;
> }
> + sample_arg->elfclass = ebl_get_elfclass(ebl);
> + ebl_sample_sp_pc(ebl, regs, n_regs,
> + sample_arg->regs_mapping, sample_arg->n_regs_mapping,
> + &sample_arg->base_addr, &sample_arg->pc);
>
> - /* Then we can call dwflst_sample_getframes: */
> - return dwflst_sample_getframes (dwfl, elf, pid, tid,
> - stack, stack_size,
> - regs, n_regs,
> - regs_mapping, n_regs_mapping,
> - callback, arg);
> + /* XXX May want to check if abi matches ebl_get_elfclass(ebl). */
> + return INTUSE(dwfl_getthread_frames) (dwfl, tid, callback, arg);
> }
> --
> 2.52.0
>