如何主动打印调用栈?如果是Java、Js,那么很简单,三行就能实现。但 VisualStudio 就复杂多了。如果不下断点,那么只能在崩溃的时候被动查看。
而使用 Backward-Cpp ,只需在项目中拖入一个hpp文件,就可以主动打印。但默认输出是 stderr,无法在 VisualStudio 的 output 窗口看到任何信息。全网搜索半小时后,才从另外零星的代码片段中推得,需要将 stringstream 作为输出对象:
std::stringstream stream;
using namespace backward;
StackTrace st; st.load_here(32);
Printer p; p.print(st, stream);
然后打印 stream 内容至 VisualStudio 输出窗口:
::OutputDebugStringA(stream.str().c_str());
默认输出格式比较零散,下面分享一些强化的方法。
一、调整 Backward-Cpp 参数
backward.hpp
为我们引入了 StackTrace 和 Printer, 这两个对象的参数都可以调整(开发者比较用心)。前者的构造参数是数字,代表打印深度,默认32层,一般8层即可。
StackTrace 还有一个有用的方法:skip_n_firsts(数字)
,指示跳过前面几层的打印,建议跳过两层,这样就可以自写util方法包绕,实现一行代码打印调用栈,然后直接从感兴趣的的方开始打印。
Printer 对象则可配置多个成员变量:
- 控制是否打印代码片段,建议关闭,因为开启后调用栈特别长,不利于定位上下游
bool snippet = true;
- 控制是否逆序打印。默认逆序不用修改,和Java等同一个顺序(和Python不一样),这样就可以在打印调用栈前,输出一段关键字,然后调试运行,在 output 窗口搜索关键字,离关键字最近的下方就是最近的函数调用点了。
bool reverse = true;
二、魔改打印格式,适配VisualStudio
Backward-Cpp 的默认打印格式不是上图这样的,虽然可以很漂亮,但没有适配 VisualStudio,无法双击定位文件。文件名前面不能有非空字符,需要照着这个pull请求魔改:
os << "\n";//Need a newline or it does not work
os << source_loc.filename << "(" << source_loc.line << "):";//This exact format is required
os << " line " << source_loc.line << ", in " << source_loc.function;
#else
os << indent << "Source \"" << source_loc.filename << "\", line "
<< source_loc.line << ", in " << source_loc.function;
此外,我还去掉了 #序号
前缀,使得调用栈更加紧凑:
//os << "#" << std::left << std::setw(2) << trace.idx << std::right;
三、用 vhk 实现快捷刷新调试器
修改代码后,经常需要重新开始调试。若只有一个解决方案还好,但如果是多模块项目,比如重新编译duilib这一界面的静态库后,需要重新调试实际程序,于是需要以下两个重复步骤——
- 查找底部点击底部****毫无高亮效果的任务栏,切换至程序项目的窗口。
- 再点击VisualStudio顶部****小小的"Restart"按钮
"Restart"按钮默认快捷键是 ctrl+shift+f5,需要双手操作,很不方便。其实,F5按键默认是运行,但都处于调试状态了,为何不用F5直接“刷新”调试器?这样一键多用,岂不美哉!
可以用ahk拦截F5,然后根据 VisualStudio 标题是否含有(Running)
子串分发不同的快捷键:
#IfWinActive ahk_exe devenv.exe
$F5::
WinGetTitle,S
;消息(S, "w450")
StringGetPos, idx, S, (Running)
if(idx > 0) {
Send ^+{F5}
} else {
Send {F5}
}
return
更激进地,还可以右击左边框启动调试器:
(需要设置Debug.start快捷键为Ctrl+Shift+Alf+F5,Debug.start竟然和Debug.Restart相互冲突,不能设置为同一个,不知咋想的)
~RButton::
MouseGetPos, xpos, ypos
if(xpos < 10) {
SendInput ^+{F5}
SendInput ^+!{F5}
}
return