Monday, February 23, 2009

Stack Backtracing Inside Your Program

If you usually work with non-trivial C sources, you may have wondered which execution path (that is, which sequence of function calls) brought you to a certain point in your program. Also, it would be even more useful if you could have that piece of information whenever your beautiful, bug-free program suddenly crashes, and you have no debugger at hand. What is needed is a stack backtrace and, thanks to a little known feature of the GNU C library, obtaining it is a fairly easy task.

Stack Frames and Backtraces

Before diving into the article, let's briefly go over how function calls and parameters pass work in C. In order to prepare for the function call, parameters are pushed on the stack in reverse order. Afterwards, the caller's return address also is pushed on the stack and the function is called. Finally, the called function's entry code creates some more space on the stack for storage of automatic variables. This layout commonly is called a stack frame for that particular instance of the function call. When more function calls are nested, the whole procedure is repeated, causing the stack to keep growing downwards and building a chain of stack frames (see Figure 1). Thus, at any given point in a program it theoretically is possible to backtrace the sequence of stack frames to the originating calling point, up to the main() function (to be exact, up to the libc function, which calls main() when the process starts up).

Figure 1. Nested Function Calls

Stack Backtracing from within GDB

Getting the stack backtrace with GDB (or an equivalent graphical front end) for a program that crashed while running is straightforward: you simply issue the bt command, which returns the list of functions called up to the point of the crash. As this is a standard practice, we do not provide any more details here; have a look at the GDB info page if you need specifics (info gdb stack gets you there).

Stack Backtracing Using libc

If for some reason you're not running inside a debugger, two options are available for tracing what the program is doing. The first method is to disseminate it with print and log messages in order to pinpoint the execution path. In a complex program, this option can become cumbersome and tedious even if, with the help of some GCC-specific macros, it can be simplified a bit. Consider, for example, a debug macro such as

#define TRACE_MSG fprintf(stderr, __FUNCTION__     \
"() [%s:%d] here I am\n", \
__FILE__, __LINE__)

You can propagate this macro quickly throughout your program by cutting and pasting it. When you do not need it anymore, switch it off simply by defining it to no-op.

A nicer way to get a stack backtrace, however, is to use some of the specific support functions provided by glibc. The key one is backtrace(), which navigates the stack frames from the calling point to the beginning of the program and provides an array of return addresses. You then can map each address to the body of a particular function in your code by having a look at the object file with the nm command. Or, you can do it a simpler way--use backtrace_symbols(). This function transforms a list of return addresses, as returned by backtrace(), into a list of strings, each containing the function name offset within the function and the return address. The list of strings is allocated from your heap space (as if you called malloc()), so you should free() it as soon as you are done with it.

If you prefer to avoid dynamic memory allocation during the backtrace--reasonable, as the backtrace is likely to happen under faulty conditions--you can resort to backtrace_symbols_fd(). This prints the strings directly to the given file descriptor and does not allocate new memory for strings storage. It is a safer choice in those cases where memory heap potentially is corrupted.

In order to convert an address to a function name, the last two functions rely on symbol information to be available inside the program itself. To enable this feature, compile your program with the -rdynamic option (see man dlopen for more details).

Listing 1. How to Use the Backtrace Functions

Listing 1 demonstrates how to use these functions. The test() function calls either func_low() or func_high(), both of which call show_stackframe() to print out the execution path. The program is compiled with

gcc -rdynamic listing1.c -o listing1

The output should look something like:

Execution path:
./listing1(show_stackframe+0x2e) [0x80486de]
./listing1(func_high+0x11) [0x8048799]
./listing1(test+0x43) [0x80487eb]
./listing1(main+0x13) [0x8048817]
/lib/libc.so.6(__libc_start_main+0xbd) [0x4003e17d]
./listing1(backtrace_symbols+0x31) [0x80485f1]
First call: 167
Execution path:
./listing1(show_stackframe+0x2e) [0x80486de]
./listing1(func_low+0x11) [0x8048779]
./listing1(test+0x21) [0x80487c9]
./listing1(main+0x33) [0x8048837]
/lib/libc.so.6(__libc_start_main+0xbd) [0x4003e17d]
./listing1(backtrace_symbols+0x31) [0x80485f1]
Second call: -3

