hg888皇冠手机登录

嵌入式 linux下利用backtrace追踪函数调用旅馆以致牢固段错误

十一月 15th, 2019  |  www.hg888.com

C语言的段错误大概是各样人都蒙受过,整理一下,以供就学之用。

luck@geekard:~/codes/12.21$ gdb  //不带参数运维gdb调节和测验程序
GNU gdb (GDB) 7.2-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
<>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type “show
copying”
and “show warranty” for details.
This GDB was configured as “i686-linux-gnu”.
For bug reporting instructions, please see:
<>.
(gdb) file
./myls                    //输入file命令和你的可实践文件名和路子,这里为当前目录下的myls文件
Reading symbols from /home/luck/codes/12.21/myls…done.
(gdb) run -ld ./                       //带参数(这里为 -ld
./)运营r(run)程序,那和在bash命令行上奉行:./myls -ld ./效果时同样的。
Starting program: /home/luck/codes/12.21/myls -ld ./
longlist 1, typelist 0, dirlist 1, filename
./            //myls程序的出口

貌似察看函数运维时货仓的不二法门是运用GDB(bt命令卡塔 尔(英语:State of Qatar)之类的外表调节和测量试验器,不过,有个别时候为了剖析程序的BUG,(首要针对长日子运作程序的剖析),在前后相继出错开上下班时间打字与印刷出函数的调用旅馆是老大实惠的。

 

明日自己施行了八个程序,运营了非常长日子后,段错误了,笔者定位了弹指间,原本是fprintf的主题材料,具体是vfprintf的主题材料。

        unsigned char *ptr = 0x00;
        *ptr = 0x00;        //向内部存款和储蓄器中0x00地址写多少,发生段错误
}

 print?

 

#0  0x0098735e in vfprintf () from /lib/libc.so.6
#1  0x0098e3cf in fprintf () from /lib/libc.so.6
将fprintf换来printf就不曾难点了。

五、gdb调节和测量试验入门

出口如下:

嵌入式 linux下使用backtrace追踪函数调用仓库以致稳固段错误

2015-05-27 14:19 184人阅读 评论(0) 收藏 举报

www.hg888.com 1 分类:

 

嵌入式(928) www.hg888.com 2

