[API RFC] unwinder

Jan Kratochvil jan.kratochvil at redhat.com
Sun Oct 7 15:18:38 UTC 2012


branch jankratochvil/unwind

git clone -b jankratochvil/unwind git://git.jankratochvil.net/elfutils elfutils-unwind;cd elfutils-unwind;autoreconf -f -i;./configure --enable-maintainer-mode;make;cd tests;make backtrace backtrace-child;LD_LIBRARY_PATH=../backends:../libdw:../libelf ./backtrace
git remote add jankratochvilgit git://git.jankratochvil.net/elfutils; git fetch jankratochvilgit; git checkout jankratochvil/unwind

	TID 20043:
	# 0 0x7fed6e187880    	(null)
	# 1 0x7fed6e187c86 - 1	sigusr2
	# 2 0x7fed6d9c5300    	__restore_rt
	# 3 0x7fed6e187d80    	jmp
	# 4 0x7fed6e187d0a - 1	stdarg
	# 5 0x7fed6e187d70 - 1	backtracegen
	# 6 0x7fed6e187d79 - 1	start
	# 7 0x7fed6dd4ed90 - 1	start_thread
	# 8 0x7fed6da8019d - 1	clone
	TID 20042:
	# 0 0x7fed6dd563cb    	raise
	# 1 0x7fed6e187a9d - 1	main
	# 2 0x7fed6d9b069d - 1	__libc_start_main
	# 3 0x7fed6e187b3d - 1	_start

It is 1.2MB due to non-x86* core file testcases, I can push it as
jankratochvil/unwind to the main repo upon request.

User API is to unwind PC, nothing more.  It requires CFI now, no per-arch
frames unwinding possible now.  Live PIDs and core files are supported.
Archs supported: i386 x86_64 ppc ppc64 s390 s390x (+planned arm)
New testcase: run-backtrace.sh

Going to post along some independent elfutils extensions (s390{,x} core notes,
ppc64 .opd support) and fixes (bias adjustments, uninitialized data).

I have some issues with the API, what is libdw vs. libdwfl etc.  I have put
now mostly all to libdwfl, should I really keep it there?  There is some mess
with libdw/cfi.h where are:
    Dwarf_Frame_State -> Dwarf_Frame_State_Thread -> Dwarf_Frame_State_Process

User can see only opaque Dwarf_Frame_State.  Current API for it follows.
Maybe there were already some ideas for elfutils unwinding API, I have no
problem to refactor it to some completely different model.

# /* Get innermost frame of first thread of live process PID.  Returns NULL on
#    failure.  */
# extern Dwarf_Frame_State *dwfl_frame_state_pid (Dwfl *dwfl, pid_t pid);
# /* Get innermost frame of first thread of core file COREFILE.  Returns NULL on 
#    failure.  */
# extern Dwarf_Frame_State *dwfl_frame_state_core (Dwfl *dwfl,
#                                                  const char *corefile);
# /* Return TRUE and update *STATEP for the unwound frame for successful unwind.
#    Return TRUE and set *STATEP to NULL for the outermost frame.  Return FALSE
#    (and call __libdwfl_seterrno) otherwise.  */
# extern bool dwfl_frame_unwind (Dwarf_Frame_State **statep);
# /* Get return address register value for frame.  Return TRUE if *PC set and
#    optionally *MINUSONE is also set, if MINUSONE is not NULL.  Return FALSE
#    (and call __libdw_seterrno) otherwise.  *MINUSONE is TRUE for normal calls
#    where *PC should be decremented by one to get the call instruction, it is
#    FALSE if this frame was interrupted by a signal handler.  */
# extern bool dwfl_frame_state_pc (Dwarf_Frame_State *state, Dwarf_Addr *pc,
#                                  bool *minusone);
# /* Get innermost frame of the next thread from STATE.  STATE can be any frame 
#    of (the previous) thread.  */
# extern Dwarf_Frame_State *dwfl_frame_thread_next (Dwarf_Frame_State *state);
# /* Get Task ID of the thread of STATE.  This is PID for the thread started by 
#    function main and gettid () for the other threads.  */
# extern pid_t dwfl_frame_tid_get (Dwarf_Frame_State *state);

Sample code for unwinding (without error checks) is:

# Initialize Dwfl *dwfl, including reporting PID process or a core file.  This
# is a bit unclear to me whether dwfl_frame_state_pid / dwfl_frame_state_core
# should report it themselves or not.  See also tests/backtrace.c report_core.
# Dwarf_Frame_State *state = dwfl_frame_state_pid (dwfl, pid);
# OR
# Dwarf_Frame_State *state = dwfl_frame_state_core (dwfl, corefile);
#   do
#     {
#       Dwarf_Frame_State *thread = state;
#       pid_t tid = dwfl_frame_tid_get (thread);
#       # TID for printing.
#       while (state)
#         {
#           Dwarf_Addr pc;
#           bool minusone;
#           dwfl_frame_state_pc (state, &pc, &minusone);
#           Dwarf_Addr pc_adjusted = pc - (minusone ? 1 : 0);
# 	  ### Print PC and dwfl_module_addrname (PC_ADJUSTED).
#           dwfl_frame_unwind (&state);
#         }
#       state = dwfl_frame_thread_next (thread);
#     }
#   while (state);

The initial reporting is a bit problematic for me.  For example

src/stack -e ~/t/sleep6000-32 --core=$HOME/t/sleep6000-32.core
# 1 0xf75e64f0 - 1	__nanosleep
# 2 0xf75e632f - 1	sleep
# 3 0x80483d9 - 1	(null)
                        ^^^^^^ main is not resolved, we need to use:
src/stack --core=$HOME/t/sleep6000-32.core -e ~/t/sleep6000-32
 - as otherwise dwfl_addrmodule will find the one from a core file without
   symbols first which dwfl_module_addrname will not work for.

Still currently does not work after uncommenting run-backtrace.sh line:
  # mytestrun ./backtrace ./$child ./$core
./backtrace ./backtrace-child ./core.20860
0x7fa68624b000	0x7fa68644d000	[pie]
0x7fffa49ff000	0x7fffa4a00000	linux-vdso.so.1
0x7fa685e0b000	0x7fa686027000	libpthread.so.0
0x7fa685a53000	0x7fa685e0b000	libc.so.6
0x7fa686027000	0x7fa68624b000	ld-linux-x86-64.so.2
0x200000	0x401558	<executable>
TID 20860:
# 0 0x7fa685e14080    	pthread_join
# 1 0x7fa68624beba - 1	(null)
No DWARF information found

Because <executable> which is PIE is reported with different VMA than [pie].
I had this case working in the past but apparently the bias adjustments are
still not right, I will try more.


More information about the elfutils-devel mailing list