AddressSanitizer,简称 ASan,是一个非常实用的内存错误类 bug 检测工具。但在 macOS 上调试符号是不会直接写到二进制文件里的,想要获取带有行号的堆栈追踪十分麻烦。

以下是在没有调试符号的情况下 ASan 报出的堆栈追踪示例,可以看到函数名,但无法精确定位到文件和行号。

    #0 0x110a17ab0 in __sanitizer::internal_strlen(char const*)+0x10 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5eab0)
    #1 0x1109dd87e in printf_common(void*, char const*, __va_list_tag*)+0x7ee (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x2487e)
    #2 0x1109ddae6 in wrap_vfprintf+0x66 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x24ae6)
    #3 0x10fe355d0 in v_do_log_to_file+0x260 (nvim:x86_64+0x1002cd5d0)
    #4 0x10fe35123 in logmsg+0x163 (nvim:x86_64+0x1002cd123)
    #5 0x1100f107b in tui_tk_ti_getstr+0xdb (nvim:x86_64+0x10058907b)

MacPorts 中开启 ASan 有 -fsanitize=address 就行,但想要获得更清晰的堆栈就复杂多了。

首先你需要在 Portfile 文件中添加以下几行,并确保编译过程有用到这些 flags。

configure.cflags    -O1 -g -fsanitize=address
configure.cxxflags  -O1 -g -fsanitize=address
configure.ldflags-append   -fsanitize=address -Wl,-object_path_lto,lto.o

-Wl,-object_path_lto,lto.o 用于保留 LTO 生成的临时文件,使用 -flto 链接时需要加到链接参数里。

修改完后安装时需要加 -s(从源码编译)和 -k(保留构建目录)选项,例如 sudo port -vsk install,否则无法生成调试用的 .dSYM 包。

安装完成后,就可以执行 dsymutil <binary> 在同目录下生成调试符号包 <binary>.dSYM。最后运行编译好的 <binary> 就能看到文件名:行号信息了。效果如下

    #0 0x101d3eab0 in __sanitizer::internal_strlen(char const*)+0x10 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5eab0)
    #1 0x101d0487e in printf_common(void*, char const*, __va_list_tag*)+0x7ee (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x2487e)
    #2 0x101d04ae6 in wrap_vfprintf+0x66 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x24ae6)
    #3 0x1011575d0 in v_do_log_to_file log.c:327
    #4 0x101157123 in logmsg log.c:151
    #5 0x10141307b in tui_tk_ti_getstr tui.c:2155

测试完后记得删除 .dSYM 目录,避免日后二进制文件更新导致调试符号对不上。

参考资料

https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports

Note that on macOS you may need to run dsymutil on your binary to have the file:line info in the AddressSanitizer reports.

https://clang.llvm.org/docs/CommandGuide/clang.html

On Darwin, when using -flto along with -g and compiling and linking in separate steps, you also need to pass -Wl,-object_path_lto,<lto-filename>.o at the linking step to instruct the ld64 linker not to delete the temporary object file generated during Link Time Optimization (this flag is automatically passed to the linker by Clang if compilation and linking are done in a single step). This allows debugging the executable as well as generating the .dSYM bundle using dsymutil(1).

https://github.com/neovim/neovim

Vim 很多人都用过,但会写 VimL 插件的人并不多。Neovim 增强了可扩展性,支持用各种语言编写插件,还内置了一个 Language Server Protocol (LSP) 客户端(仅 nightly)。这促成了 telescope.nvim 等依赖于 LSP 的插件,也使得很多 GUI 编辑器能集成上一个强大的 Vim 内核1