Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;堆栈跟踪问题_C++_Windows_Debugging_Stack Trace_Callstack - Fatal编程技术网

C++ C++;堆栈跟踪问题

C++ C++;堆栈跟踪问题,c++,windows,debugging,stack-trace,callstack,C++,Windows,Debugging,Stack Trace,Callstack,我正在使用一个类,我想用它来记录Windows Vista/7计算机上的当前调用堆栈。(非常类似于“遍历调用堆栈”) 首先,我使用RtlCaptureContext获取当前上下文记录,然后使用StackWalk64获取各个堆栈帧。现在,我意识到每当我关闭并再次启动程序时,STACKFRAME64.AddrPC中的程序计数器实际上会改变特定代码行。出于某种原因,我认为只要不更改源代码并重新编译,特定代码行的PC地址就会保持不变 我需要PC地址来使用SymFromAddr和SymGetLineFro

我正在使用一个类,我想用它来记录Windows Vista/7计算机上的当前调用堆栈。(非常类似于“遍历调用堆栈”)

首先,我使用RtlCaptureContext获取当前上下文记录,然后使用StackWalk64获取各个堆栈帧。现在,我意识到每当我关闭并再次启动程序时,STACKFRAME64.AddrPC中的程序计数器实际上会改变特定代码行。出于某种原因,我认为只要不更改源代码并重新编译,特定代码行的PC地址就会保持不变

我需要PC地址来使用SymFromAddr和SymGetLineFromAddr64获取有关被调用函数、代码文件和行号的信息。不幸的是,这只在程序调试数据库(PDB文件)存在的情况下有效,但不允许我将其提供给客户机

我的计划是记录调用堆栈的PC地址(无论何时需要),然后从客户端发送给我。因此,我可以使用我的PDB文件来找出调用了哪些函数,但这当然只在PC地址是唯一标识符的情况下起作用。因为每次我启动程序时它们都会改变,所以我不能使用这种方法

您知道更好的方法来读取调用堆栈或克服程序计数器更改的问题吗

我认为一个可能的解决方案是始终获取已知位置的PC地址,并将其用作参考,以确定不同PC地址之间的偏移量。这似乎是可行的,但我不确定这是否是一种有效的方法,是否会一直有效


非常感谢你的帮助!我将在codeproject.com上发布最终(封装)解决方案,如果您愿意,我会说您帮助了我。

我建议您查看Visual Studio项目的设置:链接器->高级->随机基址,查看所有程序和相关DLL (您可以重建)并重试。这是我唯一想到的一件事


希望对您有所帮助。

您需要发送程序的运行内存映射,该映射告诉您从客户端加载的基址库/程序


然后,您可以使用基址计算符号。

堆栈本身还不够,您需要加载的模块映射,这样您就可以将任何地址(随机、真)与模块关联,并找到PDB符号。但你真的是在重新发明轮子,因为至少有两种现成的解决方案可以很好地解决这个问题:

  • Windows特定的DbgHlp小型转储API:。你的应用程序不应该直接调用它,而是应该附带一个极小的.exe,它所做的一切都需要一个进程的sa转储(进程ID作为参数给出),当你的应用程序遇到错误条件时,应该启动这个.exe,然后等待它的完成。原因是“转储程序”进程将在转储期间冻结转储进程,因此正在转储的进程不能与进行转储的进程相同。此方案适用于所有实现的应用程序。更不用说生成的转储是一个true.mdmp,您可以在WinDbg(或者VisualStudio中加载,如果您喜欢的话)

  • 跨平台开源解决方案:。被Chrome、Firefox、Picassa和其他知名应用程序使用


因此,首先,不要重新发明轮子。另外,还有一些服务可以为错误报告增加价值,比如聚合、通知、跟踪和自动客户端响应,比如前面提到的由Microsoft提供的WER(您的代码必须经过数字签名才能符合要求),,(这是您真正创建的)和其他,但可以。只有WER适用于本机Windows应用程序。

使用信息表单
上下文
可以在PE图像中找到函数部分和偏移量。例如,您可以使用此信息从链接器生成的.map文件中获取函数名

  • 获取
    CONTEXT
    struct。您对程序计数器成员感兴趣。由于
    CONTEXT
    依赖于平台,因此您必须自己解决这个问题。您在初始化时就已经这样做了,例如x64窗口的
    STACKFRAME64.AddrPC.Offset=CONTEXT.Rip
    。现在我们开始堆栈漫游,并使用
    STACKFRAME64.AddrPC.Offset
    ,由
    staclkwark64
    填充作为起点

  • 您需要使用分配基址将其转换为相对虚拟地址(RVA):
    RVA=STACKFRAME64.AddrPC.Offset-AllocationBase
    。您可以使用
    VirtualQuery
    获取
    AllocationBase

  • 一旦你有了它,你需要找到这个RVA落在哪个节中,并从中减去节的起始地址以得到SectionOffset:
    SectionOffset=RVA-SectionBase=STACKFRAME64.AddrPC.Offset-AllocationBase-SectionBase
    。为了做到这一点,您需要访问PE图像头结构(图像头、图像头、图像头),以获取PE中的节数及其开始/结束地址。这很简单

  • 就这样。现在您在PE图像中有了节号和偏移量。函数偏移量是.map文件中小于SectionOffset的最高偏移量

    如果你愿意,我可以稍后发布代码

    编辑:用于打印功能地址的代码(我们假设x64通用CPU):

    现在,地址方面的调用堆栈是(由内而外)
    00002F8D->000031EB->00003253->00007947->0001552D->0002B521
    。 将前三个偏移量与
    .map
    文件内容进行比较:

    ...
    
     0001:00002f40       ?GenerateReport@@YAXXZ     0000000140003f40 f   FMain.obj
     0001:000031e0       ?Run@@YAXXZ                00000001400041e0 f   FMain.obj
     0001:00003220       main                       0000000140004220 f   FMain.obj
    
    ...
    
    其中
    00002f40
    是距离
    00002F8D
    最近的较小偏移量,依此类推。最后三个地址是指调用
    main
    \u tmainCRTstartup
    等)的CRT/OS函数-我们应该忽略它们

    因此,我们可以看到,我们能够在
    .map
    文件的帮助下恢复堆栈跟踪。为了
    D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000002F8D
    D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 00000000000031EB
    D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000003253
    D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000007947
    C:\Windows\system32\kernel32.dll : 0001 : 000000000001552D
    C:\Windows\SYSTEM32\ntdll.dll : 0001 : 000000000002B521
    ------------------
    
    ...
    
     0001:00002f40       ?GenerateReport@@YAXXZ     0000000140003f40 f   FMain.obj
     0001:000031e0       ?Run@@YAXXZ                00000001400041e0 f   FMain.obj
     0001:00003220       main                       0000000140004220 f   FMain.obj
    
    ...