步骤:

1. 调用RtlCaptureContext函数(MSDN)获取上下文(context)。

2. 把寄存器地址填充到STACKFRAME结构中。

3. 循环调用StackWalk64函数(MSDN)回溯调用栈(call stack)。

4. 调用SymFromAddr函数(MSDN)获得符号(symbol),再调用SymGetLineFromAddr64函数(MSDN)获取源码地址与行号。

Sample:

void dumpStack(void)
{const UINT max_name_length = 256; // Max length of symbols' name.CONTEXT context;            // Store register addresses.STACKFRAME64 stackframe;        // Call stack.HANDLE process, thread;           // Handle to current process & thread.// Generally it can be subsitituted with 0xFFFFFFFF & 0xFFFFFFFE.PSYMBOL_INFO symbol;         // Debugging symbol's information.IMAGEHLP_LINE64 source_info;     // Source information (file name & line number)DWORD displacement;          // Source line displacement.// Initialize PSYMBOL_INFO structure.// Allocate a properly-sized block.symbol = (PSYMBOL_INFO)malloc(sizeof(SYMBOL_INFO) + (max_name_length - 1) * sizeof(TCHAR));   memset(symbol, 0, sizeof(SYMBOL_INFO) + (max_name_length - 1) * sizeof(TCHAR));symbol->SizeOfStruct = sizeof(SYMBOL_INFO); // SizeOfStruct *MUST BE* set to sizeof(SYMBOL_INFO).symbol->MaxNameLen = max_name_length;// Initialize IMAGEHLP_LINE64 structure.memset(&source_info, 0, sizeof(IMAGEHLP_LINE64));source_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);// Initialize STACKFRAME64 structure.RtlCaptureContext(&context);            // Get context.memset(&stackframe, 0, sizeof(STACKFRAME64));stackframe.AddrPC.Offset = context.Eip;        // Fill in register addresses (EIP, ESP, EBP).stackframe.AddrPC.Mode = AddrModeFlat;stackframe.AddrStack.Offset = context.Esp;stackframe.AddrStack.Mode = AddrModeFlat;stackframe.AddrFrame.Offset = context.Ebp;stackframe.AddrFrame.Mode = AddrModeFlat;process = GetCurrentProcess();  // Get current process & thread.thread = GetCurrentThread();// Initialize dbghelp library.if(!SymInitialize(process, NULL, TRUE))return ;_putts(__T("Call stack: \n\n"));// Enumerate call stack frame.while(StackWalk64(IMAGE_FILE_MACHINE_I386, process, thread, &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)){if(stackframe.AddrFrame.Offset == 0)  // End reaches.break;if(SymFromAddr(process, stackframe.AddrPC.Offset, NULL, symbol))// Get symbol._tprintf(__T(" > %s\n"), symbol->Name);if(SymGetLineFromAddr64(process, stackframe.AddrPC.Offset, &displacement, &source_info)) {                // Get source information._tprintf(__T("\t[%s:%d] at addr 0x%08LX\n"), source_info.FileName, source_info.LineNumber,stackframe.AddrPC.Offset);} else {if(GetLastError() == 0x1E7) {     // If err_code == 0x1e7, no symbol was found._tprintf(__T("\tNo debug symbol loaded for this function.\n"));}}}SymCleanup(process); // Clean up and exit.free(symbol);
}

Result:

Note:

1. 在程序头加入:

#include <windows.h>
#include <dbghelp.h>
#pragma comment (lib, "dbghelp.lib")

若在Unicode环境下,在include前加入

#define DBGHELP_TRANSLATE_TCHAR

转译成TCHAR字符串。或使用%S强制wprintf输出multibyte string。

2. 使用dbghelp库前先初始化

if(!SymInitialize(hProc, NULL, TRUE)) return ;

完成后 释放 :

SymCleanup(hProc);

hProc为进程地址,使用GetCurrentProcess函数返回值或(HRESULT)-1均可,详见 MSDN 。

3. PSYMBOL_INFO structure使用前须分配内存,并设置MaxNameLen

SYMBOL_INFO仅定义了TCHAR name[1],SymFromAddr function可能会写到其他内存去了。

MSDN建议SYMBOL_INFO应分配sizeof(SYMBOL_INFO) + (MaxNameLen - 1) * sizeof(TCHAR)大小的内存。

4.与SymFromAddr不同,SymGetLineFromAddr64的pdwDisplacement参数不可省。参见MSDN。