By the way, function prototypes for the backtrace functions reside in the header file execinfo.h.

One Step Farther

At this point, we have in hand a tool that is able to print the list of function calls up to the current execution point. This can be a useful tool in many different contexts. Think of having a complex program and needing to know who's calling a given function with the wrong parameters. With a simple check and a call to our show_stackframe() function, the faulty caller can be spotted easily.

An even more useful application for this technique is putting a stack backtrace inside a signal handler and having the latter catch all the "bad" signals your program can receive (SIGSEGV, SIGBUS, SIGILL, SIGFPE and the like). This way, if your program unfortunately crashes and you were not running it with a debugger, you can get a stack trace and know where the fault happened. This technique also can be used to understand where your program is looping in case it stops responding. All you need to do is set up a SIGUSR1/2 handler and send such a signal when needed. Before presenting an example, we need to open a parenthesis on signal handling.

Signal Handling and Stack Frames

Backtracing from within a signal handler requires some interesting intricacies that take us on a little detour through signal delivery to processes. Going into deep detail on this matter is outside the scope of this article, but we briefly can summarize it this way:

  • When the kernel needs to notify a signal of a given process, it prepares some data structures attached to the process' task struct and sets a signal-pending bit.

  • Later on, when the signalee process is scheduled for execution, its stack frame is altered by the kernel in order to have EIP point to the process' signal handler. This way, when the process runs it behaves as if it had called its own signal handler by itself before being suspended.

  • The initial steps of user space signal management are taken care of inside libc, which eventually calls the real process' signal handling routines which, in turn, execute our stack backtrace function.

As a consequence of this mechanism, the first two entries in the stack frame chain when you get into the signal handler contain, respectively, a return address inside your signal handler and one inside sigaction() in libc. The stack frame of the last function called before the signal (which, in case of fault signals, also is the one that supposedly caused the problem) is lost. Thus, if function B called function A, which in turn caused a SIGSEGV, a plain backtrace would list these entry points:

your_sig_handler()
sigaction() in libc.so
func_B()
main()

and no trace of the call to function A would be found. For more details, have a look at the manuals for signal() and sigaction().

Back to Backtrace

In order to get a meaningful backtrace, we need a workaround. Luckily, when you have the sources of both the kernel and libc, you can find a workaround for nearly anything. In Listing 2 we exploit an undocumented parameter of type sigcontext that is passed to the signal handler (see the UNDOCUMENTED section in man sigaction) and contains, among other things, the value of EIP when the signal was raised. After the call to backtrace(), we use this value to overwrite the useless entry corresponding to the sigaction() return address in the trace array. When we later call backtrace_symbols(), the address we inserted is resolved the same as any other entry in the array. Finally, when we print the backtrace, we start from the second entry (i=1 in the loop), because the first one always would be inside our signal handler.

Listing 2. Using sigcontext

Since kernel version 2.2 the undocumented parameter to the signal handler has been declared obsolete in adherence with POSIX.1b. A more correct way to retrieve additional information is to use the SA_SIGINFO option when setting the handler, as shown in Listing 3 and documented in the man page. Unfortunately, the siginfo_t structure provided to the handler does not contain the EIP value we need, so we are forced to resort again to an undocumented feature: the third parameter to the signal handler. No man page is going to tell you that such a parameter points to an ucontext_t structure that contains the values of the CPU registers when the signal was raised. From this structure, we are able to extract the value of EIP and proceed as in the previous case.

Listing 3. Using the SA_SIGINFO Option

Hazards and Limitations

A couple of points are important to keep in mind when you use the backtrace functions. First, backtrace_symbols() internally calls malloc() and, thus, can fail if the memory heap is corrupted--which might be the case if you are dealing with a fault signal handler. If you need to resolve the return addresses in such a situation, calling backtrace_symbols_fd() is safer, because it directly writes to the given file descriptor without allocating memory. The same reasoning implies that it is safer to use either static or automatic (non dynamic) storage space for the array passed to backtrace().

