[PATCH 0/3] Performance tweaks for libdw
by Josh Stone
I did some investigation into libdw performance hotspots, and came up
with a few tweaks that in total trim nearly 1/3 the running time. I'm
running on Mark's recent empty-location patch, and I primarily used
"tests/varlocs -k >/dev/null" as a moderately long-running benchmark.
I'm using gcc-4.8.2-1.fc20.x86_64 and kernel-3.11.9-300.fc20.x86_64,
running on an i7-2600.
The perf profile initially looked like this:
Samples: 343K of event 'cycles', Event count (approx.): 318932712301
33.69% varlocs libdw.so [.] __libdw_find_attr
16.47% varlocs libdw.so [.] lookup.isra.0
15.63% varlocs libdw.so [.] __libdw_form_val_len
13.11% varlocs libdw.so [.] dwarf_siblingof
4.84% varlocs libdw.so [.] dwarf_tag
4.62% varlocs libdw.so [.] walk_children.4364
2.35% varlocs libdw.so [.] __libdw_findabbrev
2.32% varlocs libdw.so [.] Dwarf_Abbrev_Hash_find
1.26% varlocs libdw.so [.] dwarf_child
Patch 1 addresses form_val_len with an inlined fast path for forms with
constant length. Patch 2 is a rework of get_uleb128 and get_sleb128,
which are significant in find_attr and elsewhere. Patch 3 addresses the
hash lookup which is called often to find DIE abbreviations.
The perf profile now looks like this:
Samples: 229K of event 'cycles', Event count (approx.): 213925592727
44.63% varlocs libdw.so [.] __libdw_find_attr
22.28% varlocs libdw.so [.] dwarf_siblingof
7.64% varlocs libdw.so [.] walk_children.4388
7.07% varlocs libdw.so [.] dwarf_tag
5.18% varlocs libdw.so [.] __libdw_findabbrev
2.88% varlocs libdw.so [.] __libdw_form_val_compute_len
2.11% varlocs libdw.so [.] dwarf_child
1.44% varlocs libdw.so [.] __libdw_formref
1.12% varlocs libdw.so [.] scope_visitor
The remaining busy work is simply walking through attributes, from DIE
to DIE. I believe optimizing this further will be hard without keeping
track of DIE lengths somewhere, which is a lot to cache. Putting the
length in Dwarf_Die itself is not feasible, because those are short-
lived and frequently recreated.
Here's some summary information for how these patches change varloc -k:
libdw varlocs varlocs
text time maxres
Base: 243072 84.42s 242360k
P1: 243296 74.91s 242356k
P2: 243184 70.61s 242360k
P3: 243600 56.75s 243588k
My timings are not statistically rigorous measurements, but it still
seems a clear win across the board. Other benchmarks I've tried, like
tests/allfcts and stap -l syscall.*, show similar improvement.
Feedback is always appreciated.
Thanks,
Josh
9 years, 7 months
[RFC] libelf: segment fault on x86-64 while file's bss offset have a large number
by Hongxu Jia
*Environment
cat /etc/issue
Ubuntu 13.04 \n \l
uname -a
Linux pek-hjia-d1 3.8.0-31-generic #46-Ubuntu SMP Tue Sep 10 20:03:44 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
*Problem
1) Here is the test source code
$ cat >> test.c << EOF
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include "libelf.h"
int main(int argc, char *argv[])
{
int fd;
Elf *e;
if (elf_version(EV_CURRENT) == EV_NONE)
{
printf ("library out of date\n");
exit (1);
}
if ((fd = open("test/xB.linkhuge", O_RDWR)) < 0) {
printf("%s %d failed\n", __FUNCTION__, __LINE__);
exit (1);
}
if ((e = elf_begin(fd, ELF_C_RDWR_MMAP, (Elf *) 0)) == 0) {
printf("failed %s", elf_errmsg (-1));
exit (1);
}
elf_flagelf (e, ELF_C_SET, ELF_F_LAYOUT);
elf_update(e, ELF_C_WRITE);
elf_end(e);
close(fd);
}
EOF
2) Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/e...
to get libelf.a for debug.
Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/e...
to get libelf.h for debug.
3) Compile test.c with libelf.a
$ gcc test.c -o test_case -static -L. -lelf
4) Prepare file whose bss offset have a large number '00200000'
Download the attachment from
https://bugzilla.redhat.com/show_bug.cgi?id=1020842
$ ls test/xB.linkhuge -al
-rwxr-xr-x 1 jiahongxu jiahongxu 1221403 Oct 18 18:55 test/xB.linkhuge
$ readelf -a xB.linkhuge
......
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
skip..
[25] .data PROGBITS 00000000005128a0 001128a0
0000000000010168 0000000000000000 WA 0 0 32
[26] .bss NOBITS 0000000001000000 00200000
0000000000010050 0000000000000000 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 00122a08
0000000000000011 0000000000000001 MS 0 0 1
......
5) Run test_case with strace, there was mmap/munmap error.
$ strace ./test_case
execve("./test_case", ["./test_case"], [/* 59 vars */]) = 0
uname({sys="Linux", node="pek-hjia-d1", ...}) = 0
brk(0) = 0x16a6000
brk(0x16a71c0) = 0x16a71c0
arch_prctl(ARCH_SET_FS, 0x16a6880) = 0
brk(0x16c81c0) = 0x16c81c0
brk(0x16c9000) = 0x16c9000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("test/xB.linkhuge", O_RDWR) = 3
fcntl(3, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0
mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000
fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0
ftruncate(3, 2097152) = 0
msync(0x7ff720fe2000, 1216568, MS_SYNC) = 0
munmap(0x7ff720fe2000, 2097152) = 0
close(3) = 0
exit_group(0) = ?
6) $ ls test/xB.linkhuge -al
-rwxr-xr-x 1 jiahongxu jiahongxu 2097152 Oct 18 19:04 test/xB.linkhuge
*Analysis
1) While ELF_C_RDWR_MMAP was used, elf_begin invoked mmap() to map file
into memory with the size of '1221403'.
...strace log...
mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000
................
2) While 'xB.linkhuge' bss Offset has a large number '00200000', elf_update
caculated file size by __elf64_updatenull_wrlock and the size was
enlarged from '1221403' to '2097152'
3) In this situation, elf_update invoked ftruncate to enlarge the file,
and memory size (elf->maximum_size) also was incorrectly updated.
...strace log...
ftruncate(3, 2097152)
................
4) There was segment fault in elf_end which invoked munmap with the
length is the enlarged file size '2097152', not the length of
mmap '1216568'.
...strace log...
munmap(0x7ff720fe2000, 2097152) = 0
................
*Solution
1) I tried to modify elf_update.c, don't update memory size
(elf->maximum_size) in this situation. It fixed this issue
and everything looks ok, but I am not sure the modification
is necessary.
......
11 diff --git a/libelf/elf_update.c b/libelf/elf_update.c
12 --- a/libelf/elf_update.c
13 +++ b/libelf/elf_update.c
14 @@ -120,7 +120,9 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
15 size = -1;
16 }
17
18 - if (size != -1 && elf->parent == NULL)
19 + /* If the file is enlarged by truncate, we should not update maximum_size to
20 + avoid segment fault while invoking munmap in elf_end */
21 + if (size != -1 && elf->parent == NULL && (size_t) size <= elf->maximum_size)
22 elf->maximum_size = size;
......
2) I also tried to add check before munmap in elf_end by msync with
the length of elf->maximum_size, if msync return error, munmap
should not be invoked, this could avoid segment fault.
--- a/libelf/elf_end.c
+++ b/libelf/elf_end.c
@@ -217,7 +217,10 @@ elf_end (elf)
if ((elf->flags & ELF_F_MALLOCED) != 0)
free (elf->map_address);
else if ((elf->flags & ELF_F_MMAPPED) != 0)
- munmap (elf->map_address, elf->maximum_size);
+ {
+ if (msync (elf->map_address, elf->maximum_size, MS_SYNC) == 0)
+ munmap (elf->map_address, elf->maximum_size);
+ }
}
3) Any suggestion is welcomed.
--
1.8.1.2
9 years, 10 months
[PATCH] stack: Add new '-n MAXFRAMES' option. Resolve addresses after unwind.
by Mark Wielaard
Limit the number of frames printed per thread (defaults to 64) and resolve
addresses to names, modules and source after unwinding so the thread is
only stopped for the minimum time needed to do the actual unwinding. The
thread doesn't need to wait for the lookups and printing of information.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 16 +++++
src/stack.c | 174 ++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 127 insertions(+), 63 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 0e6d2dc..d3d6807 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,19 @@
+2013-12-22 Mark Wielaard <mjw(a)redhat.com>
+
+ * stack.c (maxframes): New static unsigned. Initialize to 64.
+ (struct frame): New struct.
+ (struct frames): Likewise.
+ (dwfl): New static Dwfl pointer.
+ (frame_callback): Use arg as struct frames and fill it next frame.
+ Return DWARF_CB_ABORT when maxframes has been reached. Move
+ printing of frame to...
+ (print_frames): ...here. New function.
+ (thread_callback): Use arg as struct frames and set frames to zero.
+ Call print_frames.
+ (parse_opt): Handle '-n'.
+ (main): Add -n to options. Allocate frames using maxframes. Pass
+ frames to frame_callback and thread_callback.
+
2013-12-20 Mark Wielaard <mjw(a)redhat.com>
* stack.c (show_one_tid): New static boolean.
diff --git a/src/stack.c b/src/stack.c
index 512c85b..188aa00 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -41,91 +41,122 @@ 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)
+static unsigned maxframes = 64;
+
+struct frame
{
- unsigned *framenop = arg;
Dwarf_Addr pc;
bool isactivation;
- if (! dwfl_frame_pc (state, &pc, &isactivation))
+};
+
+struct frames
+{
+ unsigned frames;
+ struct frame frame[];
+};
+
+static Dwfl *dwfl = NULL;
+
+static int
+frame_callback (Dwfl_Frame *state, void *arg)
+{
+ struct frames *frames = (struct frames *) 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;
}
- Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
-
- /* Get PC->SYMNAME. */
- Dwfl *dwfl = dwfl_thread_dwfl (dwfl_frame_thread (state));
- Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
- const char *symname = NULL;
- if (mod)
- symname = dwfl_module_addrname (mod, pc_adjusted);
-
- // Try to find the address wide if possible.
- static int width = 0;
- if (width == 0 && mod)
+
+ frames->frames++;
+ if (frames->frames == maxframes)
+ return DWARF_CB_ABORT;
+
+ return DWARF_CB_OK;
+}
+
+static void
+print_frames (struct frames *frames)
+{
+ for (unsigned nr = 0; nr < frames->frames; nr++)
{
- Dwarf_Addr bias;
- Elf *elf = dwfl_module_getelf (mod, &bias);
- if (elf)
+ Dwarf_Addr pc = frames->frame[nr].pc;
+ bool isactivation = frames->frame[nr].isactivation;
+ Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+
+ /* Get PC->SYMNAME. */
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ const char *symname = NULL;
+ if (mod)
+ symname = dwfl_module_addrname (mod, pc_adjusted);
+
+ // Try to find the address wide if possible.
+ static int width = 0;
+ if (width == 0 && mod)
{
- GElf_Ehdr ehdr_mem;
- GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
- if (ehdr)
- width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ if (elf)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr)
+ width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
+ }
}
- }
- if (width == 0)
- width = 16;
+ if (width == 0)
+ width = 16;
- printf ("#%-2u 0x%0*" PRIx64, (*framenop)++, width, (uint64_t) pc);
+ printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc);
- if (show_activation)
- printf ("%4s", ! isactivation ? "- 1" : "");
+ if (show_activation)
+ printf ("%4s", ! isactivation ? "- 1" : "");
- if (symname != NULL)
- printf (" %s", symname);
+ if (symname != NULL)
+ printf (" %s", symname);
- if (show_module)
- {
- const char* fname;
-
- fname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- if (fname != NULL)
- printf (" - %s", fname);
- }
+ if (show_module)
+ {
+ const char* fname;
+ fname = dwfl_module_info(mod,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (fname != NULL)
+ printf (" - %s", fname);
+ }
- if (show_source)
- {
- Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted);
- if (lineobj)
+ if (show_source)
{
- int line, col;
- const char* sname;
- line = col = -1;
- sname = dwfl_lineinfo (lineobj, NULL, &line, &col, NULL, NULL);
- if (sname != NULL)
+ Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted);
+ if (lineobj)
{
- printf ("\n %s", sname);
- if (line > 0)
+ int line, col;
+ const char* sname;
+ line = col = -1;
+ sname = dwfl_lineinfo (lineobj, NULL, &line, &col, NULL, NULL);
+ if (sname != NULL)
{
- printf (":%d", line);
- if (col > 0)
- printf (":%d", col);
+ printf ("\n %s", sname);
+ if (line > 0)
+ {
+ printf (":%d", line);
+ if (col > 0)
+ printf (":%d", col);
+ }
}
}
}
+ printf ("\n");
}
- printf ("\n");
- return DWARF_CB_OK;
}
static int
-thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__ ((unused)))
+thread_callback (Dwfl_Thread *thread, void *thread_arg)
{
printf ("TID %ld:\n", (long) dwfl_thread_tid (thread));
- unsigned frameno = 0;
- switch (dwfl_thread_getframes (thread, frame_callback, &frameno))
+ struct frames *frames = (struct frames *) thread_arg;
+ frames->frames = 0;
+ switch (dwfl_thread_getframes (thread, frame_callback, thread_arg))
{
case DWARF_CB_OK:
case DWARF_CB_ABORT:
@@ -136,6 +167,7 @@ thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__ ((unused)))
default:
abort ();
}
+ print_frames (frames);
return DWARF_CB_OK;
}
@@ -175,6 +207,15 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
show_one_tid = true;
break;
+ case 'n':
+ maxframes = atoi (arg);
+ if (maxframes == 0)
+ {
+ argp_error (state, N_("-n MAXFRAMES should be 1 or higher."));
+ return EINVAL;
+ }
+ break;
+
default:
return ARGP_ERR_UNKNOWN;
}
@@ -204,6 +245,8 @@ main (int argc, char **argv)
N_("Show all additional information (activation, module and source)"), 0 },
{ NULL, '1', NULL, 0,
N_("Show the backtrace of only one thread"), 0 },
+ { NULL, 'n', "MAXFRAMES", 0,
+ N_("Show at most MAXFRAMES per thread (defaults to 64)"), 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
@@ -224,11 +267,11 @@ Only real user processes are supported, no kernel or process maps."),
};
int remaining;
- Dwfl *dwfl = NULL;
argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
assert (dwfl != NULL);
if (remaining != argc)
- error (2, 0, "eu-stack [-a] [-m] [-s] [-v] [-1] [--debuginfo-path=<path>]"
+ error (2, 0, "eu-stack [-a] [-m] [-s] [-v] [-1] [-n MAXFRAMES]"
+ " [--debuginfo-path=<path>]"
" {-p <process id>|--core=<file> [--executable=<file>]|--help}");
/* dwfl_linux_proc_report has been already called from dwfl_standard_argp's
@@ -236,11 +279,14 @@ Only real user processes are supported, no kernel or process maps."),
if (dwfl_report_end (dwfl, NULL, NULL) != 0)
error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ struct frames *frames = malloc (sizeof (struct frames)
+ + sizeof (struct frame) * maxframes);
+ frames->frames = 0;
+
if (show_one_tid)
{
pid_t tid = dwfl_pid (dwfl);
- unsigned frameno = 0;
- switch (dwfl_getthread_frames (dwfl, tid, frame_callback, &frameno))
+ switch (dwfl_getthread_frames (dwfl, tid, frame_callback, frames))
{
case DWARF_CB_OK:
break;
@@ -251,10 +297,11 @@ Only real user processes are supported, no kernel or process maps."),
default:
abort ();
}
+ print_frames (frames);
}
else
{
- switch (dwfl_getthreads (dwfl, thread_callback, NULL))
+ switch (dwfl_getthreads (dwfl, thread_callback, frames))
{
case DWARF_CB_OK:
break;
@@ -265,6 +312,7 @@ Only real user processes are supported, no kernel or process maps."),
abort ();
}
}
+ free (frames);
dwfl_end (dwfl);
return 0;
--
1.7.1
9 years, 11 months
[PATCH] libdw: Try .gnu_debugaltlink paths relative to the debug file
by Josh Stone
The existing code was first trying .gnu_debugaltlink paths directly, so
relative paths would start from the current program's working directory.
That's unlikely to be useful, so it was almost always falling back to a
build-id based path.
In Fedora, these paths are relative to the debug file itself, so that's
a better thing to try first. We don't actually have the debug path in
libdw, but we can usually find it from elf->fildes and /proc/self/fd/.
Signed-off-by: Josh Stone <jistone(a)redhat.com>
---
libdw/ChangeLog | 5 +++++
libdw/dwarf_begin_elf.c | 44 +++++++++++++++++++++++++++++++++++++++++---
tests/ChangeLog | 4 ++++
tests/run-allfcts-multi.sh | 14 ++++++++++----
4 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index aa7b9ca486c2..a540a5219856 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2013-12-14 Josh Stone <jistone(a)redhat.com>
+
+ * dwarf_begin_elf.c (build_rel_debugaltlink): New function.
+ (open_debugaltlink): Use it.
+
2013-12-10 Josh Stone <jistone(a)redhat.com>
* memory-access.h (get_uleb128_rest_return): Removed.
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 6cf3aa17855d..eeb84c051770 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -42,8 +42,10 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <libgen.h>
#include "libdwP.h"
+#include "libelfP.h"
#if USE_ZLIB
# include <endian.h>
@@ -136,14 +138,50 @@ try_debugaltlink (Dwarf *result, const char *try_name,
return NULL;
}
+/* Make relative links into an absolute path. */
+static char *
+build_rel_debugaltlink (Dwarf *result, const char *alt_name)
+{
+ if (alt_name[0] == '/' || result->elf->fildes < 0)
+ return NULL;
+
+#define SELFFD_PREFIX "/proc/self/fd/"
+ char fd_name[sizeof SELFFD_PREFIX + 12];
+ sprintf (fd_name, "%s%d", SELFFD_PREFIX, result->elf->fildes);
+
+ /* Note, readlink(2) recommends sizing the buffer based on lstat(2), but
+ procfs always reports st_size=64, even if it's really longer. */
+ char fd_link[PATH_MAX + 1];
+ ssize_t len_link = readlink (fd_name, fd_link, sizeof fd_link);
+ if (len_link == -1 || (size_t) len_link > sizeof fd_link - 1)
+ return NULL;
+ fd_link[len_link] = '\0';
+
+ char *rel_name;
+ if (asprintf (&rel_name, "%s/%s", dirname (fd_link), alt_name) == -1)
+ return NULL;
+
+ return rel_name;
+}
+
/* For dwz multifile support, ignore if it looks wrong. */
static Dwarf *
open_debugaltlink (Dwarf *result, const char *alt_name,
const uint8_t *build_id, const size_t id_len)
{
- /* First try the name itself, it is either an absolute path or
- a relative one. Sadly we don't know relative from where at
- this point. */
+ /* First try it relative to the debug file. */
+ char *rel_name = build_rel_debugaltlink (result, alt_name);
+ if (rel_name != NULL)
+ {
+ Dwarf *alt = try_debugaltlink (result, rel_name, build_id, id_len);
+ free (rel_name);
+ if (alt != NULL)
+ return result;
+ }
+
+ /* Then try the name itself, it is either an absolute path or
+ a relative one. (Using a path relative to the tool's cwd
+ doesn't make much sense in general, but it's also harmless.) */
if (try_debugaltlink (result, alt_name, build_id, id_len) != NULL)
return result;
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 008198166da6..6325a74d5915 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2013-12-14 Josh Stone <jistone(a)redhat.com>
+
+ * run-allfcts-multi.sh: Test relative altlinks.
+
2013-12-10 Mark Wielaard <mjw(a)redhat.com>
* Makefile.am (backtrace_child_biarch_SOURCES): New backtrace-child.c.
diff --git a/tests/run-allfcts-multi.sh b/tests/run-allfcts-multi.sh
index 727b76ee5c72..35e3177a57ae 100755
--- a/tests/run-allfcts-multi.sh
+++ b/tests/run-allfcts-multi.sh
@@ -42,15 +42,21 @@ EOF
# dwz test-offset-loop test-offset-loop2 -m test-offset-loop.alt
testfiles test-offset-loop test-offset-loop.alt
-tempfiles allfcts.out
+tempfiles allfcts.in allfcts.out
-# Use head to capture output because the output could be infinite...
-testrun ${abs_builddir}/allfcts test-offset-loop | head -n 20 > allfcts.out
-testrun_compare cat allfcts.out <<\EOF
+cat >allfcts.in <<\EOF
/tmp/test-offset-loop.c:6:get_errno
/tmp/test-offset-loop.c:5:is_error
/tmp/test-offset-loop.c:4:padding
/tmp/test-offset-loop.c:7:main
EOF
+# Use head to capture output because the output could be infinite...
+testrun ${abs_builddir}/allfcts test-offset-loop | head -n 20 > allfcts.out
+testrun_compare cat allfcts.out <allfcts.in
+
+# Repeat from a different directory for relative .gnu_debugaltlink
+(cd / && testrun ${abs_builddir}/allfcts $OLDPWD/test-offset-loop) | head -n 20 > allfcts.out
+testrun_compare cat allfcts.out <allfcts.in
+
exit 0
--
1.8.4.2
9 years, 11 months
[PATCH] stack: -b, --build-id shows module build-id, load address and pc offset.
by Mark Wielaard
A convenient format for offline processing of the backtrace.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 8 ++++++++
src/stack.c | 31 +++++++++++++++++++++++++++----
2 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index d3d6807..4737d69 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,11 @@
+2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+
+ * stack.c (show_build_id): New static boolean.
+ (print_frames): Print module build-id, load address and pc offset
+ if show_build_id is true.
+ (parse_opt): Handle '-b'.
+ (main): Add -b to options.
+
2013-12-22 Mark Wielaard <mjw(a)redhat.com>
* stack.c (maxframes): New static unsigned. Initialize to 64.
diff --git a/src/stack.c b/src/stack.c
index 188aa00..362cc06 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -38,6 +38,7 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
static bool show_activation = false;
static bool show_module = false;
+static bool show_build_id = false;
static bool show_source = false;
static bool show_one_tid = false;
@@ -116,15 +117,31 @@ print_frames (struct frames *frames)
if (symname != NULL)
printf (" %s", symname);
+ const char* fname;
+ Dwarf_Addr start;
+ fname = dwfl_module_info(mod, NULL, &start,
+ NULL, NULL, NULL, NULL, NULL);
if (show_module)
{
- const char* fname;
- fname = dwfl_module_info(mod,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (fname != NULL)
printf (" - %s", fname);
}
+ if (show_build_id)
+ {
+ const unsigned char *id;
+ GElf_Addr id_vaddr;
+ int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+ if (id_len > 0)
+ {
+ printf ("\n [");
+ do
+ printf ("%02" PRIx8, *id++);
+ while (--id_len > 0);
+ printf ("]@0x%0" PRIx64 "+%" PRIx64, start, pc_adjusted - start);
+ }
+ }
+
if (show_source)
{
Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted);
@@ -203,6 +220,10 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
show_activation = show_source = show_module = true;
break;
+ case 'b':
+ show_build_id = true;
+ break;
+
case '1':
show_one_tid = true;
break;
@@ -243,6 +264,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 },
+ { "build-id", 'b', NULL, 0,
+ N_("Show module build-id, load address and pc offset"), 0 },
{ NULL, '1', NULL, 0,
N_("Show the backtrace of only one thread"), 0 },
{ NULL, 'n', "MAXFRAMES", 0,
@@ -270,7 +293,7 @@ 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] [-1] [-n MAXFRAMES]"
+ error (2, 0, "eu-stack [-a] [-m] [-b] [-s] [-v] [-1] [-n MAXFRAMES]"
" [--debuginfo-path=<path>]"
" {-p <process id>|--core=<file> [--executable=<file>]|--help}");
--
1.7.1
9 years, 11 months
[PATCH] stack: Add --quiet to not resolve addresses, add --raw to not demangle.
by Mark Wielaard
Resolving addresses to function symbol names can be expensive. Use -q
to only print addresses (use together with --build-id to process later).
Demangle names by default, but add the -r option to not demangle and
show the raw names.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 12 ++++++++++++
src/Makefile.am | 3 ++-
src/stack.c | 45 +++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 0f1b971..28a6f9e 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,17 @@
2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+ * Makefile.am (stack_LDADD): Add demanglelib.
+ * stack.c (show_quiet): New static boolean, default false.
+ (show_raw): Likewise.
+ (demangle_buffer_len): New static size_t.
+ (demangle_buffer): New static char *.
+ (print_frames): Don't resolve pc name if show_quiet. Demangle name
+ unless show_raw.
+ (parse_opt): Handle '-q' and '-r'.
+ (main): Add 'q' and 'r' to options. Free demangle_buffer.
+
+2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+
* stack.c (main): Print pid of process or core.
2013-12-23 Mark Wielaard <mjw(a)redhat.com>
diff --git a/src/Makefile.am b/src/Makefile.am
index 954a14b..9a78348 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -115,7 +115,8 @@ ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
strings_LDADD = $(libelf) $(libeu) $(libmudflap)
ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
-stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
+stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl \
+ $(demanglelib)
ldlex.o: ldscript.c
ldlex_no_Werror = yes
diff --git a/src/stack.c b/src/stack.c
index dfb0272..3568298 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -45,6 +45,10 @@ static bool show_module = false;
static bool show_build_id = false;
static bool show_source = false;
static bool show_one_tid = false;
+static bool show_quiet = false;
+#ifdef USE_DEMANGLE
+static bool show_raw = false;
+#endif
static unsigned maxframes = 64;
@@ -81,6 +85,11 @@ static const Dwfl_Callbacks core_callbacks =
.debuginfo_path = &debuginfo_path,
};
+#ifdef USE_DEMANGLE
+static size_t demangle_buffer_len = 0;
+static char *demangle_buffer = NULL;
+#endif
+
static int
frame_callback (Dwfl_Frame *state, void *arg)
{
@@ -112,7 +121,7 @@ print_frames (struct frames *frames)
/* Get PC->SYMNAME. */
Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
const char *symname = NULL;
- if (mod)
+ if (mod && ! show_quiet)
symname = dwfl_module_addrname (mod, pc_adjusted);
// Try to find the address wide if possible.
@@ -138,7 +147,19 @@ print_frames (struct frames *frames)
printf ("%4s", ! isactivation ? "- 1" : "");
if (symname != NULL)
- printf (" %s", symname);
+ {
+#ifdef USE_DEMANGLE
+ if (! show_raw)
+ {
+ int status = -1;
+ char *dsymname = __cxa_demangle (symname, demangle_buffer,
+ &demangle_buffer_len, &status);
+ if (status == 0)
+ symname = demangle_buffer = dsymname;
+ }
+#endif
+ printf (" %s", symname);
+ }
const char* fname;
Dwarf_Addr start;
@@ -267,6 +288,16 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
show_build_id = true;
break;
+ case 'q':
+ show_quiet = true;
+ break;
+
+#ifdef USE_DEMANGLE
+ case 'r':
+ show_raw = true;
+ break;
+#endif
+
case '1':
show_one_tid = true;
break;
@@ -352,6 +383,12 @@ 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 },
+ { "quiet", 'q', NULL, 0,
+ N_("Do not resolve address to function symbol name"), 0 },
+#ifdef USE_DEMANGLE
+ { "raw", 'r', NULL, 0,
+ N_("Show raw function symbol names, do not try to demangle names"), 0 },
+#endif
{ "build-id", 'b', NULL, 0,
N_("Show module build-id, load address and pc offset"), 0 },
{ NULL, '1', NULL, 0,
@@ -413,5 +450,9 @@ main (int argc, char **argv)
if (core_fd != -1)
close (core_fd);
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+
return 0;
}
--
1.7.1
9 years, 11 months
[PATCH] libdwfl: Add dwfl_getthread and dwfl_getthread_frames.
by Mark Wielaard
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
9 years, 11 months
[patch] Support live PIDs with deleted files
by Jan Kratochvil
Hi Mark,
jankratochvil/deleted
with the new /proc/PID/map_files/ feature it is possible to fully access even
deleted executable + shared_libraries. Current Linux kernels (3.11.10) permit
on root user as the tracer but (1) hopefully it gets fixed in the future and
(2) at least for the use in ABRT it is OK as backtracer is run from core
dumping hook.
Linux kernels have always supported also /proc/PID/exe which works for deleted
main executable. It even works for non-root users. But it works only for the
executable, not for shared libraries. I find more useful to push for Linux
kernel /proc/PID/map_files/ permissions fix than to implement /proc/PID/exe.
If the running binary has been upgraded while it is still running its separate
debug info files in /usr/lib/debug/ will no longer match. But still
.eh_frame-based unwind is possible using /proc/PID/map_files/ that time.
Testcase is skipped when /proc/PID/map_files/ gives permission error.
OK for check-in?
I am not completely sure setting just mod->main.name is right but it looks so.
Thanks,
Jan
9 years, 11 months
[PATCH] libdwfl: Fix build_id memory leak in dwfl_segment_report_module.
by Mark Wielaard
We might already have allocated memory to hold the build_id early in
consider_notes when we called consider_phdr for the program headers
we've read from the image. We would leak that memory when we don't use
it then because we return early/fail. This can be because either we
didn't find the correct bias or we skip the module because it would
conflict in address space with any already existing module of DWFL.
In both cases explicitly free the build_id memory.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdwfl/ChangeLog | 5 +++++
libdwfl/dwfl_segment_report_module.c | 10 ++++++++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 8a164f0..0e7f80a 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,5 +1,10 @@
2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Free
+ build_id before returning early.
+
+2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+
* linux-pid-attach.c (__libdwfl_attach_state_for_pid): Report actual
pid (thread group leader) to dwfl_attach_state.
diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
index 55f6d38..fd967e9 100644
--- a/libdwfl/dwfl_segment_report_module.c
+++ b/libdwfl/dwfl_segment_report_module.c
@@ -427,7 +427,10 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
/* We must have seen the segment covering offset 0, or else the ELF
header we read at START was not produced by these program headers. */
if (unlikely (!found_bias))
- return finish ();
+ {
+ free (build_id);
+ return finish ();
+ }
/* Now we know enough to report a module for sure: its bounds. */
module_start += bias;
@@ -519,7 +522,10 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
}
}
if (skip_this_module)
- return finish ();
+ {
+ free (build_id);
+ return finish ();
+ }
}
/* Our return value now says to skip the segments contained
--
1.7.1
9 years, 11 months
[PATCH] stack: Improve error checking and exit code handling.
by Mark Wielaard
Check up front whether we attached correctly, if not error out. Make sure
callbacks report -1 only on real errors and DWARF_CB_ABORT if exiting early
(but not in error). Handle all errors from frame callback in print_frames
after printing of good frames. Print as much information as possible like
tid, address and module name if known with error messages. Only exit with
exit code zero if everything went fine. Exit with error code one if there
were any non-fatal errors. Exit with error code two if no frames could be
printed or a fatal error occurred.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 17 ++++++++++
src/stack.c | 102 +++++++++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 142a1c3..7911236 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,20 @@
+2013-12-27 Mark Wielaard <mjw(a)redhat.com>
+
+ * stack.c (frames_shown): New static boolean.
+ (EXIT_OK,EXIT_ERROR,EXIT_BAD,EXIT_USAGES): New defines.
+ (frame_callback): Return -1 on error. Don't print error.
+ (print_frames): Add arguments, tid, dwflerr and what. Print tid.
+ If there was an error report it with address and module if possible.
+ Record whether any frames were actually printed.
+ (thread_callback): Collect tid and err, pass it to print_frames.
+ (parse_opt): Use EXIT_BAD for errors. On ARGP_KEY_END print errno
+ if dwfl_linux_proc_report returned it. Check whether we are properly
+ attached with dwfl_pid.
+ (main): Document exit status. Don't report DWARF_CB_ABORT from
+ callbacks as error. Pass real errors to print_frames. Return
+ EXIT_BAD if no frames could be shown. Return EXIT_ERROR if there
+ were any non-fatal errors.
+
2013-12-23 Mark Wielaard <mjw(a)redhat.com>
* Makefile.am (stack_LDADD): Add demanglelib.
diff --git a/src/stack.c b/src/stack.c
index 3568298..d71d669 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -90,6 +90,18 @@ static size_t demangle_buffer_len = 0;
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)
{
@@ -97,10 +109,7 @@ 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)
@@ -110,8 +119,12 @@ frame_callback (Dwfl_Frame *state, void *arg)
}
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;
@@ -209,13 +222,39 @@ print_frames (struct frames *frames)
}
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))
{
@@ -223,12 +262,12 @@ thread_callback (Dwfl_Thread *thread, void *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;
}
@@ -253,11 +292,11 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
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':
@@ -328,22 +367,31 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
{
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:
@@ -402,7 +450,13 @@ main (int argc, char **argv)
{
.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);
@@ -413,19 +467,19 @@ main (int argc, char **argv)
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
{
@@ -433,6 +487,7 @@ main (int argc, char **argv)
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));
@@ -454,5 +509,8 @@ main (int argc, char **argv)
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;
}
--
1.8.4.2
9 years, 11 months