Post

cpp crash和如何调试

C++ crash解析

符号解析堆栈,然后看具体的crash reason。一般这种可以首先看错误信号

全部的signal这里列举一下,主要看下产生core的就可以了,这里就是3, 4, 8, 11

signalvalueactioncomment
SIGHUP1TermHangup detected on controlling terminal or death of controlling process
SIGINT2TermInterrupt from keyboard
SIGQUIT3CoreQuit from keyboard
SIGILL4CoreIllegal Instruction
SIGABRT6CoreAbort signal from abort(3)
SIGFPE8CoreFloating point exception
SIGKILL9TermKill signal
SIGSEGV11CoreInvalid memory reference
SIGPIPE13TermBroken pipe: write to pipe with no readers
SIGALRM14TermTimer signal from alarm(2)
SIGTERM15TermTermination signal
SIGUSR130,10,16TermUser-defined signal 1
SIGUSR231,12,17TermUser-defined signal 2
SIGCHLD20,17,18IgnChild stopped or terminated
SIGCONT19,18,25ContContinue if stopped
SIGSTOP17,19,23StopStop process
SIGTSTP18,20,24StopStop typed at terminal
SIGTTIN21,21,26StopTerminal input for background process
SIGTTOU22,22,27StopTerminal output for background process

重点关注3, 4, 8, 11

signalvalueactioncomment
SIGQUIT3CoreQuit from keyboard
SIGILL4CoreIllegal Instruction
SIGFPE8CoreFloating point exception
SIGSEGV11CoreInvalid memory reference

下面4个不太常见

  1. SIGQUIT一般在进程之外触发,引发coredump的地点完全是随机的, 可以 发生在任何一条指令执行之后。
  2. SIGFPE 这个信号虽然叫作浮点异常,但是触发的场景实际上更广:除0异常等算数操作指令(INT_MIN/(-1)在某些架构下会触发)
  3. SIGABRT abort()函数触发,用于严重错误下的场景,程序异常退出,不做任何资源清理。比较常见在fail在STL(libc, asssert fail)里
  4. SIGILL是非法指令触发,这种正常情况下不会触发,以下几种场景触发:
    1. 运行32的机器上用了64
    2. 用了骚操作,比如手写了汇编,运行的时候改了代码段的内容
    3. 变更了程序执行指令序列的控制逻辑,导致CPU的指令寄存器IP指向非合法指令地址

最常见的当然是SIGSEGV,这种一般属于非法内存访问导致的crash

非法内存引用包括:

  1. 内存地址不在进程的地址空间之内
    1. 空指针访问
    2. 未申请的堆栈空间(对于栈,可能导致回溯失败)
    3. 访问已经释放的地址
    4. 段中间的空洞
  2. 内存地址空间合法,但是权限不满足
    1. 对代码段进行写操作:野指针,向代码段进行写操作
    2. 对数据段进行执行操作: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.

Trending Tags