自己动手发明编程语言
❯
教程介绍📘
❯
在线体验编译器🚀
❯
为什么要发明一个新的编程语言?☕
❯
开始🔛
❯
开发编译器的流程 🚀
❯
基础理论和原理 ⚛️
❯
计算机指令基础 (CPU)🖥️
❯
自定义虚拟机指令(VM) 🕹️
❯
C语言崩溃栈信息🐞
❯
设计强化虚拟机指令(VM) 🕹️
❯
手搓编写词法分析(Lexer)📜
❯
手搓表达式编译 🔥
❯
手搓 if-else 语法编译 ⚙️🔗
❯
手搓 for 语法编译 ⚙️🔗
❯
手搓 while 语法编译 ⚙️🔗
❯
手搓 函数调用 语法编译 ⚙️🔗
❯
虚拟机高性能优化 🚀
❯
try catch异常捕获简单实现🐞
❯
手搓即时编译(JIT)入门 ⚡️
❯
包管理器开发入门 📦📦
❯
开发故事
# C语言崩溃栈信息 当我们使用 C 语言进行开发时,程序出错了,比如内存越界访问等情况,进程会被操作系统干掉,默认情况并不会给出像Java或其他语言那样详细的崩溃栈信息。 但是 崩溃栈信息对于程序找问题、解决Bug 是非常关键的。所以如何在使用C语言时看到崩溃栈信息非常重要。 - 使用 gdb - 使用 C语言的signal 信号和 Linux 的 backtrace_symbols 方法 这里贴出完整的 crash.c 的源代码: ````c #include
#include
int crash_function() { int *ptr = NULL; printf("%s line %d then crash\n", __FUNCTION__, __LINE__); *ptr = 1; // Segmentation fault printf("return from function\n");// 无法正常运行到这一行 return 1; } int main(int argc, char *argv[]) { int d = crash_function(); // crash printf("end d=%d\n", d); return 0; } ```` 编译程序 ````sh gcc crash.c -o crash ```` 运行 ````sh $ ./crash crash_function line 6 then crash Segmentation fault (core dumped) ```` 可以看到程序被操作系统强制停止,给出了一个 Segmentation fault 错误提示。但是我们并不知道具体是哪行代码导致了这个问题。 ## 使用 gdb 最常见的是方法是 gdb 调试 ,Linux、 MacOS 或 Windows 都可以使用 gdb 。 假设要调试的程序是 crash,那么运行 gdb : ````sh gdb crash ```` 这时候进入了 gdb 交互,输入 r 回车 ,就可以执行程序,当程序崩溃后,gdb 可以给出了一些信息: ````sh Starting program: /home/i7/crash/crash [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". crash_function line 6 then crash Program received signal SIGSEGV, Segmentation fault. 0x00005555555551a4 in crash_function () ```` 我们看到了崩溃发生的函数,但是没有具体源代码行数信息。我们可以通过在 gcc 编译时 -g 参数带上调试信息。 从 gdb 交互命令中可以输入 quit 退出,按 y 确认退出 完整的编译命令如下: ````sh gcc crash.c -g -o crash ```` 继续使用 gdb crash 调试程序,按 r 运行 ,输出如下: ````sh (gdb) r Starting program: /home/i7/crash/crash [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". crash_function line 6 then crash Program received signal SIGSEGV, Segmentation fault. 0x00005555555551a4 in crash_function () at crash.c:7 7 *ptr = 1; // Segmentation fault ```` 可以看到这次可以详细的精确到源代码的位置行数 crash.c:7 和具体代码 `*ptr = 1; // Segmentation fault` ,这就非常有帮助了! ## 使用 C语言的signal 信号和 Linux 的 backtrace_symbols 方法 当程序发生内存越界访问时,操作系统会给程序发送信号,如果程序没有处理这些信号,那么程序就会被操作系统直接干掉。 我们可以通过注册信号处理函数,来处理这些信号。同时想办法把发生崩溃的函数调用栈打印出来,也会有一些帮助。 这里给出一个 smartc_trace.c 的源代码: ````c #include
#include
#include
#include
// Smart Crash Trace 初始化 void smart_crash_trace_init(char *argv0); // Smart Crash 接收 signal 信号打印调用栈 void smart_crash_stack_trace(int signum); char *smartc_argv0; void smart_crash_stack_trace(int signum) { void *array[10]; size_t size; char **strings; size_t i; size = backtrace(array, 10); strings = backtrace_symbols(array, size); printf("\n"); for (int i = 0; i < 60; i++) { printf("="); if(i==20) printf("smart_crash_stack_trace -start "); } printf("\nError '%s' (%d):\n", strsignal(signum), signum); printf("Smart Crash Stack trace:\n"); for (i = 2; i < size - 1; i++) { if (strstr(strings[i], smartc_argv0) != NULL) { char *start, *end; char buf[128], cmd[1024]; // printf("%s\n", strings[i]); start = strstr(strings[i], "("); end = strstr(strings[i], ")"); int len = (int)(end - start - 1); strncpy(buf, start + 1, len); snprintf(cmd, 1024, "addr2line -ife %s %s", smartc_argv0, buf); // printf("%s\n",cmd); system(cmd); } else { printf("%s\n", strings[i]); } } // 释放分配的内存 free(strings); for (int i = 0; i < 60; i++) { printf("="); if(i==20) printf("smart_crash_stack_trace -end "); } printf("\n"); exit(-1); } void smart_crash_trace_init(char *argv0) { // 设置信号处理函数 signal(SIGSEGV, smart_crash_stack_trace); signal(SIGFPE, smart_crash_stack_trace); signal(SIGTERM, smart_crash_stack_trace); signal(SIGKILL, smart_crash_stack_trace); signal(SIGINT, smart_crash_stack_trace); signal(SIGILL, smart_crash_stack_trace); smartc_argv0 = argv0; } ```` 现在我们更新一下 crash.c 代码 ````c #include
#include
#include
#include
#include
#include
#include "smartc_trace.c" int crash_function() { int *ptr = NULL; printf("%s line %d then crash\n", __FUNCTION__, __LINE__); *ptr = 1; // Segmentation fault //return 5 / 0; printf("return from function\n");// 无法正常运行到这一行 return 1; } int main(int argc, char *argv[]) { smart_crash_trace_init(argv[0]); int d = crash_function(); // crash printf("end d=%d\n", d); return 0; } ```` 完整的编译命令如下: ````sh gcc crash.c -g -o crash ```` 运行 ````sh ./crash ```` 输出如下: ````sh crash_function line 13 then crash =====================smart_crash_stack_trace -start ======================================= Error 'Segmentation fault' (11): Smart Crash Stack trace: crash_function /home/i7/crash/crash.c:14 main /home/i7/crash/crash.c:24 /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7fad28040d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7fad28040e40] =====================smart_crash_stack_trace -end ======================================= ```` 可以看到注册的信号处理函数已经执行了,打印了函数调用栈是 crash.c:14 行 ,这里是函数发生崩溃的位置,对找问题也是有较大帮助的。 这个程序依然可以通过 gdb 进行调试 ````sh (gdb) r Starting program: /home/i7/crash/crash [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". crash_function line 13 then crash Program received signal SIGSEGV, Segmentation fault. 0x000055555555567a in crash_function () at crash.c:14 14 *ptr = 1; // Segmentation fault ```` 可以看到,也是相同的位置信息 crash.c:14 相对于每次崩溃后,再去执行 gdb 。不如崩溃后直接把信息打印出来,提高一些效率。当然 gdb 还有很多非常强大的功能,值得去挖掘使用。
Hello!请先完成登录验证
微信公众号
哈希空间
扫码进入公众号回复 9 即可完成验证,实现自动登录网站。
关注就是支持,更好的服务提供给粉丝