+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
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
+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
dwfl_frame_thread;
dwfl_thread_state_registers;
dwfl_thread_state_register_pc;
+ dwfl_getthread_frames;
dwfl_getthreads;
dwfl_thread_getframes;
dwfl_frame_pc;
+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
}
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),
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
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
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)
static const Dwfl_Thread_Callbacks core_thread_callbacks =
{
core_next_thread,
+ NULL, /* get_thread */
core_memory_read,
core_set_initial_registers,
core_detach,
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
static const Dwfl_Thread_Callbacks pid_thread_callbacks =
{
pid_next_thread,
+ pid_getthread,
pid_memory_read,
pid_set_initial_registers,
pid_detach,
+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'.
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)
show_activation = show_source = show_module = true;
break;
+ case '1':
+ show_one_tid = true;
+ break;
+
default:
return ARGP_ERR_UNKNOWN;
}
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 }
};
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);
static const Dwfl_Thread_Callbacks callbacks =
{
next_thread,
+ NULL, /* get_thread */
memory_read,
set_initial_registers,
NULL, /* detach */