【除錯】列印函式棧,以及由函式指標輸出函式名的方法
Before All
以下皆在linux環境下。windows上用vs可隨時檢視函式棧。
使用者態
列印函式棧
使用backtrace()相關函式來達到輸出函式棧的目的,man backtrace檢視詳細的引數,返回值等資訊。
以下測試例,編譯時需加上-rdynamic選項:
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#define SIZE 100
void myfunc3(void)
{
int nptrs;
void *buffer[100 ];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* 1 means standard output */
backtrace_symbols_fd(buffer, nptrs, 1);
/* or use backtrace_symbols() */
/*
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (j = 0; j < nptrs; j++)
printf("%s\n", strings[j]);
free(strings);
// */
}
static void /* "static" means don't export the symbol... */
myfunc2()
{
myfunc3();
}
void myfunc()
{
myfunc2();
}
int main()
{
myfunc();
return 0;
}
簡單說下這3個函式的用法:
int backtrace(void **buffer, int size);
backtrace()把函式棧中的函式地址寫到buffer陣列,buffer陣列的成員即void *型別。size表示把棧頂size個函式地址搞出來,如果想把函式棧完全搞出來,確保buffer陣列和size足夠大。
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
backtrace_symbols()或者backtrace_symbols_fd()與上邊的backtrace()配合使用,起一個翻譯作用,即把函式地址翻譯為函式名(還有函式的偏移和返回地址)。backtrace_symbols()把buffer陣列中的size個元素翻譯為函式名,把字串陣列儲存在返回值char **中,自帶了malloc功能,用完需把返回值free,見測試例。backtrace_symbols_fd()前兩個引數和前者一樣,但是把結果字串陣列輸出到fd中,如fd=1,即輸出到螢幕。
由函式指標得到函式名
根據以上可以看出,backtrace_symbols和backtrace_symbols_fd可以用來將函式指標轉換為函式名,函式指標的講解可以參考這裡,以下是測試例:
#include<stdio.h>
#include<execinfo.h>
void hello_world()
{
printf("hello world!\n");
}
int main()
{
void *func = hello_world; //注意這裡必須轉化為一個void *指標
backtrace_symbols_fd(&func, 1, 1);
return 0;
}
這裡就把函式指標func的函式名輸出了出來。
核心態
列印函式棧
使用dump_stack()函式。直接在需要檢視函式棧的位置加入這一句,然後dmesg中就可以看到函式棧了。
由函式指標得到函式名
核心中可以直接使用printk的%pf,%pF選項,%pF選項多列印偏移地址。測試例:
void testname()
{
return;
}
void test()
{
void (*funcptr) = testname;
printk("%pf\n", funcptr);
}
小結
以上就是在使用者態,核心態輸出函式棧,以及由函式指標輸出函式名的方法,在除錯的過程中有時能幫助加深對程式碼的理解。