欢迎光临散文网 会员登陆 & 注册

C/C++调试技巧-debugbreak

2018-09-16 10:18 作者:AICDG  | 我要投稿

       有日子没有写基础点的文章了啊。最近在iOS上调试,发现iOS上没有MSVC上已经用惯了的__debugbreak,所以花了点时间,研究了一下如何在iOS/Android调试时使用__debugbreak,本着研究问题一次就研究清楚的态度,稍微看了两眼,然后就有了这篇文章。

MSVC独有神器 DebugBreak ( __debugbreak )

       要说调试时最常用的手段,那应该就是打断点调试了。但是如果每次错误都需要手工去定位断点的位置,未免还是有点麻烦。用Unreal engine 4开发的同学应该都有经历,就是崩溃的时候总能触发一次断点,给个机会查看崩溃时的程序调用栈和变量值。这个就是依靠MSVC提供的 DebugBreak 函数实现的。

        DebugBreak 函数相当于一个断点,在可能发生崩溃的地方都加一个,一旦崩溃自动断住,查看栈和数值,对于分析bug非常有帮助。

其他平台的 DebugBreak ( __debugbreak ) 的

       理想的 debug_break 函数的功能

  • 在执行此函数时触发一个软件断点(e.g. Linux系统上的 SIGTRAP 信号)

  • 在触发断点后,可以继续执行,比如GDB的 continuenextstepstepi 命令

  • debug_break 函数不应该导致代码优化

GCC

       GCC提供了内置的 __builtin_trap() 函数。可以在debug模式下自动进入断点,但是,GCC编译器默认情况会把 __builtin_trap() 后面的代码优化掉。比如在i386上


会被GCC编译成

 

       printf被优化掉了。

       并且在 i386 / x86-64 体系结构上 __builtin_trap() 编译成了汇编指令 ud2, 在Linux上发出 SIGILL 信号而不是 SIGTRAP 信号。如果希望GDB在此断住,还需要告诉GDB,在接收SIGILL信号之后进入断点停止执行。



       除此之外,GCC/GDB还是有某些版本不认 __builtin_trap() 。

       在ARM体系结构上,GCC的 __builtin_trap() 会被直接翻译成 abort(),比x86上的 ud2 更加不可靠。

       幸运的是GCC已经认识到这个需求,在GCC 7.3.1 预计会添加__builtin_break()

Clang

       Clang/LLVM除了提供了内置的 __builtin_trap 函数之外,还额外提供了 __builtin_debugtrap 函数,这个函数在x86体系结构上会生成 int3 。


会被clang编译成



        int3在Linux(x86)上可以发出一个 SIGTRAP 信号,用GDB/LLDB可以正常调试。

Linux SIGTRAP 的触发方式

       在前文已经提过,在i386 / x86-64体系结构上,int3指令可以发出一个SIGTRAP信号。

       在ARM体系结构上,也有等价的指令。在32位ARM上,对于ARM mode,.inst 0xe7f001f0汇编可以发出SIGTRAP信号,对于Thumb mode,.inst 0xde01可以发出 SIGTRAP信号。

       不过GDB在32位ARM上,接收汇编直接发出的SIGTRAP信号可能出现不能继续调试的情况,幸好还有个workaround


       在64位ARM上,.inst 0xd4200000.汇编可以产生SIGTRAP信号。

       简单总结一下,就是在Linux系统中,可以通过内嵌汇编的方式在特定位置强行触发断点。

debugbreak库

       上文基本简述了x86和ARM实现断点的方式,有个人封装了个库,来简化开发,这个库就是 debugbreak

       用法


       debugbreak库在各个平台上的实际情况。



C/C++调试技巧-debugbreak的评论 (共 条)

分享到微博请遵守国家法律