Also, there are some limitations to the ability of automatically tracing back the execution of a program. The most relevant are some compiler optimizations that, in one way or another, alter the contents of the stack frame or even prevent a function from having one (think of function inlining). Obviously, the stack frame does not even exist for macros, which are not function calls at all. Finally, a stack backtrace is impossible to perform if the stack itself has been corrupted by a memory trash.

Regarding symbol resolution, the current glibc (version 2.3.1 at the time of this writing) allows users to obtain the function name and offset only on systems based on the ELF binary format. Furthermore, static symbols' names cannot be resolved internally, because they cannot be accessed by the dynamic linking facilities. In this case, the external command addr2line can be used instead.

Inner Workings

In case you wonder how would you access stack information in a C program, the answer is simple: you can't. Stack handling, in fact, depends heavily on the platform your program runs on, and the C language does not provide any means to do it in a standard way. The implementation of backtrace() in the glibc library contains platform-specific code for each platform, which is based either on GCC internal variables (__builtin_frame_address and __builtin_return_address) or on assembly code.

In the case of the i386 platform (in glibc-x.x.x/sysdeps/i386/backtrace.c), a couple of lines of assembly code are used to access the contents of the ebp and esp CPU registers, which hold the address of the current stack frame and of the stack pointer for any given function:

register void *ebp __asm__ ("ebp");
register void *esp __asm__ ("esp");

Starting from the value of ebp, it is easy to follow the chain of pointers and move up to the initial stack frame. In this way you gather the sequence of return addresses and build the backtrace.

At this point, you still have to resolve the return addresses into function names, an operation dependent on the binary format you are using. In the case of ELF, it is performed by using a dynamic linker internal function (_dl_addr(), see glibc-x.x.x/sysdeps/generic/elf/backtracesyms.c).

Conclusion

Are you working on a complex program that contains a lot of different execution paths that make you cluelessly wander through hundreds of functions, desperately trying to understand which one called which other function? Wander no more and print a backtrace. It's free, fast and easy. While you are at it, do yourself a favour and also use that function inside a fault signal handler--it's guaranteed to help you with those nasty bugs that appear once in a thousand runs.

Gianluca Insolvibile has been a Linux enthusiast since kernel 0.99pl4. He currently deals with networking and digital video research and development.

Interactive Linux Kernel map
















Java script 版

Resources for Tracing Linux

Unix下工具
grep: “grep [option] …pattern… [file] ”
ex : grep –ri return rtlcore.c
無敵的指令,雖然可能找很久
ctags: “find -name ‘*.[ch]’ | xargs ctags”
這樣檔案tags裡就有各種關鍵字的索引了
cflow: “cflow –r fun_name *.c”
產生call graph
c2html: “c2html <> file.html”

結構圖繪制工具
Jude
http://objectclub.esm.co.jp/Jude/

Sunday, February 22, 2009

耶和華所賜的福使人富足,並不加上憂愁。

耶和華所賜的福使人富足,並不加上憂愁。(箴言10:22)

The blessing of the LORD brings wealth,
and he adds no trouble to it. (Proverbs 10:22)

Saturday, February 07, 2009

x86 32bit call backtrace

需include

driver可以用dump_stack() 這個func來back trace
不過還沒有試過,能不能work還未知

底下是x86 32bit的實作
http://lxr.linux.no/linux+v2.6.27.2/arch/x86/kernel/traps_32.c#L288
288void dump_stack(void)
289{
290 unsigned long bp = 0;
291 unsigned long stack;
292
293#ifdef CONFIG_FRAME_POINTER
294 if (!bp)
295 asm("movl %%ebp, %0" : "=r" (bp):);
296#endif
297
298 printk("Pid: %d, comm: %.20s %s %s %.*s\n",
299 current->pid, current->comm, print_tainted(),
300 init_utsname()->release,
301 (int)strcspn(init_utsname()->version, " "),
302 init_utsname()->version);
303
304 show_trace(current, NULL, &stack, bp);
305}
306
307EXPORT_SYMBOL(dump_stack);

Thursday, February 05, 2009

