cpp crash和如何调试
C++ crash解析
符号解析堆栈,然后看具体的crash reason。一般这种可以首先看错误信号
全部的signal这里列举一下,主要看下产生core的就可以了,这里就是3, 4, 8, 11
signal | value | action | comment |
---|---|---|---|
SIGHUP | 1 | Term | Hangup detected on controlling terminal or death of controlling process |
SIGINT | 2 | Term | Interrupt from keyboard |
SIGQUIT | 3 | Core | Quit from keyboard |
SIGILL | 4 | Core | Illegal Instruction |
SIGABRT | 6 | Core | Abort signal from abort(3) |
SIGFPE | 8 | Core | Floating point exception |
SIGKILL | 9 | Term | Kill signal |
SIGSEGV | 11 | Core | Invalid memory reference |
SIGPIPE | 13 | Term | Broken pipe: write to pipe with no readers |
SIGALRM | 14 | Term | Timer signal from alarm(2) |
SIGTERM | 15 | Term | Termination signal |
SIGUSR1 | 30,10,16 | Term | User-defined signal 1 |
SIGUSR2 | 31,12,17 | Term | User-defined signal 2 |
SIGCHLD | 20,17,18 | Ign | Child stopped or terminated |
SIGCONT | 19,18,25 | Cont | Continue if stopped |
SIGSTOP | 17,19,23 | Stop | Stop process |
SIGTSTP | 18,20,24 | Stop | Stop typed at terminal |
SIGTTIN | 21,21,26 | Stop | Terminal input for background process |
SIGTTOU | 22,22,27 | Stop | Terminal output for background process |
重点关注3, 4, 8, 11
signal | value | action | comment |
---|---|---|---|
SIGQUIT | 3 | Core | Quit from keyboard |
SIGILL | 4 | Core | Illegal Instruction |
SIGFPE | 8 | Core | Floating point exception |
SIGSEGV | 11 | Core | Invalid memory reference |
下面4个不太常见
- SIGQUIT一般在进程之外触发,引发coredump的地点完全是随机的, 可以 发生在任何一条指令执行之后。
- SIGFPE 这个信号虽然叫作浮点异常,但是触发的场景实际上更广:除0异常等算数操作指令(INT_MIN/(-1)在某些架构下会触发)
- SIGABRT abort()函数触发,用于严重错误下的场景,程序异常退出,不做任何资源清理。比较常见在fail在STL(libc, asssert fail)里
- SIGILL是非法指令触发,这种正常情况下不会触发,以下几种场景触发:
- 运行32的机器上用了64
- 用了骚操作,比如手写了汇编,运行的时候改了代码段的内容
- 变更了程序执行指令序列的控制逻辑,导致CPU的指令寄存器IP指向非合法指令地址
最常见的当然是SIGSEGV,这种一般属于非法内存访问导致的crash
非法内存引用包括:
- 内存地址不在进程的地址空间之内
- 空指针访问
- 未申请的堆栈空间(对于栈,可能导致回溯失败)
- 访问已经释放的地址
- 段中间的空洞
- 内存地址空间合法,但是权限不满足
- 对代码段进行写操作:野指针,向代码段进行写操作
- 对数据段进行执行操作:rip错误,把数据段的数据当作指令来执行
在不同的操作系统上,调用栈可能在最后一个(或者几个)是不对的,因为野指针的访问可能是合法的访问,所以在代码的时候要尽量考虑到把不用的指针设置成nullptr,这样栈会比较清楚。
但是前面能对起来代码的一般是对的。
重点关注你能看到的最后一个正确的frame,这里一般有问题,这种也没必要怀疑内存坏了重新解析了。
调试技巧
调试的断点就不说了,有些watch point之类的我觉得要会,另外配合脚本可以参考一下ref-2.
debug coredump的经验可以看看ref-3
发现线上的堆栈,可能要看点汇编,看看ref-4, 另外时刻牢记c++对象模型,看ref-5
iostream & stoi
stoi会抛异常,如果转换不能进行,如果你想比较稳的话,istringstream构造,然后»就可以了,这个不会抛异常,前提是只能是POD类型
然后结果可以通过返回的引用的bool operator()来判断(继承了basic_ios)
Ref
1. Crash/coredump 原理与实例
2. 你还在用GDB调试程序吗?
3. 两大绝招debug core dump?
4. 如何阅读简单的汇编(持续更新)
5. 怎么理解C++虚函数?fat pointer in GO/Rust vs thin pointer in C++
6. Memory Layout of C++ Object in Different Scenarios
7. Memory Layout for Multiple and Virtual Inheritance
8. C++ Virtual Functions
This post is licensed under CC BY 4.0 by the author.