介绍
使用 gdb 或 lldb 等调试器时,通常您:
- 运行调试器并指定程序可执行文件的名称。
- 设置断点。
- 从调试器中运行您的程序。
但是,某些程序是通过 fork() 和 exec++() 从守护程序启动或作为管道的一部分启动。 如果程序本身是守护进程(或其他长时间运行的进程),您可以简单地将调试器附加到程序的运行进程中。 但是,如果您的程序是短暂的,那么在您将调试器附加到它之前,它早已完成执行。 需要的是一种运行程序的方法,但让它立即暂停并无限期地等待您附加调试器。
等待
c 标准库包含 raise() 函数,该函数向当前进程发送信号。 posix 定义了 sigstop 信号,该信号使程序停止并无限期地等待,直到收到 sigcont 信号(当您输入 continue 命令时调试器恰好发送该信号),这正是我们想要的。这是一个可以做到这一点的函数:
void wait_for_debugger_attach( void ) { fprintf( stderr, "%s: pid=%d: waiting for debugger to attach...n", me, (int)getpid() ); if ( raise( sigstop ) == -1 ) { perror( me ); exit( ex_oserr ); } }
登录后复制
sigstop 与调试器本身发送到进程以在遇到断点时暂停其执行的信号相同。
首先,我们打印一条包含进程 id 的消息,以便您知道将调试器附加到哪个进程。 然后我们通过引发 sigstop 等待。为了稳健起见,我们检查 raise() 的返回值:如果是 -1,则发生错误,因此通过 perror() 打印出来并退出。
变量 me 是全局变量,并在 main() 中设置为 argv[0] 的基本名称,即可执行文件的名称。
有条件等待
当然,只有在实际调试程序时才需要调用 wait_for_debugger_attach()。 有几种方法可以让你的程序知道情况是这样的:
- 通过命令行选项,例如 –debug。
- 通过文件的存在,例如 ~/.foo_debug (其中 foo 将替换为您的程序的名称)。
- 通过环境变量,例如 foo_debug。
其中,我最喜欢#3,因为它是最难意外完成的,因为必须在父进程的环境中设置环境变量才能被程序继承。 (您不希望生产中的程序运行然后立即无限期地等待。)
因此,在main()中,我们可以这样做:
int main( int argc, char const *argv[] ) { me = basename( argv[0] ); if ( getenv( "foo_debug" ) != null ) wait_for_debugger_attach(); // ... }
登录后复制
其中 getenv() 仅当给定的环境变量存在时才返回非 null。 (它的值,即使是空的,也没关系。)
细节
如果你想让你的程序更难意外地无限期等待,你可以检查环境变量的“肯定”值。 我们可以通过添加几个辅助函数来做到这一点:
bool str_is_any( char const *s, char const *const matches[const static 2] ) { if ( s != null ) { for ( char const *const *match = matches; *match != null; ++match ) { if ( strcasecmp( s, *match ) == 0 ) return true; } } return false; } bool str_is_affirmative( char const *s ) { static char const *const affirmatives[] = { "1", "t", "true", "y", "yes", null }; return str_is_any( s, affirmatives ); }
登录后复制
如果你不知道 const static 2 在 c 中的作用,请参阅此处。 在 c++ 程序中,只需省略此即可。
然后将main()中的代码更改为:
if ( str_is_affirmative( getenv( "FOO_DEBUG" ) ) ) wait_for_debugger_attach();
登录后复制
结论
有条件地使用 sigstop 是调试以命令行以外的方式启动的程序的好方法。
以上就是等待调试器的详细内容,更多请关注叮当号网其它相关文章!
文章来自互联网,只做分享使用。发布者:pansz,转转请注明出处:https://www.dingdanghao.com/article/677066.html