Win32 - 从 C 代码回溯
我目前正在寻找一种在 Windows 下从 C 代码(非 C++)获取回溯信息的方法。
我正在构建一个跨平台 C 库,具有引用计数内存管理功能。它还具有一个集成的内存调试器,可提供有关内存错误的信息(XEOS C 基础库)。
当发生故障时,启动调试器,提供有关故障的信息以及涉及的内存记录。
在 Linux 或 Mac OS X 上,我可以在以下位置查找 execinfo.h
为了使用 backtrace
函数,这样我就可以显示有关内存故障的附加信息。
我正在 Windows 上寻找同样的东西。
我在 Stack Overflow 上看到过如何在 C 中获取堆栈跟踪?。我不想使用第三方库,因此 CaptureStackBackTrace
或 StackWalk
函数看起来不错。
唯一的问题是我不知道如何使用它们,即使有微软的文档也是如此。
我不习惯 Windows 编程,因为我通常在 POSIX 兼容系统上工作。
这些功能有哪些解释,也许还有一些例子?
编辑
我现在正在考虑使用DbgHelp.lib
中的CaptureStackBackTrace
函数,因为看起来开销要少一些......
这就是我的到目前为止已经尝试过:
unsigned int i;
void * stack[ 100 ];
unsigned short frames;
SYMBOL_INFO symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize( process, NULL, TRUE );
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
for( i = 0; i < frames; i++ )
{
SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, &symbol );
printf( "%s\n", symbol.Name );
}
我只是得到垃圾。我想我应该使用 SymFromAddr
以外的其他东西。
I'm currently looking for a way to get backtrace information under Windows, from C code (no C++).
I'm building a cross-platform C library, with reference-counting memory management. It also have an integrated memory debugger that provides informations about memory mistakes (XEOS C Foundation Library).
When a fault occurs, the debugger is launched, providing information about the fault, and the memory record involved.
On Linux or Mac OS X, I can look for execinfo.h
in order to use the backtrace
function, so I can display additional infos about the memory fault.
I'm looking for the same thing on Windows.
I've seen How can one grab a stack trace in C? on Stack Overflow. I don't want to use a third-party library, so the CaptureStackBackTrace
or StackWalk
functions looks good.
The only problem is that I just don't get how to use them, even with the Microsoft documentation.
I'm not used to Windows programming, as I usually work on POSIX compliant systems.
What are some explanations for those functions, and maybe some examples?
EDIT
I'm now considering using the CaptureStackBackTrace
function from DbgHelp.lib
, as is seems there's a little less overhead...
Here's what I've tried so far:
unsigned int i;
void * stack[ 100 ];
unsigned short frames;
SYMBOL_INFO symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize( process, NULL, TRUE );
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
for( i = 0; i < frames; i++ )
{
SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, &symbol );
printf( "%s\n", symbol.Name );
}
I'm just getting junk. I guess I should use something else than SymFromAddr
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
好吧,现在我明白了。 :)
问题出在 SYMBOL_INFO 结构中。它需要在堆上分配,为符号名称保留空间,并正确初始化。
这是最终的代码:
输出是:
Alright, now I got it. : )
The problem was in the SYMBOL_INFO structure. It needs to be allocated on the heap, reserving space for the symbol name, and initialized properly.
Here's the final code:
Output is:
这是我的超低保真替代方案,用于从 C++ Builder 应用程序读取堆栈。当进程崩溃时,此代码在进程本身内执行,并将堆栈获取到 cs 数组中。
更新
一旦我得到了堆栈,我就会将其翻译成名称。我通过交叉引用 C++Builder 输出的
.map
文件来完成此操作。可以使用另一个编译器的映射文件完成相同的操作,尽管格式会有所不同。以下代码适用于 C++Builder 映射。这又是低保真的,可能不是典型的 MS 做事方式,但它适用于我的情况。下面的代码不会交付给最终用户。运行此代码后,
fns
数组包含 .map 文件中的最佳匹配函数。在我的情况下,我实际上拥有由提交到 PHP 脚本的第一段代码生成的调用堆栈 - 我使用一段 PHP 执行与上面的 C 代码相同的操作。第一位解析映射文件(同样,这适用于 C++Builder 映射,但可以轻松适应其他映射文件格式):
然后该位将地址(在
$rowaddr
中)转换为给定函数(以及函数后的偏移量):Here's my super-low-fi alternative, as used for reading stacks from a C++ Builder app. This code is executed within the process itself when it crashes and gets a stack into the cs array.
UPDATE
Once I've got the stack, I then go about translating it into names. I do this by cross-referencing with the
.map
file that C++Builder outputs. The same thing could be done with a map file from another compiler, although the formatting would be somewhat different. The following code works for C++Builder maps. This is again quite low-fi and probably not the canonical MS way of doing things, but it works in my situation. The code below isn't delivered to end users.After running this code, the
fns
array contains the best-matching function from the .map file.In my situation, I actually have the call stack as produced by the first piece of code submitting to a PHP script - I do the equivalent of the C code above using a piece of PHP. This first bit parses the map file (Again, this works with C++Builder maps but could be easily adapted to other map file formats):
Then this bit translates an address (in
$rowaddr
) into a given function (as well as the offset after the function):@Jon Bright:你说“谁知道堆栈是否有效......”:有一种方法可以找出答案,因为堆栈地址是已知的。当然,假设您需要在当前线程中进行跟踪:
我的“GetTEB()”是来自 NTDLL.DLL 的 NtCurrentTeb() - 而且它不仅适用于当前 MSDN 中所述的 Windows 7 及更高版本。 MS 破坏了文档。它在那里呆了很长时间。使用ThreadEnvironment Block (TEB),您不需要ReadProcessMemory(),因为您知道堆栈的下限和上限。我认为这是最快的方法。
使用 MS 编译器,GetEBPForStackTrace() 可以
作为获取当前线程的 EBP 的简单方法(但您可以将任何有效的 EBP 传递到此循环,只要它适用于当前线程)。
限制:这对于 Windows 下的 x86 有效。
@Jon Bright: You say "who known whether the stack is valid...": Well there's a way to find out, as the stack addresses are known. Assuming you need a trace in the current thread, of course:
My "GetTEB()" is NtCurrentTeb() from NTDLL.DLL - and it is not only Windows 7 and above as stated in the current MSDN. MS junks up the documentation. It was there for a long time. Using the ThreadEnvironment Block (TEB), you do not need ReadProcessMemory() as you know the stack's lower and upper limit. I assume this is the fastest way to do it.
Using the MS compiler, GetEBPForStackTrace() can be
as easy way to get EBP of the current thread (but you can pass any valid EBP to this loop as long as it is for the current thread).
Limitation: This is valid for x86 under Windows.