Wednesday, February 04, 2009

用Open Source工具開發軟體: 新軟體開發關念

http://www.freebsd.org.hk/html/cyril/x1249.html

http://lxr.linux.no/linux+v2.6.27.2/arch/x86/kernel/traps_32.c#L453

Enable Backtrace

Enable Backtrace

April 18th, 2007 · 3 Comments

最近 Linux kernel 2.4 的 Backtrace 不見了, 這樣實在非常不好 Debug,
查了一下才發現, 少加了一些 Flags.

原來會顯示(部份)

Process swapper (pid: 1, stack limit = 0xc030a368)
Stack: (0xc030bfc0 to 0xc030c000)
bfc0: c0042410 c00424a8 c030a000 c020f558 c0011118 c030a000 c020f558 c0200d10
bfe0: 000191cc c00430d8 c00430a4 c01fe000 c0237948 c0046974 c4a57686 1b469a8c
Backtrace: no frame pointer
Code: c0237ba0 e92d40f0 e3a04000 e1a05004 (e5854000)
Kernel panic: Attempted to kill init!

這時是沒有 frame pointer , 這時在 compile 時加上參數 -fomit-frame-pointer
就可以了. 以 Linux 2.4 ARM Platform 為例, 請在 arch/arm/Makefile 的 cflags 加上
這個參數
加了以後, 程式有問題就會顯示

Process swapper (pid: 1, stack limit = 0xc0efa368)
Stack: (0xc0efbf9c to 0xc0efc000)
bf80: c0042a70
bfa0: c0042b08 c0efa000 c0217558 c0efbfd8 c0efbfbc c00114cc c0011820 c0efa000
bfc0: c0217558 c0208d10 00019834 c0efbff4 c0efbfdc c00430e0 c00114c8 c00430a4
bfe0: c0206000 c023f948 00000000 c0efbff8 c0046a54 c00430b4 00000000 00000000
Backtrace:
Function entered at [] from []
r7 = C0217558 r6 = C0EFA000 r5 = C0042B08 r4 = C0042A70
Function entered at [] from []
Function entered at [] from []
r6 = C023F948 r5 = C0206000 r4 = C00430A4
Code: e92dd8f0 e24cb004 e3a04000 e1a05004 (e5854000)
Kernel panic: Attempted to kill init!

但是這一堆 Code, 實在是看不懂, 那要怎麼辦呢? 還好 Linux kernel 2.4 有提供一個
Tool: ksymoops 可以用.
執行指令
ksymoops -m System.map

將 Backtrace 那一段貼上去

Backtrace:
Function entered at [] from []
r7 = C0217558 r6 = C0EFA000 r5 = C0042B08 r4 = C0042A70
Function entered at [] from []
Function entered at [] from []
r6 = C023F948 r5 = C0206000 r4 = C00430A4
Code: e92dd8f0 e24cb004 e3a04000 e1a05004 (e5854000)
Kernel panic: Attempted to kill init!
Backtrace:
Function entered at [] from []
r7 = C0217558 r6 = C0EFA000 r5 = C0042B08 r4 = C0042A70
Function entered at [] from []
Function entered at [] from []
r6 = C023F948 r5 = C0206000 r4 = C00430A4
Code: e92dd8f0 e24cb004 e3a04000 e1a05004 (e5854000)


就會得到結果

Trace; c0011810 <$a+0/0>
Trace; c00114cc

>>r7; c0217558 <__machine_arch_type+0/4>
>>r5; c0042b08 <__initcall_end+0/4f8>
>>r4; c0042a70 <$d+0/0>

Trace; c00114b8 <$a+0/0>
Trace; c00430e0
Trace; c00430a4
Trace; c0046a54

>>r6; c023f948
>>r5; c0206000
>>r4; c00430a4

Code; c0011814
00000000 <_eip>:
Code; c0011814
0: f0 d8 2d e9 04 b0 4c lock fsubrs 0×4cb004e9
Code; c001181b
7: e2 00 loop 9 <_eip+0×9>
Code; c001181d
9: 40 inc %eax
Code; c001181e
a: a0 e3 04 50 a0 mov 0xa05004e3,%al
Code; c0011823
f: e1 00 loope 11 <_eip+0×11>
Code; c0011825
11: 40 inc %eax
Code; c0011826
12: 85 e5 test %esp,%ebp