相近察看函数运营时货仓的措施是使用GDB(bt命令卡塔尔国之类的外界调试器,不过,有个别时候为了深入分析程序的BUG,(首要针对长日子运作程序的解析),在程序出错开上下班时间打字与印刷出函数的调用货仓是这一个实用的。

在glibc头文件”execinfo.h”中宣称了四个函数用于获取当前线程的函数调用堆栈。

 

[cpp] view
plaincopy

 

  1. int backtrace(void **buffer,int size)  

[cpp] view
plain copy

 

 www.hg888.com 3www.hg888.com 4

  1. <span style=”font-size:12px;”>int backtrace(void **buffer,int size)</span>  

该函数用于获取当前线程的调用旅馆,获取的音信将会被寄存在buffer中,它是一个指针列表。参数 size 用来钦赐buffer中得以保留多少个void* 成分。函数重回值是实在获得的指针个数,最大不抢先size大小

在buffer中的指针实际是从货仓中赢得的回来地址,每八个库房框架有一个赶回地址

潜心:某个编写翻译器的优化增选对获取科学的调用仓库有和弄,此外内联函数没有酒馆框架;删除框架指针也会引致力不胜任准确分析仓库内容

 

[cpp] view
plaincopy

 

  1. char ** backtrace_symbols (void *const *buffer, int size)  

[cpp] view
plain copy

 

 www.hg888.com 5www.hg888.com 6

  1. <span style=”font-size:12px;”>char ** backtrace_symbols (void *const *buffer, int size)</span>  

backtrace_symbols将从backtrace函数获取的音信转变为一个字符串数组. 参数buffer应该是从backtrace函数获取的指针数组,size是该数组中的成分个数(backtrace的重临值)   
   
函数重返值是叁个针对性字符串数组的指针,它的大小同buffer近似.每种字符串满含了二个周旋于buffer中对应成分的可打字与印刷消息.它回顾函数名,函数的偏移地址,和实在的归来地址

方今,独有利用ELF二进制格式的次第工夫拿到函数名称和摇头地址.在任何系统,独有16进制的归来地址能被获取.别的,你大概供给传递相应的符号给链接器,以能支撑函数名效用(比方,在运用GNU ld链接器的系统中,你须要传递(-rdynamic), -rdynamic可用来通知链接器将具备符号增添到动态符号表中,如若您的链接器扶助-rdynamic的话,提出将其增加!)

该函数的重回值是通过malloc函数申请的空间,由此调用者必需利用free函数来释放指针.

注意:假如不可能为字符串获取足够的空中等高校函授数的重返值将会为NULL

 

[cpp] view
plaincopy

 

  1. void backtrace_symbols_fd (void *const *buffer, int size, int fd)  

[cpp] view
plain copy

 

 www.hg888.com 7www.hg888.com 8

  1. <span style=”font-size:12px;”>void backtrace_symbols_fd (void *const *buffer, int size, int fd)</span>  

backtrace_symbols_fd与backtrace_symbols 函数有所相符的法力,差别的是它不会给调用者重返字符串数组,而是将结果写入文件陈诉符为fd的文件中,每一种函数对应黄金时代行.它无需调用malloc函数,因而适用于有不小可能率调用该函数会退步的事态

 

下边是glibc中的实例(稍有涂改卡塔 尔(阿拉伯语:قطر‎:

[cpp] view
plaincopy

 

  1. #include <execinfo.h>   
  2. #include <stdio.h>   
  3. #include <stdlib.h>   
  4.   
  5. /* Obtain a backtrace and print it to @code{stdout}. */  
  6. void print_trace (void)  
  7. {  
  8.     void *array[10];  
  9.     size_t size;  
  10.     char **strings;  
  11.     size_t i;  
  12.    
  13.     size = backtrace (array, 10);  
  14.     strings = backtrace_symbols (array, size);  
  15.     if (NULL == strings)  
  16.     {  
  17.         perror(“backtrace_synbols”);  
  18.         Exit(EXIT_FAILURE);  
  19.     }  
  20.   
  21.     printf (“Obtained %zd stack frames.\n”, size);  
  22.   
  23.     for (i = 0; i < size; i++)  
  24.         printf (“%s\n”, strings[i]);  
  25.   
  26.     free (strings);  
  27.     strings = NULL;  
  28. }  
  29.   
  30. /* A dummy function to make the backtrace more interesting. */  
  31. void dummy_function (void)  
  32. {  
  33.     print_trace ();  
  34. }  
  35.   
  36. int main (int argc, char *argv[])  
  37. {  
  38.     dummy_function ();  
  39.     return 0;  
  40. }  

[cpp] view
plain copy

 

 www.hg888.com 9www.hg888.com 10

  1. <span style=”font-size:12px;”>#include <execinfo.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4.   
  5. /* Obtain a backtrace and print it to @code{stdout}. */  
  6. void print_trace (void)  
  7. {  
  8.     void *array[10];  
  9.     size_t size;  
  10.     char **strings;  
  11.     size_t i;  
  12.    
  13.     size = backtrace (array, 10);  
  14.     strings = backtrace_www.hg888.com,symbols (array, size);  
  15.     if (NULL == strings)  
  16.     {  
  17.         perror(“backtrace_synbols”);  
  18.         Exit(EXIT_FAILURE);  
  19.     }  
  20.   
  21.     printf (“Obtained %zd stack frames.\n”, size);  
  22.   
  23.     for (i = 0; i < size; i++)  
  24.         printf (“%s\n”, strings[i]);  
  25.   
  26.     free (strings);  
  27.     strings = NULL;  
  28. }  
  29.   
  30. /* A dummy function to make the backtrace more interesting. */  
  31. void dummy_function (void)  
  32. {  
  33.     print_trace ();  
  34. }  
  35.   
  36. int main (int argc, char *argv[])  
  37. {  
  38.     dummy_function ();  
  39.     return 0;  
  40. }</span>  

输出如下:

[cpp] view
plaincopy

 

  1. Obtained 4 stack frames.  
  2. ./execinfo() [0x80484dd]  
  3. ./execinfo() [0x8048549]  
  4. ./execinfo() [0x8048556]  
  5. /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x70a113]  

[cpp] view
plain copy

 

 www.hg888.com 11www.hg888.com 12

  1. <span style=”font-size:12px;”>Obtained 4 stack frames.  
  2. ./execinfo() [0x80484dd]  
  3. ./execinfo() [0x8048549]  
  4. ./execinfo() [0x8048556]  
  5. /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x70a113]  
  6. </span>  

 

小编们还足以接纳那backtrace来牢固段错误地点。

通常状态系,程序发生段错误时系统会发送SIGSEGV时限信号给程序,缺省管理是脱离函数。大家得以应用 signal(SIGSEGV, &your_function);函数来接管SIGSEGV确定性信号的拍卖,程序在发生段错误后,自动调用我们希图好的函数,进而在老大函数里来获取当前函数调用栈。

譬释迦牟尼讲如下:

[cpp] view
plaincopy

 

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <stddef.h>   
  4. #include <execinfo.h>   
  5. #include <signal.h>   
  6.   
  7. void dump(int signo)  
  8. {  
  9.     void *buffer[30] = {0};  
  10.     size_t size;  
  11.     char **strings = NULL;  
  12.     size_t i = 0;  
  13.   
  14.     size = backtrace(buffer, 30);  
  15.     fprintf(stdout, “Obtained %zd stack frames.nm\n”, size);  
  16.     strings = backtrace_symbols(buffer, size);  
  17.     if (strings == NULL)  
  18.     {  
  19.         perror(“backtrace_symbols.”);  
  20.         exit(EXIT_FAILURE);  
  21.     }  
  22.       
  23.     for (i = 0; i < size; i++)  
  24.     {  
  25.         fprintf(stdout, “%s\n”, strings[i]);  
  26.     }  
  27.     free(strings);  
  28.     strings = NULL;  
  29.     exit(0);  
  30. }  
  31.   
  32. void func_c()  
  33. {  
  34.     *((volatile char *)0x0) = 0x9999;  
  35. }  
  36.   
  37. void func_b()  
  38. {  
  39.     func_c();  
  40. }  
  41.   
  42. void func_a()  
  43. {  
  44.     func_b();  
  45. }  
  46.   
  47. int main(int argc, const char *argv[])  
  48. {  
  49.     if (signal(SIGSEGV, dump) == SIG_ERR)  
  50.         perror(“can’t catch SIGSEGV”);  
  51.     func_a();  
  52.     return 0;  
  53. }  

[cpp] view
plain copy

 

 www.hg888.com 13www.hg888.com 14

  1. <span style=”font-size:12px;”>#include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <stddef.h>  
  4. #include <execinfo.h>  
  5. #include <signal.h>  
  6.   
  7. void dump(int signo)  
  8. {  
  9.     void *buffer[30] = {0};  
  10.     size_t size;  
  11.     char **strings = NULL;  
  12.     size_t i = 0;  
  13.   
  14.     size = backtrace(buffer, 30);  
  15.     fprintf(stdout, “Obtained %zd stack frames.nm\n”, size);  
  16.     strings = backtrace_symbols(buffer, size);  
  17.     if (strings == NULL)  
  18.     {  
  19.         perror(“backtrace_symbols.”);  
  20.         exit(EXIT_FAILURE);  
  21.     }  
  22.       
  23.     for (i = 0; i < size; i++)  
  24.     {  
  25.         fprintf(stdout, “%s\n”, strings[i]);  
  26.     }  
  27.     free(strings);  
  28.     strings = NULL;  
  29.     exit(0);  
  30. }  
  31.   
  32. void func_c()  
  33. {  
  34.     *((volatile char *)0x0) = 0x9999;  
  35. }  
  36.   
  37. void func_b()  
  38. {  
  39.     func_c();  
  40. }  
  41.   
  42. void func_a()  
  43. {  
  44.     func_b();  
  45. }  
  46.   
  47. int main(int argc, const char *argv[])  
  48. {  
  49.     if (signal(SIGSEGV, dump) == SIG_ERR)  
  50.         perror(“can’t catch SIGSEGV”);  
  51.     func_a();  
  52.     return 0;  
  53. }</span>  

 

编写翻译程序:

gcc -g -rdynamic test.c -o test; ./test

输出如下:

[cpp] view
plaincopy

 

  1. Obtained6stackframes.nm  
  2. ./backstrace_debug(dump+0x45)[0x80487c9]  
  3. [0x468400]  
  4. ./backstrace_debug(func_b+0x8)[0x804888c]  
  5. ./backstrace_debug(func_a+0x8)[0x8048896]  
  6. ./backstrace_debug(main+0x33)[0x80488cb]  
  7. /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x129113]  

