使用CaptureStackBackTrace和SymGetLineFromAddr64返回487无法获取行号怎么办
问题根因
你调用CaptureStackBackTrace获取到的栈帧地址是函数调用的返回地址,也就是当前函数执行完后,CPU要跳转到的下一条指令的地址。这个地址并不属于当前被调用函数的指令段范围,而是属于上层调用函数的指令范围内,甚至可能刚好落在两个函数的边界处,SymGetLineFromAddr64找不到对应地址的行号信息,就会返回487错误(ERROR_INVALID_ADDRESS)。
解决方法
把传给SymGetLineFromAddr64的地址减1即可。返回地址是call指令执行完成后的下一条指令地址,减1后地址就会落在当前函数的指令范围内,可以正确匹配到对应的行号信息。
额外需要修正的代码问题
SymSetOptions需要放到SymInitialize之前调用。你当前先初始化符号再设置SYMOPT_LOAD_LINES选项,已经加载的符号不会自动加载行号信息,导致后续查询失败。GetLastError需要在API调用失败后立即获取,你当前在printf之后再取错误码,printf内部调用会覆盖系统错误码,拿到的错误码不可靠。- 系统内核库(如ntdll.dll、kernel32.dll)没有公开的行号调试信息,所以
BaseThreadInitThunk、Rtl开头的系统函数查询不到行号属于正常现象。
修正后的核心代码片段
// 先设符号选项再初始化 SymSetOptions(SYMOPT_LOAD_LINES); HANDLE process = GetCurrentProcess(); SymInitialize(process, NULL, TRUE); // ... 省略其他不变的代码 ... for(int i = 0; i < frames; i++) { DWORD64 addr = (DWORD64)(stack[i]) - 1; // 地址减1修正 SymFromAddr(process, addr, 0, symbol); printf("\t %i: %s\n", frames - i - 1, symbol->Name); line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64)); line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); if (SymGetLineFromAddr64(process, addr, &displacement, line)) { printf("%s %i\n", line->FileName, line->LineNumber); } else { DWORD lastError = GetLastError(); // 失败后立刻取错误码 printf("failed, errcode: %i\n", lastError); } free(line); }
编译注意事项
需要确保编译时开启调试信息生成:MSVC编译器需添加/Zi或/ZI编译选项,链接器需开启生成PDB文件选项,否则无法查询到行号和函数名信息。
内容的提问来源于stack exchange,提问作者eat_a_lemon
相关产品推荐
相关产品推荐

