While playing with the new unwinder interfaces I noticed that I was
writing the same extra wrapper callbacks every time. Often one has,
through some other mechanism, a specific thread id that one is interested
in. Then you write a dwfl_getthreads callback just to filter out that one
tid. So this patch adds such convenience wrappers, but also adds a
new callback for a backend to easily get at a specific thread. The
pid backend implements this optimization by directly checking the tid
exists instead of iterating through them all.
This is also useful for eu-stack where it is often convenient to get
the stack of just one thread. That is now implemented through the
new interface when giving "-1" as extra argument.
<- snip ->
dwfl_getthread and dwfl_getthread_frames are convenience functions for
when the user is only interested in one specific thread id of a process.
The can be implemented by just simple wrapper functions that remove and
extra callback layer just to filter on thread id. But they also provide
an optimized path to getting access to just one Dwfl_Thread by providing
and (optional) new callback for provider. The pid_thread_callbacks now
provide pid_getthread that doesn't need to travers all threads anymore.
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(a)redhat.com>
---
ChangeLog | 5 ++
NEWS | 5 ++-
libdw/ChangeLog | 5 ++
libdw/libdw.map | 2 +
libdwfl/ChangeLog | 18 +++++++++
libdwfl/dwfl_frame.c | 86 +++++++++++++++++++++++++++++++++++++++++++
libdwfl/libdwfl.h | 31 +++++++++++++++
libdwfl/libdwflP.h | 2 +
libdwfl/linux-core-attach.c | 1 +
libdwfl/linux-pid-attach.c | 12 ++++++
src/ChangeLog | 7 +++
src/stack.c | 46 ++++++++++++++++++-----
tests/backtrace-data.c | 1 +
13 files changed, 210 insertions(+), 11 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index fe109e1..503b95c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
2013-12-18 Mark Wielaard <mjw(a)redhat.com>
+ * NEWS (libdwfl): Add dwfl_getthread and dwfl_getthread_frames.
+ (stack): New entry.
+
+2013-12-18 Mark Wielaard <mjw(a)redhat.com>
+
* NEWS (libdwfl): Add dwfl_module_getsym_info and
dwfl_module_addrinfo.
(addr2line): Add -x option.
diff --git a/NEWS b/NEWS
index 49510f7..b22578f 100644
--- a/NEWS
+++ b/NEWS
@@ -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, 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
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index ef3ed57..2cb8c0c 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2013-12-20 Mark Wielaard <mjw(a)redhat.com>
+
+ * libdw.map (ELFUTILS_0.158): Add dwfl_getthread and
+ dwfl_getthread_frames.
+
2013-12-18 Mark Wielaard <mjw(a)redhat.com>
* libdw.map (ELFUTILS_0.158): Remove dwfl_module_addrsym_elf and
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 92392bc..cb8a146 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -280,6 +280,8 @@ ELFUTILS_0.158 {
dwfl_frame_thread;
dwfl_thread_state_registers;
dwfl_thread_state_register_pc;
+ dwfl_getthread;
+ dwfl_getthread_frames;
dwfl_getthreads;
dwfl_thread_getframes;
dwfl_frame_pc;
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index c3569aa..07667ef 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,21 @@
+2013-12-20 Mark Wielaard <mjw(a)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): New function definition.
+ (dwfl_getthread_frames): Likewise.
+ * libdwflP.h (dwfl_getthread): New internal function declaration.
+ (dwfl_getthread_frames): Likewise.
+ * 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-18 Mark Wielaard <mjw(a)redhat.com>
* derelocate.c (__libdwfl_find_section_ndx): New internal function.
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
index 4a7b3cd..fbc603b 100644
--- a/libdwfl/dwfl_frame.c
+++ b/libdwfl/dwfl_frame.c
@@ -273,6 +273,92 @@ 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;
+};
+
+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;
+ return oa->callback (thread, oa->arg);
+ }
+
+ return DWARF_CB_OK;
+}
+
+int
+dwfl_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_OK && ! oa.seen)
+ return -1;
+
+ return err;
+}
+INTDEF(dwfl_getthread)
+
+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 INTUSE(dwfl_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),
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 007089b..aae2b64 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -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 may 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
@@ -748,6 +759,16 @@ int dwfl_getthreads (Dwfl *dwfl,
void *arg)
__nonnull_attribute__ (1, 2);
+/* Like dwfl_getthreads, but only for one specific thread with the
+ given thead id number if it exists. Returns zero on success,
+ 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 (Dwfl *dwfl, pid_t tid,
+ int (*callback) (Dwfl_Thread *thread, void *arg),
+ void *arg)
+ __nonnull_attribute__ (1, 3);
+
/* Iterate through the frames for a thread. Returns zero if all frames
have been processed by the callback, returns -1 on error, or the value of
the callback when not DWARF_CB_OK. -1 returned on error will
@@ -762,6 +783,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
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 267b021..8a53fca 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -702,6 +702,8 @@ INTDECL (dwfl_thread_tid)
INTDECL (dwfl_frame_thread)
INTDECL (dwfl_thread_state_registers)
INTDECL (dwfl_thread_state_register_pc)
+INTDECL (dwfl_getthread)
+INTDECL (dwfl_getthread_frames)
INTDECL (dwfl_getthreads)
INTDECL (dwfl_thread_getframes)
INTDECL (dwfl_frame_pc)
diff --git a/libdwfl/linux-core-attach.c b/libdwfl/linux-core-attach.c
index f55faf7..f259b2c 100644
--- a/libdwfl/linux-core-attach.c
+++ b/libdwfl/linux-core-attach.c
@@ -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,
diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c
index 3d0716a..13e60f8 100644
--- a/libdwfl/linux-pid-attach.c
+++ b/libdwfl/linux-pid-attach.c
@@ -201,6 +201,17 @@ pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
return tid;
}
+/* Just checks that the thread id status file (currently) exists. */
+static bool
+pid_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
+ void *dwfl_arg, void **thread_argp)
+{
+ char fbuf[36];
+ snprintf (fbuf, sizeof (fbuf), "/proc/%ld/status", (long) tid);
+ *thread_argp = dwfl_arg;
+ return access (fbuf, R_OK) == 0;
+}
+
/* Implement the ebl_set_initial_registers_tid setfunc callback. */
static bool
@@ -260,6 +271,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,
diff --git a/src/ChangeLog b/src/ChangeLog
index 5722a50..0e6d2dc 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2013-12-20 Mark Wielaard <mjw(a)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(a)redhat.com>
* addr2line.c (options): Add symbol-sections, 'x'.
diff --git a/src/stack.c b/src/stack.c
index 242d393..512c85b 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -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);
diff --git a/tests/backtrace-data.c b/tests/backtrace-data.c
index 6d21345..9fa3c4a 100644
--- a/tests/backtrace-data.c
+++ b/tests/backtrace-data.c
@@ -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 */
--
1.7.1