[cpp] view
plain copy

 

 www.hg888.com 15www.hg888.com 16

  1. <span style=”font-size:12px;”>Obtained6stackframes.nm  
  2. ./backstrace_debug(dump+0x45)[0x80487c9]  
  3. [0x468400]  
  4. ./backstrace_debug(func_b+0x8)[0x804888c]  
  5. ./backstrace_debug(func_a+0x8)[0x8048896]  
  6. ./backstrace_debug(main+0x33)[0x80488cb]  
  7. /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x129113]</span>  

 (这里有个难点:
数十次运营的结果是/lib/i368-linux-gnu/libc.so.6和[0x468400]的归来地址是转变的,但不变的是后二位,
不明白为啥卡塔 尔(阿拉伯语:قطر‎

接着:

objdump -d test > test.s

在test.s中搜索804888c如下:

 

[cpp] view
plaincopy

 

  1. 8048884 <func_b>:  
  2. 8048884:    55              push %ebp  
  3. 8048885:    89 e5            mov %esp, %ebp  
  4. 8048887:    e8 eb ff ff ff       call 8048877 <func_c>  
  5. 804888c:    5d                pop %ebp  
  6. 804888d:    c3                ret  

[cpp] view
plain copy

 

 www.hg888.com 17www.hg888.com 18

  1. <span style=”font-size:12px;”>8048884 <func_b>:  
  2. 8048884:    55              push %ebp  
  3. 8048885:    89 e5            mov %esp, %ebp  
  4. 8048887:    e8 eb ff ff ff       call 8048877 <func_c>  
  5. 804888c:    5d                pop %ebp  
  6. 804888d:    c3                ret</span>  

其间80488c时调用(call 8048877卡塔尔C函数后之处,就算并不曾直接固定到C函数,通过汇编代码, 基本得以推出是C函数出难题了(pop指令不会促成段错误的卡塔尔国。

我们也能够经过addr2line来查阅

[cpp] view
plaincopy

 

  1. addr2line 0x804888c -e backstrace_debug -f  

[cpp] view
plain copy

 

 www.hg888.com 19www.hg888.com 20

  1. <span style=”font-size:12px;”>addr2line 0x804888c -e backstrace_debug -f</span>  

输出:

[cpp] view
plaincopy

 

  1. func_b  
  2. /home/astrol/c/backstrace_debug.c:57  

[cpp] view
plain copy

 

 www.hg888.com 21www.hg888.com 22

  1. <span style=”font-size:12px;”>func_b  
  2. /home/astrol/c/backstrace_debug.c:57  
  3. </span>  

 

以下是简单的backtrace原理完成:

 

[cpp] view
plaincopy

 

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <string.h>   
  4.   
  5. #define LEN 4   
  6. #define FILENAME “stack”   
  7.   
  8. int backtrace(void **buffer, int size)  
  9. {  
  10.     int i = 0;  
  11.     unsigned long int reg_eip = 0;  
  12.     unsigned long int reg_ebp = 0;  
  13.     char cmd[size][64];  
  14.   
  15.     memset(cmd, 0, size * 64);  
  16.     __asm__ volatile (  
  17.         /* get current EBP */  
  18.         “movl %%ebp, %0 \n\t”  
  19.         :”=r”(reg_ebp)  /* output register */  
  20.         :       /* input  register */  
  21.         :”memory”   /* cloberred register */  
  22.     );    
  23.   
  24.     for (i = 0; i < size; i++)  
  25.     {  
  26.         reg_eip = *(unsigned long int *)(reg_ebp + 4);  
  27.         reg_ebp = *(unsigned long int *)(reg_ebp);  
  28.         buffer[i] = (void *)reg_eip;  
  29.         fprintf(stderr, “%p -> “, buffer[i]);  
  30.         sprintf(cmd[i], “addr2line %p -e “, buffer[i]);  
  31.         strncat(cmd[i], FILENAME” -f”, strlen(FILENAME)+3);  
  32.         system(cmd[i]);  
  33.         puts(“”);         
  34.     }  
  35.   
  36.     return size;  
  37. }  
  38.   
  39. static void test2(void)  
  40. {  
  41.     int i = 0;  
  42.     void *buffer[LEN] = {0};  
  43.     backtrace(buffer, LEN);  
  44.     return;  
  45. }  
  46.   
  47. static void test1(void)  
  48. {  
  49.     test2();  
  50. }  
  51.   
  52. static void test(void)  
  53. {  
  54.     test1();  
  55. }  
  56.   
  57. int main(int argc, const char *argv[])  
  58. {  
  59.     test();  
  60.     return 0;  
  61. }    

前不久自己试行了四个程序,运营了相当短日子后,段错误了,作者定位了一下…

 风姿罗曼蒂克、背景介绍
本条笔记主要介绍开源的次序调节和测量检验器(gdb)的入门知识,指标是使unix/linux蒙受的编制程序新手能够火速学会运用gdb调节和测验程序的不二等秘书诀,同偶尔候也是对自身动用gdb的一个经历总括。
正文借让你能采纳简易的unix/linux命令并能用gcc(GNU C Compiler, GNU C
语言编写翻译器)编写翻译程序,当然有编制程序涉世更加好。:卡塔 尔(英语:State of Qatar)
为支援您驾驭和操作,作者将选拔本人碰着过的不追求虚名事例来演示使用gdb调节和测量检验有宿疾(bug)的先后进程,你看过那篇笔记后能自个儿出手练一下最棒。

输出如下:

调用栈为:

(gdb) stop                    //截至当前调试
(gdb) break 261              //在第261行设置多少个断点
Breakpoint 1 at 0x8048bf1: file myls-0.0.c, line 261.
(gdb) run  -ld ./           //带参数运路程序(myls)
The program being debugged has been started already.
Start it from the beginning? (y or n) y   //当然yes

 

 

能够看出myls程序的函数调用关系为:
main() —> detailList() —> finalprt 
接下来在标号为0-2的行步向了系统的C库函数,所以产生错误的大概在标号3、4、5指明的函数中。
小编们先看一下最终调用finalprt()函数时也许发生错误的代码行:

[cpp] view
plain copy

同理,sprintf由于也会调用vfprintf,所以应该亦然也可以有标题。

ulimit -c unlimited      
                                         //张开内核的为主转储机制
gcc -g -o outPutName sourceCodeName.c
 //编写翻译时加-g选项,使生成的可实践文件中包括调节和测验音讯
gdb outPutName core                                     
 //运转gdb,能够咋命令行上钦定要调节和测量检验程序
or:  gdb  file  outPutName                              
 //也能够在gdb命令提示符中输入要调整的次第名                        
  
core  core                                                          
//内定程序试行错误时内核生成的转储文件
list  [function]|[row-number]
                           //查看源代码,能够跟函数名或行号
break
[function]|[row-number]                        //设置断点,能够跟函数名或行号
clear [function]|[row-number]                        
//消释断点,能够跟函数名或行号或断点号 
r     [paramiters]                                                /

 print?

 

用gdb调节和测量检验程序笔记: 以段错误(Segmental fault)为例[转]

 

Program received signal SIGSEGV, Segmentation fault. 0x0098735e in
vfprintf () from /lib/libc.so.6

大规模的段错误原因如下:
1卡塔尔国往受到系统一保险证的内部存款和储蓄器地址写多少有些内部存款和储蓄器是根本占用的要么是其余程序正在接收,为了保险系统平常职业,所以会碰到系统的体贴,而不能轻巧寻访
.2卡塔 尔(英语:State of Qatar)内部存储器越界(数组越界,变量类型区别等等)
上面小编以地点的myls程序现身的不当为例介绍用gdb进行调理的办法和进度。

 

自家开头以为是长度难题,招致溢出,后来自己组合了各个方案,发掘不是长度难题,原本是叁个出奇的字符系列引致的难点,那几个元凶祸首正是%20那几个字符串,它会促成vfprintf不健康运营。%20实在正是空格的url编码。

接受gdb,你能够调试C,C++,以至Modula-2语言编写的次序。

 

上述重点是因为可变参数列表和格式化字符串的特色招致的,如%20s行列,它会感觉是一个字符串,但是大家并从未传到叁个字符串,所以程序就能够有标题。平时的话只要字符串中含有%,就算未有段错误,也会现身很奇怪的出口现象。

Program received signal SIGSEGV, Segmentation fault.      
//出错后退出
0x0016e78f in vfprintf () from /lib/libc.so.6
(gdb) 
从今未来处大家还发掘经过是出于接收了SIGSEGV数字信号而告终的。通过越来越查看文书档案(man
7
signal),我们清楚SIGSEGV暗中同意handler的动作是打印”段错误”的失误消息,并发出Core文件。
翻开一下本人的当前目录,果然有core文件。

 

从标号为0的行大家并不能够收看程序到底在哪出错,所以下一步需求规定爆发错误前前后相继中函数之间的调用关系
(gdb) backtrace    //展现程序的酒店消息
#0  0x0014f78f in vfprintf () from /lib/libc.so.6
#1  0x0016f4dc in vsprintf () from /lib/libc.so.6
#2  0x00157b4b in sprintf () from /lib/libc.so.6
#3  0x08048c56 in finalprt (file=0x8a9b02b “..”, dirlist=1,
typelist=0, 
    longlist=1) at myls-0.0.c:261
#4  0x080487c3 in detailList (file=0xbfab684d “.”, dirlist=1,
typelist=0, 
    longlist=1) at myls-0.0.c:132
#5  0x08048712 in main (argc=3, argv=0xbfab4804) at myls-0.0.c:89
(gdb) 

 

Program received signal SIGSEGV, Segmentation fault.
0x0016e78f in vfprintf () from /lib/libc.so.6

  1. void backtrace_symbols_fd (void *const *buffer, int size, int fd)  

四、段错误(Segmental fault)介绍
在用C/C++语言写程序的时侯,内部存款和储蓄器管理的绝大部分做事都以亟需大家来做的。实际上,内部存款和储蓄器管理是三个比较麻烦的工作,所以像java和c#等语言使用了内存自动回笼机制,防止了内部存款和储蓄器泄漏。假若程序试图往内部存款和储蓄器地址0处写东西时,内核就能向其发送段错误非复信号,固然程序尚未捕获该随机信号,暗中同意的操作时内核终止该程序的运维,比方作者写的八个myls程序就境遇了这种气象:
luck@geekard:~/codes/12.21$ ./myls -ld .
longlist 1, typelist 0, dirlist 1, filename .
Segmentation fault
luck@geekard:~/codes/12.21$ 

 

        return 0;
}
编写翻译运转作效果果如下:
luck@geekard test $ gcc -g -rdynamic f.c
luck@geekard test $ ./a.out
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
。。。。省略。。。。
0xffffe410 in __kernel_vsyscall ()
(gdb) bt
#0  0xffffe410 in __kernel_vsyscall ()
#1  0xb7ee4b53 in waitpid () from /lib/libc.so.6
#2  0xb7e925c9 in strtold_l () from /lib/libc.so.6
#3  0x08048830 in dump (signo=11) at f.c:22         
#4  <signal handler called>
#5  0x0804884c in dummy_function () at f.c:31
#6  0x08048886 in main () at f.c:38
首个frame提示发生错误的行为f.c中的22行,即为*ptr = 0x00;行。

只顾:有个别编写翻译器的优化增选对拿到科学的调用货仓有搅拌,别的内联函数未有宾馆框架;删除框架指针也会促成不恐怕正确解析仓库内容

随着考虑下去,早先用windows系统下的ie的时侯,不时展开某个网页,会现出“运维时不当”,这么些时侯假设刚好你的机械上又怀有windows的编写翻译器的话,他会弹出来二个会话框,问您是或不是开展调节和测验,倘令你筛选是,编写翻译器将被张开,并跻身调节和测量检验意况,最初调节和测量试验。
Linux下如何是好到这一个呢?
咱俩得以在要调解的前后相继中定义叁个分层错误时限信号(SIGSEGV)的管理函数(handler),在该函数中中调用gdb,那样当段错误产生时前后相继就能够自动运营gdb进行调整,贰个差相当少的演示代码如下:

  1. char ** backtrace_symbols (void *const *buffer, int size)  

好了,以上正是这篇笔记的机要内容,上边计算一下gdb的重大命令:

 print?

能够观察在调用sprintf()函数时或者产生了分段错误(由地下援引内部存款和储蓄器引起),而sprintf()的原型为:
int sprintf(char *str, const char *format, …);
最有希望孳生错误之处是其首先个参数:char
*str,七个针对字符串数组的指针,大家先把难点放在这里,接下去看一下函数之间相互调用时传递的参数值和函数的里边变量值:
(gdb)
backtrace  full  //full参数表示完全突显函数之间交互作用调用时传递的参数值和函数的内部变量值
#0  0x0014f78f in vfprintf () from /lib/libc.so.6
No symbol table info available.
#1  0x0016f4dc in vsprintf () from /lib/libc.so.6
No symbol table info available.
#2  0x00157b4b in sprintf () from /lib/libc.so.6
No symbol table info available.
#3  0x08048c56 in finalprt (file=0x8a9b02b “..”, dirlist=1,
typelist=0, 
    longlist=1) at myls-0.0.c:261
        str = 0x4d11faec <Address 0x4d11faec out of bounds>
        flag = 65
