1. 程式人生 > 其它 >應用層列印PC指標LR指標解決段錯誤總結【轉】

應用層列印PC指標LR指標解決段錯誤總結【轉】

轉自:https://blog.csdn.net/sdsszk/article/details/109765180?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

1.需求的產生
寫程式難免會出現段錯誤的情況,這時候很想知道,到底在什麼地方崩潰了,對於程式碼很少,或者你很有把握的時候,或許用二分法配合printf就可以搞定了;而對於非常複雜的程式碼,比如像Xserver這樣的程式,可能就不太好定位了;
(本文討論的情況都是針對arm環境,並且gdb不方便使用的情況)

2. 解決思路 思路其實很簡單,對於使用者態段錯誤的原因,大約可以分為兩種, a) 沒有許可權訪問這個地址; b) 訪問的地址沒有對映,比如NULL地址; 當出現這兩種情況的時候,linux核心都會向用戶態的程式傳送SIGSEGV的訊號,於是程式執行預設的訊號處理函式,就退出了; 所以有兩個解決辦法: A) 在核心裡面把這些暫存器打印出來; B) 在上層程式裡面把暫存器打印出來; 下面來分別說明: 3. 核心資訊列印 核心的執行路徑如下: 我們只需要在__do_user_fault的時候把列印資訊開啟就可以了,如下: #ifdef CONFIG_DEBUG_USER if (user_debug & UDBG_SEGV) { printk(KERN_DEBUG
"%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n", tsk->comm, sig, addr, fsr); show_pte(tsk->mm, addr); show_regs(regs); } #endif 改成 printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n", tsk->comm, sig, addr, fsr); show_pte(tsk->mm, addr); show_regs(regs); 裡面會打印出pc暫存器的值,有了這個就可以定位了。
4. 使用者態資訊列印 這個做法的主要思路就是先攔截SIGSEGV訊號,然後在訊號處理函式裡面列印資訊,訊號攔截程式碼如下: static void catch_sigsegv() { struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_sigaction = sigsegv_handler; action.sa_flags = SA_SIGINFO; if(sigaction(SIGSEGV, &action, NULL) < 0) { perror("sigaction"); } } 只需要在main函式裡面加入這個函式就可以了: main(…) { …. catch_sigsegv(); … } 下面來看看這個處理函式sigsegv_handler是怎麼寫的,程式碼如下: static void sigsegv_handler(int signum, siginfo_t* info, void*ptr) { static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"}; int i; ucontext_t *ucontext = (ucontext_t*)ptr; void *bt[100]; char **strings; printf("Segmentation Fault Trace:\n"); printf("info.si_signo = %d\n", signum); printf("info.si_errno = %d\n", info->si_errno); printf("info.si_code = %d (%s)\n", info->si_code, si_codes[info->si_code]); printf("info.si_addr = %p\n", info->si_addr); /*for arm*/ printf("the arm_fp 0x%3x\n",ucontext->uc_mcontext.arm_fp); printf("the arm_ip 0x%3x\n",ucontext->uc_mcontext.arm_ip); printf("the arm_sp 0x%3x\n",ucontext->uc_mcontext.arm_sp); printf("the arm_lr 0x%3x\n",ucontext->uc_mcontext.arm_lr); printf("the arm_pc 0x%3x\n",ucontext->uc_mcontext.arm_pc); printf("the arm_cpsr 0x%3x\n",ucontext->uc_mcontext.arm_cpsr); printf("the falut_address 0x%3x\n",ucontext->uc_mcontext.fault_address); **/*backtrace函式有的系統不支援,如hisi平臺/FH平臺等不支援*/** printf("Stack trace (non-dedicated):"); int sz = backtrace(bt, 20); printf("the stack trace is %d\n",sz); strings = backtrace_symbols(bt, sz); for(i = 0; i < sz; ++i) { printf("%s\n", strings[i]); } _exit (-1); } 測試程式碼如下: void test_segv() { char *i=0; *i=10;//產生段錯誤的位置 } void cause_segv() { printf("this is the cause_segv\n"); test_segv();//呼叫函式 } int main(int argc,char **argv) { catch_sigsegv();//初始化註冊捕捉函式 cause_segv(); return 0; } 編譯方法: gcc segment_trace.c -g –rdynamic –o segment_trace 執行: ./segment_trace 輸出如下: this is the catch_sigsegv Segmentation Fault Trace: info.si_signo = 11 info.si_errno = 0 info.si_code = 1 (SEGV_MAPERR) info.si_addr = (nil) the arm_fp 0xb7f8a3d4 the arm_ip 0xb7f8a3d8 the arm_sp 0xb7f8a3c0 the arm_lr 0x8998 the arm_pc 0x8974 the arm_cpsr 0x60000010 the falut_address 0x 0 Stack trace (non-dedicated):the stack trace is 5 ./segment_trace(backtrace_symbols+0x1c8) [0x8844] /lib/libc.so.6(__default_rt_sa_restorer+0) [0xb5e22230] ./segment_trace(cause_segv+0x18) [0x8998] ./segment_trace(main+0x20) [0x89c0] /lib/libc.so.6(__libc_start_main+0x108) [0xb5e0c10c] 5. 輸出資訊分析 根據上面的輸出可以看出一些端倪: 根據棧資訊,可以看出是在cause_segv裡面出了問題,但是最後一層棧資訊是看不到的,另外需要根據pc暫存器的值來定位: addr2line -f -e segment_trace 0x8974 test_segv /home/wf/test/segment_trace.c:55 可以看到說是在55行,一看: 剛好是 *i=10; 這一行, 而且可以看出,函式名是test_segv, 所以基本上不需要列印棧資訊,也可以定位了。 6. 注意 這個方法最好不要用在多執行緒環境裡面; 如果打印不出棧資訊,需要在核心中去掉: -fomit-frame-pointer編譯選項; ———————————————— 版權宣告:本文為CSDN博主「sdsszk」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。 原文連結:https://blog.csdn.net/sdsszk/article/details/109765180
【作者】張昺華 【出處】http://www.cnblogs.com/sky-heaven/ 【部落格園】 http://www.cnblogs.com/sky-heaven/ 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋轉倒立擺】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自動循跡車】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【大餅教你學系列】https://edu.csdn.net/course/detail/10393 【新浪微博】 張昺華--sky 【twitter】 @sky2030_ 【微信公眾號】 張昺華 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利.