mirror of
https://github.com/openharmony/third_party_elfutils.git
synced 2026-07-20 23:45:11 -04:00
libdwfl: Add dwfl_getthread_frames.
dwfl_getthread_frames is a convenience function for when the user is only interested in one specific thread id of a process. It can be implemented by a simple wrapper function that removes an extra callback layer just to filter on thread id. But it also provides an optimized path to getting access to just one particular Dwfl_Thread of the Dwfl process by providing and (optional) new callback for the state provider. The pid_thread_callbacks now provide an (optional) pid_getthread that doesn't need to travers all threads anymore. Which is implemented for the linux-pid-attach provider. stack now uses this to implement a new '-1' option that shows just one specific thread of a process. Signed-off-by: Mark Wielaard <mjw@redhat.com>
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
2013-12-20 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* NEWS (libdwfl): Add dwfl_getthread_frames.
|
||||
(stack): New entry.
|
||||
|
||||
2013-12-18 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* NEWS (libdwfl): Add dwfl_module_getsym_info and
|
||||
|
||||
@@ -7,10 +7,13 @@ libdwfl: dwfl_core_file_report has new parameter executable.
|
||||
Dwfl_Thread and Dwfl_Frame and functions dwfl_attach_state,
|
||||
dwfl_pid, dwfl_thread_dwfl, dwfl_thread_tid, dwfl_frame_thread,
|
||||
dwfl_thread_state_registers, dwfl_thread_state_register_pc,
|
||||
dwfl_getthreads, dwfl_thread_getframes and dwfl_frame_pc.
|
||||
dwfl_getthread_frames, dwfl_getthreads, dwfl_thread_getframes
|
||||
and dwfl_frame_pc.
|
||||
|
||||
addr2line: New option -x to show the section an address was found in.
|
||||
|
||||
stack: New utility that uses the new unwinder for processes and cores.
|
||||
|
||||
Version 0.157
|
||||
|
||||
libdw: Add new functions dwarf_getlocations, dwarf_getlocation_attr
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2013-12-20 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* libdw.map (ELFUTILS_0.158): Add dwfl_getthread_frames.
|
||||
|
||||
2013-12-18 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* libdw.map (ELFUTILS_0.158): Remove dwfl_module_addrsym_elf and
|
||||
|
||||
@@ -280,6 +280,7 @@ ELFUTILS_0.158 {
|
||||
dwfl_frame_thread;
|
||||
dwfl_thread_state_registers;
|
||||
dwfl_thread_state_register_pc;
|
||||
dwfl_getthread_frames;
|
||||
dwfl_getthreads;
|
||||
dwfl_thread_getframes;
|
||||
dwfl_frame_pc;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
2013-12-20 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* dwfl_frame.c (one_arg): New struct.
|
||||
(get_one_thread_cb): New function.
|
||||
(dwfl_getthread): Likewise.
|
||||
(one_thread): New struct.
|
||||
(get_one_thread_frames_cb): New function.
|
||||
(dwfl_getthread_frames): Likewise.
|
||||
* libdwfl.h (Dwfl_Thread_Callbacks): Add get_thread function.
|
||||
(dwfl_getthread_frames): Likewise.
|
||||
* libdwflP.h (dwfl_getthread_frames): New internal function declaration.
|
||||
* linux-core-attach.c (core_thread_callbacks): Initialize get_thread
|
||||
to NULL.
|
||||
* linux-pid-attach.c (pid_getthread): New function.
|
||||
(pid_thread_callbacks): Initialize get_thread to pid_getthread.
|
||||
|
||||
2013-12-20 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* linux-kernel-modules.c (report_kernel_archive): Correct nested
|
||||
|
||||
@@ -273,6 +273,103 @@ dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
|
||||
}
|
||||
INTDEF(dwfl_getthreads)
|
||||
|
||||
struct one_arg
|
||||
{
|
||||
pid_t tid;
|
||||
bool seen;
|
||||
int (*callback) (Dwfl_Thread *thread, void *arg);
|
||||
void *arg;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static int
|
||||
get_one_thread_cb (Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct one_arg *oa = (struct one_arg *) arg;
|
||||
if (! oa->seen && INTUSE(dwfl_thread_tid) (thread) == oa->tid)
|
||||
{
|
||||
oa->seen = true;
|
||||
oa->ret = oa->callback (thread, oa->arg);
|
||||
return DWARF_CB_ABORT;
|
||||
}
|
||||
|
||||
return DWARF_CB_OK;
|
||||
}
|
||||
|
||||
/* Note not currently exported, will be when there are more Dwfl_Thread
|
||||
properties to query. Use dwfl_getthread_frames for now directly. */
|
||||
static int
|
||||
getthread (Dwfl *dwfl, pid_t tid,
|
||||
int (*callback) (Dwfl_Thread *thread, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
Dwfl_Process *process = dwfl->process;
|
||||
if (process == NULL)
|
||||
{
|
||||
__libdwfl_seterrno (dwfl->process_attach_error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (process->callbacks->get_thread != NULL)
|
||||
{
|
||||
Dwfl_Thread thread;
|
||||
thread.process = process;
|
||||
thread.unwound = NULL;
|
||||
thread.callbacks_arg = NULL;
|
||||
|
||||
if (process->callbacks->get_thread (dwfl, tid, process->callbacks_arg,
|
||||
&thread.callbacks_arg))
|
||||
{
|
||||
int err;
|
||||
thread.tid = tid;
|
||||
err = callback (&thread, arg);
|
||||
thread_free_all_states (&thread);
|
||||
return err;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct one_arg oa = { .tid = tid, .callback = callback,
|
||||
.arg = arg, .seen = false };
|
||||
int err = INTUSE(dwfl_getthreads) (dwfl, get_one_thread_cb, &oa);
|
||||
|
||||
if (err == DWARF_CB_ABORT && oa.seen)
|
||||
return oa.ret;
|
||||
|
||||
if (err == DWARF_CB_OK && ! oa.seen)
|
||||
{
|
||||
errno = ESRCH;
|
||||
__libdwfl_seterrno (DWFL_E_ERRNO);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct one_thread
|
||||
{
|
||||
int (*callback) (Dwfl_Frame *frame, void *arg);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static int
|
||||
get_one_thread_frames_cb (Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct one_thread *ot = (struct one_thread *) arg;
|
||||
return INTUSE(dwfl_thread_getframes) (thread, ot->callback, ot->arg);
|
||||
}
|
||||
|
||||
int
|
||||
dwfl_getthread_frames (Dwfl *dwfl, pid_t tid,
|
||||
int (*callback) (Dwfl_Frame *frame, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
struct one_thread ot = { .callback = callback, .arg = arg };
|
||||
return getthread (dwfl, tid, get_one_thread_frames_cb, &ot);
|
||||
}
|
||||
INTDEF(dwfl_getthread_frames)
|
||||
|
||||
int
|
||||
dwfl_thread_getframes (Dwfl_Thread *thread,
|
||||
int (*callback) (Dwfl_Frame *state, void *arg),
|
||||
|
||||
@@ -662,6 +662,17 @@ typedef struct
|
||||
pid_t (*next_thread) (Dwfl *dwfl, void *dwfl_arg, void **thread_argp)
|
||||
__nonnull_attribute__ (1);
|
||||
|
||||
/* Called to get a specific thread. Returns true if there is a
|
||||
thread with the given thread id number, returns false if no such
|
||||
thread exists and will set dwfl_errno in that case. THREAD_ARGP
|
||||
is never NULL. *THREAD_ARGP will be passed to
|
||||
set_initial_registers or thread_detach callbacks together with
|
||||
Dwfl_Thread *thread. This method may be NULL and will then be
|
||||
emulated using the next_thread callback. */
|
||||
bool (*get_thread) (Dwfl *dwfl, pid_t tid, void *dwfl_arg,
|
||||
void **thread_argp)
|
||||
__nonnull_attribute__ (1);
|
||||
|
||||
/* Called during unwinding to access memory (stack) state. Returns true for
|
||||
successfully read *RESULT or false and sets dwfl_errno () on failure.
|
||||
This method may be NULL - in such case dwfl_thread_getframes will return
|
||||
@@ -762,6 +773,16 @@ int dwfl_thread_getframes (Dwfl_Thread *thread,
|
||||
void *arg)
|
||||
__nonnull_attribute__ (1, 2);
|
||||
|
||||
/* Like dwfl_thread_getframes, but specifying the thread by its unique
|
||||
identifier number. Returns zero if all frames have been processed
|
||||
by the callback, returns -1 on error (and when no thread with
|
||||
the given thread id number exists), or the value of the callback
|
||||
when not DWARF_CB_OK. -1 returned on error will set dwfl_errno (). */
|
||||
int dwfl_getthread_frames (Dwfl *dwfl, pid_t tid,
|
||||
int (*callback) (Dwfl_Frame *thread, void *arg),
|
||||
void *arg)
|
||||
__nonnull_attribute__ (1, 3);
|
||||
|
||||
/* Return *PC (program counter) for thread-specific frame STATE.
|
||||
Set *ISACTIVATION according to DWARF frame "activation" definition.
|
||||
Typically you need to substract 1 from *PC if *ACTIVATION is false to safely
|
||||
|
||||
@@ -702,6 +702,7 @@ INTDECL (dwfl_thread_tid)
|
||||
INTDECL (dwfl_frame_thread)
|
||||
INTDECL (dwfl_thread_state_registers)
|
||||
INTDECL (dwfl_thread_state_register_pc)
|
||||
INTDECL (dwfl_getthread_frames)
|
||||
INTDECL (dwfl_getthreads)
|
||||
INTDECL (dwfl_thread_getframes)
|
||||
INTDECL (dwfl_frame_pc)
|
||||
|
||||
@@ -288,6 +288,7 @@ core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
|
||||
static const Dwfl_Thread_Callbacks core_thread_callbacks =
|
||||
{
|
||||
core_next_thread,
|
||||
NULL, /* get_thread */
|
||||
core_memory_read,
|
||||
core_set_initial_registers,
|
||||
core_detach,
|
||||
|
||||
@@ -201,6 +201,20 @@ pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
|
||||
return tid;
|
||||
}
|
||||
|
||||
/* Just checks that the thread id exists. */
|
||||
static bool
|
||||
pid_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
|
||||
void *dwfl_arg, void **thread_argp)
|
||||
{
|
||||
*thread_argp = dwfl_arg;
|
||||
if (kill (tid, 0) < 0)
|
||||
{
|
||||
__libdwfl_seterrno (DWFL_E_ERRNO);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Implement the ebl_set_initial_registers_tid setfunc callback. */
|
||||
|
||||
static bool
|
||||
@@ -260,6 +274,7 @@ pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
|
||||
static const Dwfl_Thread_Callbacks pid_thread_callbacks =
|
||||
{
|
||||
pid_next_thread,
|
||||
pid_getthread,
|
||||
pid_memory_read,
|
||||
pid_set_initial_registers,
|
||||
pid_detach,
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
2013-12-20 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* stack.c (show_one_tid): New static boolean.
|
||||
(parse_opt): Handle '-1'.
|
||||
(main): Add -1 to options. Call dwfl_getthread_frames when
|
||||
show_one_tid is true.
|
||||
|
||||
2013-12-18 Mark Wielaard <mjw@redhat.com>
|
||||
|
||||
* addr2line.c (options): Add symbol-sections, 'x'.
|
||||
|
||||
+36
-10
@@ -39,6 +39,7 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
|
||||
static bool show_activation = false;
|
||||
static bool show_module = false;
|
||||
static bool show_source = false;
|
||||
static bool show_one_tid = false;
|
||||
|
||||
static int
|
||||
frame_callback (Dwfl_Frame *state, void *arg)
|
||||
@@ -170,6 +171,10 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
|
||||
show_activation = show_source = show_module = true;
|
||||
break;
|
||||
|
||||
case '1':
|
||||
show_one_tid = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
@@ -197,6 +202,8 @@ main (int argc, char **argv)
|
||||
N_("Additionally show source file information"), 0 },
|
||||
{ "verbose", 'v', NULL, 0,
|
||||
N_("Show all additional information (activation, module and source)"), 0 },
|
||||
{ NULL, '1', NULL, 0,
|
||||
N_("Show the backtrace of only one thread"), 0 },
|
||||
{ NULL, 0, NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@@ -221,23 +228,42 @@ Only real user processes are supported, no kernel or process maps."),
|
||||
argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
|
||||
assert (dwfl != NULL);
|
||||
if (remaining != argc)
|
||||
error (2, 0, "eu-stack [-a] [-m] [-s] [-v] [--debuginfo-path=<path>] {-p <process id>|"
|
||||
"--core=<file> [--executable=<file>]|--help}");
|
||||
error (2, 0, "eu-stack [-a] [-m] [-s] [-v] [-1] [--debuginfo-path=<path>]"
|
||||
" {-p <process id>|--core=<file> [--executable=<file>]|--help}");
|
||||
|
||||
/* dwfl_linux_proc_report has been already called from dwfl_standard_argp's
|
||||
parse_opt function. */
|
||||
if (dwfl_report_end (dwfl, NULL, NULL) != 0)
|
||||
error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
|
||||
|
||||
switch (dwfl_getthreads (dwfl, thread_callback, NULL))
|
||||
if (show_one_tid)
|
||||
{
|
||||
case DWARF_CB_OK:
|
||||
break;
|
||||
case -1:
|
||||
error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
pid_t tid = dwfl_pid (dwfl);
|
||||
unsigned frameno = 0;
|
||||
switch (dwfl_getthread_frames (dwfl, tid, frame_callback, &frameno))
|
||||
{
|
||||
case DWARF_CB_OK:
|
||||
break;
|
||||
case -1:
|
||||
error (0, 0, "dwfl_getthread_frames (%d): %s", tid,
|
||||
dwfl_errmsg (-1));
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (dwfl_getthreads (dwfl, thread_callback, NULL))
|
||||
{
|
||||
case DWARF_CB_OK:
|
||||
break;
|
||||
case -1:
|
||||
error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
dwfl_end (dwfl);
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ set_initial_registers (Dwfl_Thread *thread,
|
||||
static const Dwfl_Thread_Callbacks callbacks =
|
||||
{
|
||||
next_thread,
|
||||
NULL, /* get_thread */
|
||||
memory_read,
|
||||
set_initial_registers,
|
||||
NULL, /* detach */
|
||||
|
||||
Reference in New Issue
Block a user