#4  0x080487c3 in detailList (file=0xbfab684d “.”, dirlist=1,
typelist=0, 
    longlist=1) at myls-0.0.c:132
        ptr = 0x8048e44 “longlist %d, typelist %d, dirlist %d, filename
%s\n”
        dirp = 0x8a9b008
        direntp = 0x8a9b020
#5  0x08048712 in main (argc=3, argv=0xbfab4804) at myls-0.0.c:89
        file = 0xbfab684d “.”
        ptr = 0x8048d30 “U\211\345WVS\350O”
        i = 3
        j = 3
        longlist = 1
        dirlist = 1
        typelist = 0
请介怀序号3中的内部变量str的值 <Address 0x4d11faec out of
bounds>,那意味发生了数组越界,难怪发生了段错误!
几日前大家找到原因了:finalprt()中的第261行调用函数sprintf()时向其传递的第叁个参数str产生里越界存取,于是内核终止程序的运维。

 print?

(gdb)
where                                //展现最近函数之间的调用项境与breaktrace命令功效相像
#0  finalprt (file=0x804c02b “..”, dirlist=1, typelist=0, longlist=1)
    at myls-0.0.c:261
#1  0x080487c3 in detailList (file=0xbffff830 “./”, dirlist=1,
typelist=0, 
    longlist=1) at myls-0.0.c:132
