如何主动打印调用栈?如果是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.hpp
为我们引入了 StackTrace 和 Printer, 这两个对象的参数都可以调整(开发者比较用心)。前者的构造参数是数字,代表打印深度,默认32层,一般8层即可。
StackTrace 还有一个有用的方法:skip_n_firsts(数字)
,指示跳过前面几层的打印,建议跳过两层,这样就可以自写util方法包绕,实现一行代码打印调用栈,然后直接从感兴趣的的方开始打印。
Printer 对象则可配置多个成员变量:
bool snippet = true;
bool reverse = true;
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;
修改代码后,经常需要重新开始调试。若只有一个解决方案还好,但如果是多模块项目,比如重新编译duilib这一界面的静态库后,需要重新调试实际程序,于是需要以下两个重复步骤——
"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