得到当前函数堆栈调用相关推荐

  1. ebp 函数堆栈esp_函数堆栈调用过程

    从内存的角度详细的分析C语言中的函数调用过程: 首先写一个测试用的代码: #include int add(int x, int y) { int z = 0; z = x + y; return z ...

  2. 【c++】——函数的堆栈调用详细过程

    文章目录 1.main函数中的前期执行过程 2.sum函数中的执行过程 2.1函数{之间的指令执行过程 2.2 sum函数内部代码执行过程 2.3函数}之前执行的指令 3.总结 这篇文章,主要在汇编的 ...

  3. linux中追踪函数backtrace调用堆栈

    From: http://www.embeddedlinux.org.cn/html/jishuzixun/201211/19-2388.html 一般察看函数运行时堆栈的方法是使用GDB之类的外部调 ...

  4. 使用c++filt查看trafficserver堆栈调用信息

    在实际运维中,我们经常遇到Apache Traffic Server遇到段错误时,在traffic.out中留下的堆栈调用信息,比如下面的 FATAL: ../.././ats-4.2.0/proxy ...

  5. Direct3D Draw函数 异步调用原理解析

    概述 在D3D10中,一个基本的渲染流程可分为以下步骤: 清理帧缓存: 执行若干次的绘制: 通过Device API创建所需Buffer: 通过Map/Unmap填充数据到Buffer中: 将Buff ...

  6. c语言的那些小秘密pdf下载,C语言的那些小秘密之函数的调用关系.pdf

    C语言的那些小秘密之函数的调用关系.pdf C 语言的那些小秘密之函数的调用关系语言的那些小秘密之函数的调用关系 显示函数的调用关系是调试器的必备功能 如果我们在程序的运行中出现了崩溃的情 况 通过函 ...

  7. 函数的调用规则(__cdecl,__stdcall,__fastcall,__pascal)

    关于函数的调用规则(调用约定),大多数时候是不需要了解的,但是如果需要跨语言的编程,比如VC写的dll要delphi调用,则需要了解. microsoft的vc默认的是__cdecl方式,而windo ...

  8. c语言 中断 局部变量 not allocated,C语言(函数)调用过程(略译)

    C语言(函数)调用过程 Introduction A calling sequence is the conventional sequence of instructions that call a ...

  9. C语言栈的面试题,[面试题]EMC易安信-C语言函数堆栈的思考

    补充说明一下,内存结构: 内存中的堆栈结构 BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbo ...

最新文章

  1. 第十周项目3-血型统计
  2. 编译问题二 /snmplib/tools.c:920 undefined reference to `clock_gettime' 问题解决
  3. [BSidesSF2020]decrypto-2
  4. Android JNI原理分析
  5. 一张图看程序媛阿源的2021个人年度流水账
  6. 浅谈“be practical and realistic”
  7. linux挂载第二块磁盘,linux服务器挂载第二块磁盘图文解说-转自美橙
  8. 恢复初始快捷键_如何将Windows10系统还原初始状态
  9. Java常见面试题之HashSet如何检查重复
  10. ng-repeat动态生成的DOM如何获取宽度(封装好的方法)
  11. 小米发布会之文案错误:大哥你先处罚自己!再处罚相关高管!
  12. 物联网工程导论 简单整理
  13. 【转】抽象基类与接口,共性与个性的选择!
  14. 基于大佬的代码实现看板娘效果(三段代码实现)
  15. 利用人性弱电的互联网服务
  16. 2.4.3 Mybatis 高级查询, 复杂映射, 返回主键, 动态SQL if, set, foreach, 核心配置文件深入,plugins标签, 多表查询, 嵌套查询
  17. 关于Introduction、Discussion的杂记
  18. 【Java】interrupt、interrupted和isInterrupted的区别
  19. 阿里、京东、苏宁双十一“链”战
  20. 纸牌游戏扎金花的牌大小比较(PHP)

热门文章

  1. mysql数据库优化课程---15、mysql优化步骤(mysql中最常用最立竿见影的优化是什么)...
  2. centos7 ambari2.6.1.5+hdp2.6.4.0 大数据集群安装部署
  3. LightRoom操作快捷键
  4. Android draw9patch点九图常识
  5. 说一下fopen和open
  6. 分享27个最新国外超酷单页面网站设计
  7. 巴西队被打成了蜂窝煤
  8. Linux 之 Kickstart
  9. ES6常用新特性---笔记一
  10. SQL经典实例(五)元数据查询