#2  0x08048712 in main (argc=3, argv=0xbffff6e4) at myls-0.0.c:89
(gdb) printf “%d\n”,filetype            
//打字与印刷处函数中的变量filetype的值
100
(gdb) list                                //列出断点处前后的相干代码
256    //            if(filetype == ‘d’)
257                    sprintf(str, “%s\n”, file);
258                break;
259            case 0101:
260    //            if(filetype == ‘d’)
261                    sprintf(str, “%c%d    %d,%d  %d  %d  %s”,
filetype, permission, uid, gid, size, mdate, file);
262                break;
263            case 0110:
264    //            if(filetype == ‘d’)
265                    sprintf(str, “%s%c”, file, filetype);
(gdb) n                      //然后单步试行代码,即刻发出了不当

 

三、程序调节和测量检验器(如gdb)有啥样用?(参谋自gdb的在线辅帮手册, 可用命令:man
gdb, 或 info gdb查看)

在glibc头文件”execinfo.h”中声称了八个函数用于获取当前线程的函数调用仓库。

  5.1 调节和测验前的预备
咱俩先是要开动linux内核提供基本转储(core
dump)机制:当程序中冒出内部存款和储蓄器操作不那个时候,会产生崩溃并发出大旨文件(core文件卡塔尔国。使用GDB能够对产生的大旨文件举办解析,找寻程序是在怎么时候崩溃的和在崩溃从前景序都做了些什么。 
第大器晚成,你的Segmentation Fault错误必必要能重现(废话…卡塔 尔(英语:State of Qatar)。
接下来,依参照上边包车型大巴步调来操作:
1卡塔 尔(阿拉伯语:قطر‎无论你是用Makefile来编译,照旧直接在命令行手工业输入指令来编写翻译,都应当加上
-g 选项。如:
luck@geekard:~/codes/12.21$ ls
myls-0.0.c  myls-1.0.c  myls-2.0.c
luck@geekard:~/codes/12.21$ gcc -g -o myls myls-0.0.c 
luck@geekard:~/codes/12.21$ ls
myls  myls-0.0.c  myls-1.0.c  myls-2.0.c
加了-g选项后,gcc就能够在改变的可实行文件(这里-o
myls表示输出(output)的可实践文件名时myls)里增多一些调治符号(debugging
symbols),有了这一个调节和测量检验符号后就足以在稍后用gdb调试时列出推行的次序的C源代码了。-g选项附加了文本体量,日常只是在刚开发出的程序调节和测量检验时利用,当明确科学编译出实际接收的可实践文件时就没有必要-g选项了。
2卡塔 尔(英语:State of Qatar)日常的话,在私下认可意况下,在程序崩溃时,core文件是不转换的(相当多Linux发行版在私下认可时抑制生成基本文件卡塔 尔(阿拉伯语:قطر‎。所以,你必得校正那几个默许选项,在命令行实行:
ulimit -c unlimited     //unlimited 表示不约束生成的core文件的大大小小。
3卡塔尔国运转你的顺序,不管用什么样措施,使之再次现身Segmentation Fault错误。
luck@geekard:~/codes/12.21$ ./myls -ld .
longlist 1, typelist 0, dirlist 1, filename .
Segmentation fault (core dumped)
4卡塔 尔(阿拉伯语:قطر‎这时候,你会发现在您程序同样目录下,生成了一个文本名为core的文书,即着力文件。
luck@geekard:~/codes/12.21$ ls
core  myls  myls-0.0.c  myls-1.0.c
 myls-2.0.cluck@geekard:~/codes/12.21$ 
5卡塔 尔(阿拉伯语:قطر‎用GDB调节和测量检验它,在命令行试行:
luck@geekard:~/codes/12.21$ gdb ./myls  
大概先运营gdb然后在gdb命令提醒符中输入那四个文本:

上面是glibc中的实例(稍有改变卡塔尔国:

标签:, , , , ,

Your Comments

近期评论

    功能


    网站地图xml地图