Kernel panic: Attempted to kill init!


這樣就可以清楚的知道, 到底是那邊發生問題了.
Linux kernel 2.6 己經內建在 Option 內了, 在 Option “[ ] Configure standard kernel features (for small systems) “, Enable 後就看得到了.

雖然是小問題, 不過也是要找一下的.

ref.
Jserv
GCC 函式追蹤功能
Linux Device Driver 3 Chapter 4. Debugging Techniques

[Tags] Linux kernel, Debug [/Tags]

做出自己的back trace function

相信有在用arm linux的, 應該對kernel panic不陌生吧~~ 在你對kernel做了一些無法挽救的錯事後, kernel叫了一聲"Oops~~",然後就死在路邊~~ 不過幸運的事, 通常kernel會在死之前留下一些"線索", 好讓你跟隨這些線索找出些端倪...

舉例來說, 我故意在我的init module 裡插入存取非法位址的動作, kernel果然就掛在那邊並且印出:

Unable to handle kernel paging request at virtual address dcc01120
pgd = c0004000
[dcc01120] *pgd=00000000
Internal error: Oops: 805 [#1]
Modules linked in:
CPU: 0
PC is at audio_aic32_init+0x44/0x1cc
LR is at 0x1
pc : [] lr : [<00000001>] Not tainted
sp : c0387fc4 ip : 60000013 fp : c0387fd4
r10: 00000000 r9 : 00000000 r8 : 00000000
r7 : c001f70c r6 : 00000000 r5 : c0386000 r4 : 00000000
r3 : fbbc0000 r2 : e1041128 r1 : 00000001 r0 : e1041120
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment kernel
Control: 5317F Table: 80004000 DAC: 00000017
Process swapper (pid: 1, stack limit = 0xc0386198)
Stack: (0xc0387fc4 to 0xc0388000)
7fc0: c001f6dc c0387ff4 c0387fd8 c0076290 c001d00c 00000000 00000000
7fe0: 00000000 00000000 00000000 c0387ff8 c008d738 c007620c 00000000 00000000
Backtrace:
[] (audio_aic32_init+0x0/0x1cc) from [] (init+0x94/0x1e0)
r4 = C001F6DC
[] (init+0x0/0x1e0) from [] (do_exit+0x0/0xdc0)
r7 = 00000000 r6 = 00000000 r5 = 00000000 r4 = 00000000
Code: e59f3174 e3a01001 e5801000 e2422d25 (e7801003)
<0>Kernel panic - not syncing: Attempted to kill init!


嘿嘿, 很明顯的kernel就是死在audio_aic32_init,

我們再去仔細看看這個function即可;
這麼說起來, kernel的這個機制還真好用, 能讓我們了解掛掉的時候,

是由哪幾個function call下來的,

那麼...我們有沒有辦法拿來用呢?

有時你會想知道, kernel到底何時, 又是從哪呼叫到這個function;
又有時你會想知道, kernel到底怎麼call到我們function裡的,

我明明只是在結構中加入 .probe= probe_function,
kernel卻能找到我的probe_function, 到底是從哪進入的呢,

當然你也可以一層一行的去追code去printk,
但是如果能善用kernel的這個"線索"function, 想必能省力不少

好, 既然要用這個工具,

讓我們先找看看他被放在kernel source tree的哪裡
用lxr search看看, 發現他是位於/arch/arm/kernel/traps.c
從die開始(但是如何跑到die的呢? 我猜是fault interrupt),接著die()->dump_backtrace()->c_backtrace(),
但是lxr search不到c_backtrace不到, 按照慣例,

應該是一個assembly functon,
去arch/arm/lib/裡找找, 果然找到一個backtrace.S,

c_backtrace就在裡面,
有興趣的可以去看看他是怎麼寫的, 我則只想知道怎麼用它

最簡單的方式就是 看dump_backtrace()是怎麼call c_backtrace()的, 我們跟著做, 這樣應該就能印出資訊吧
在dump_backtrace()裡:

157 static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
158 {
159 unsigned int fp;
160 int ok = 1;
161
162 printk("Backtrace: ");
163 fp = regs->ARM_fp;
164 if (!fp) {
165 printk("no frame pointer");
166 ok = 0;
167 } else if (verify_stack(fp)) {
168 printk("invalid frame pointer 0x%08x", fp);
169 ok = 0;
170 } else if (fp < (unsigned long)(tsk->thread_info + 1))
171 printk("frame pointer underflow");
172 printk("\n");
173
174 if (ok)
175 c_backtrace(fp, processor_mode(regs));
176 }


其中 c_backtrace需要2個參數, 一個是fp,

一個是processor目前的mode
processor 目前的mode倒是比較容易解決,

因為我們driver都是在system mode, 所以傳入固定0x1f即可

至於fp呢, 要取得就比較麻煩啦, 要先get register的值, 我的做法如下

static void load_regs( struct pt_regs *ptr )
{
asm volatile(
"stmia %0, {r0 - r15}\n\t"
:
: "r" (ptr)
: "memory"
);
}


最後則是寫成一個function 讓別人call啦~

void back_trace( void )
{
struct pt_regs *ptr;
unsigned int fp;
unsigned long flags;

ptr = kmalloc( sizeof( struct pt_regs ), GFP_KERNEL);

local_irq_save(flags);

printk("\n\nstart back trace...");
load_regs( ptr );
fp = ptr->ARM_fp;
c_backtrace(fp, SYS_MODE);
printk("back trace end...\n\n");

local_irq_restore(flags);

kfree ( ptr );
}
EXPORT_SYMBOL( back_trace);


如此, 我們只要在driver中加入back_trace();

就可以知道整個來龍去脈啦

舉個例子, 在audio 的probe function中加入back_trace();
則會印出

start back trace...
[] (back_trace+0x0/0x68) from [] (davinci_aic32_probe+0x3c/0x144)
r5 = C0293EE8 r4 = 00000000
[] (davinci_aic32_probe+0x0/0x144) from [] (audio_probe+0x24/0x2c)
r5 = C02471B4 r4 = C024725C
[] (audio_probe+0x0/0x2c) from [] (driver_probe_device+0x54/0x74)
[] (driver_probe_device+0x0/0x74) from [] (driver_attach+0x54/0x90)
r5 = C024725C r4 = C02471BC
[] (driver_attach+0x0/0x90) from [] (bus_add_driver+0x78/0x120)
r6 = C024725C r5 = C0293E48 r4 = C0243C34
[] (bus_add_driver+0x0/0x120) from [] (audio_register_codec+0xbc/0xe8)
[] (audio_register_codec+0x0/0xe8) from [] (audio_aic32_init+0x14/0x1c4)
r6 = 00000000 r5 = C0386000 r4 = C001F6DC
[] (audio_aic32_init+0x0/0x1c4) from [] (init+0x94/0x1e0)
r4 = C001F6DC
[] (init+0x0/0x1e0) from [] (do_exit+0x0/0xdc0)
r7 = 00000000 r6 = 00000000 r5 = 00000000 r4 = 00000000
back trace end...


如此,整個流程就清清楚楚了~~~

linux kernel source code

一個source code分析與討論的好網站
http://linux.chinaunix.net/bbs/forum-8-1.html

已完成--基于LINUX内核中的TCP/IP的核心过程分析
http://linux.chinaunix.net/bbs/thread-1049757-1-2.html

如何阅读内核网络部分的代码?
http://linux.chinaunix.net/bbs/thread-1058937-1-2.html

TCP协议内核源码分析第一册v1.0.chm (671.73 KB)
http://linux.chinaunix.net/bbs/thread-1054108-1-5.html

网卡驱动注册到PCI总线这一过程的分析
http://linux.chinaunix.net/bbs/thread-1052717-1-4.html



个人对kobject的一点研究
http://linux.chinaunix.net/bbs/thread-1058833-1-2.html