我的程序正在打印几个随机数和字符。它将来自我留下的一些调试代码。它可能是在我最近更改的某些内容中间接打开的。即使在我的库中进行了一些快速的 grep 操作后,我也不知道它是从哪里来的。
如何轻松追踪打印呼叫的来源?
(它将是printf
或之一cout <<
)
我假设类似于gdb
为每次写入标准输出吐出堆栈跟踪。从现在开始,我肯定会采取预防措施,但只是对可能的解决方案感兴趣。
我的程序正在打印几个随机数和字符。它将来自我留下的一些调试代码。它可能是在我最近更改的某些内容中间接打开的。即使在我的库中进行了一些快速的 grep 操作后,我也不知道它是从哪里来的。
如何轻松追踪打印呼叫的来源?
(它将是printf
或之一cout <<
)
我假设类似于gdb
为每次写入标准输出吐出堆栈跟踪。从现在开始,我肯定会采取预防措施,但只是对可能的解决方案感兴趣。
宏可以帮助你。在宏定义中,您可以使用__FILE__
and __LINE__
(和其他此类宏)来打印位置信息。
这是一个例子:
#include <iostream>
#define mycout std::cout << __FILE__ << "(" << __LINE__ << ") "
#define cout mycout
int main()
{
cout << "Hello";
}
它打印文件名和行号,后跟您的消息("Hello"
在这种情况下)。
main.cpp(8) Hello
如果您除了要查找的控制台输出之外没有很多其他控制台输出,那么是时候使用调试器了。cout
首先,使用 a和 aprintf
语句编写一个小示例程序。调试调用以查看它们的实现,并在那里放置一个断点。对于 printf,您可能必须在汇编程序中执行此操作。
一旦你有了这些断点,在调试器中运行你的程序并等到断点被命中——调用堆栈应该告诉你对printf
/的调用在哪里cout
。
strace-plus看起来可以完成这项工作。尤其是能够仅跟踪某些系统调用,并-e trace=...
选择消除混乱。您还... 2>&1 | grep -C 20 ...
可以找到确切的打印调用。
这是该工具的给定示例输出:
write(1, "bar again\n", 10) = 10
> write() ../sysdeps/unix/syscall-template.S:82
> _IO_new_file_write() fileops.c:1277
> _IO_new_do_write() fileops.c:531
> _IO_new_file_overflow() fileops.c:889
> _IO_puts() ioputs.c:40
> bar() [/home/pgbovine/strace-plus/hello]
> foo() [/home/pgbovine/strace-plus/hello]
> main() [/home/pgbovine/strace-plus/hello]
> __libc_start_main() libc-start.c:258
> _start() [/home/pgbovine/strace-plus/hello]
@ArneMertz 好主意,谢谢!在我的情况下,使用调试器效果很好,因为在麻烦的打印之前我没有太多的输出。我只需要弄清楚如何在printf
orcout
调用上设置断点。这个页面给了我答案。
向主函数添加一个printf
和/或cout
调用,使用调试信息进行编译,以及
gdb program
br main
r
disas
给出这样的东西:
Dump of assembler code for function main():
0x00000000004cdb66 <+0>: push %rbp
0x00000000004cdb67 <+1>: mov %rsp,%rbp
0x00000000004cdb6a <+4>: push %r12
0x00000000004cdb6c <+6>: push %rbx
0x00000000004cdb6d <+7>: sub $0x380,%rsp
=> 0x00000000004cdb74 <+14>: mov $0x5ae287,%edi
0x00000000004cdb79 <+19>: callq 0x4c1cf0 <puts@plt>
...
在这种情况下0x4c1cf0
,地址在哪里。printf
b *0x4c1cf0
c
...
bt
然后您将在下一次调用 printf 时使用堆栈跟踪。
如果只有一种很好的方法可以自动执行此操作,而无需恒定的c
/alt-tab/bt
循环。