static char *demangle_buffer = NULL;
#endif
+/* Whether any frames have been shown at all. Determines exit status. */
+static bool frames_shown = false;
+
+/* Program exit codes. All frames shown without any errors is GOOD.
+ Some frames shown with some non-fatal errors is an ERROR. A fatal
+ error or no frames shown at all is BAD. A command line USAGE exit
+ is generated by argp_error. */
+#define EXIT_OK 0
+#define EXIT_ERROR 1
+#define EXIT_BAD 2
+#define EXIT_USAGE 64
+
static int
frame_callback (Dwfl_Frame *state, void *arg)
{
unsigned nr = frames->frames;
if (! dwfl_frame_pc (state, &frames->frame[nr].pc,
&frames->frame[nr].isactivation))
- {
- error (0, 0, "%s", dwfl_errmsg (-1));
- return DWARF_CB_ABORT;
- }
+ return -1;
frames->frames++;
if (frames->frames == maxframes)
}
static void
-print_frames (struct frames *frames)
+print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
{
+ if (frames->frames > 0)
+ frames_shown = true;
+
+ printf ("TID %d:\n", tid);
for (unsigned nr = 0; nr < frames->frames; nr++)
{
Dwarf_Addr pc = frames->frame[nr].pc;
}
printf ("\n");
}
+ if (dwflerr != 0)
+ {
+ if (frames->frames > 0)
+ {
+ unsigned nr = frames->frames - 1;
+ Dwarf_Addr pc = frames->frame[nr].pc;
+ bool isactivation = frames->frame[nr].isactivation;
+ Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ const char *mainfile = NULL;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, NULL,
+ NULL, &mainfile, NULL);
+ if (modname == NULL || modname[0] == '\0')
+ {
+ if (mainfile != NULL)
+ modname = mainfile;
+ else
+ modname = "<unknown>";
+ }
+ error (0, 0, "%s tid %d at 0x%" PRIx64 " in %s: %s", what, tid,
+ pc_adjusted, modname, dwfl_errmsg (dwflerr));
+ }
+ else
+ error (0, 0, "%s tid %d: %s", what, tid, dwfl_errmsg (dwflerr));
+ }
}
static int
thread_callback (Dwfl_Thread *thread, void *thread_arg)
{
- printf ("TID %ld:\n", (long) dwfl_thread_tid (thread));
struct frames *frames = (struct frames *) thread_arg;
+ pid_t tid = dwfl_thread_tid (thread);
+ int err = 0;
frames->frames = 0;
switch (dwfl_thread_getframes (thread, frame_callback, thread_arg))
{
case DWARF_CB_ABORT:
break;
case -1:
- error (0, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
+ err = dwfl_errno ();
break;
default:
abort ();
}
- print_frames (frames);
+ print_frames (frames, tid, err, "dwfl_thread_getframes");
return DWARF_CB_OK;
}
case OPT_COREFILE:
core_fd = open (arg, O_RDONLY);
if (core_fd < 0)
- error (2, errno, N_("Cannot open core file '%s'."), arg);
+ error (EXIT_BAD, errno, N_("Cannot open core file '%s'"), arg);
elf_version (EV_CURRENT);
core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
if (core == NULL)
- error (2, 0, "core '%s' elf_begin: %s", arg, elf_errmsg(-1));
+ error (EXIT_BAD, 0, "core '%s' elf_begin: %s", arg, elf_errmsg(-1));
break;
case 'e':
{
dwfl = dwfl_begin (&proc_callbacks);
if (dwfl == NULL)
- error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
- if (dwfl_linux_proc_report (dwfl, pid) != 0)
- error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+ error (EXIT_BAD, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+
+ int err = dwfl_linux_proc_report (dwfl, pid);
+ if (err < 0)
+ error (EXIT_BAD, 0, "dwfl_linux_proc_report pid %d: %s", pid,
+ dwfl_errmsg (-1));
+ else if (err > 0)
+ error (EXIT_BAD, err, "dwfl_linux_proc_report pid %d", pid);
}
if (core != NULL)
{
dwfl = dwfl_begin (&core_callbacks);
if (dwfl == NULL)
- error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+ error (EXIT_BAD, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
if (dwfl_core_file_report (dwfl, core, exec) < 0)
- error (2, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+ error (EXIT_BAD, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
}
if (dwfl_report_end (dwfl, NULL, NULL) != 0)
- error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ error (EXIT_BAD, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+
+ /* Makes sure we are properly attached. */
+ if (dwfl_pid (dwfl) < 0)
+ error (EXIT_BAD, 0, "dwfl_pid: %s\n", dwfl_errmsg (-1));
break;
default:
{
.options = options,
.parser = parse_opt,
- .doc = N_("Print a stack for each thread in a process or core file."),
+ .doc = N_("Print a stack for each thread in a process or core file.\v\
+Program exits with return code 0 if all frames were shown without \
+any errors. If some frames were shown, but there were some non-fatal \
+errors, possibly causing an incomplete backtrace, the program exits \
+with return code 1. If no frames could be shown, or a fatal error \
+occured the program exits with return code 2. If the program was \
+invoked with bad or missing arguments it will exit with return code 64.")
};
argp_parse (&argp, argc, argv, 0, NULL, NULL);
if (show_one_tid)
{
- printf ("TID %d:\n", pid);
+ int err = 0;
switch (dwfl_getthread_frames (dwfl, pid, frame_callback, frames))
{
case DWARF_CB_OK:
+ case DWARF_CB_ABORT:
break;
case -1:
- error (0, 0, "dwfl_getthread_frames (%d): %s", pid,
- dwfl_errmsg (-1));
+ err = dwfl_errno ();
break;
default:
abort ();
}
- print_frames (frames);
+ print_frames (frames, pid, err, "dwfl_getthread_frames");
}
else
{
switch (dwfl_getthreads (dwfl, thread_callback, frames))
{
case DWARF_CB_OK:
+ case DWARF_CB_ABORT:
break;
case -1:
error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
free (demangle_buffer);
#endif
- return 0;
+ if (! frames_shown)
+ error (EXIT_BAD, 0, N_("Couldn't show any frames."));
+
+ return error_message_count != 0 ? EXIT_ERROR : EXIT_OK;
}