C/C++ call stack traces

系统性调试:从stack traces到root cause的工程化方法 系统性调试是一种以证据驱动、可复现、可量化的软件问题定位技术,其核心在于将模糊的故障现象转化为可观测、可验证的工程动作。它基于stack traces构建调用链地图,依托test case实施控制变量验证,并通过层层归因逼近root cause。该能力不依赖特定语言或框架,而是强调错误信号捕获、假设快速证伪与闭环验证,广泛应用于环境差异、异步竞态、配置漂移等典型线上问题场景。本文聚焦systematic-debugging实践路径,帮助开发者建立‘问题→证据→结论’的肌肉记忆。 阅读详情

哎,郁闷,不知道是CSDN的bug还是有人故意捣乱,辛辛苦苦写的文章被替换成“自由的思维”一文,真是百思不解呀,至此也没有什么再写的激情了!(该程序能输出调用栈上的函数名称,实参个数及其值,以及函数调用处的偏移量。像VC++ 6.0中的call stack)

控制台输出如下:

StackTrace dump begin...

StackTrace(  )
Call_C( 
0x0000000A 0x00000014  )        line  448   +   0  bytes
Call_B( 
0x0000000A 0x00000014  )        line  453   +   13  bytes
Call_A( 
0x0000000A 0x00000014  )        line  458   +   13  bytes
main( 
0x00000001 0x00391380 0x003913D8  )      line  463   +   9  bytes

StackTrace dump end
!
Press any key to 
continue

 有篇文章特好,Playing with the  stack : http://www.codeguru.com/cpp/misc/misc/stack/article.php/c3875/

 下面贴个源码。

#include  < stdio.h >
#include 
< windows.h >
#include 
< Dbghelp.h >

#define  JMP_INSTR 0xE9
#define  SYM_BUFF_SIZE 512

static  BYTE g_stSymbol [ SYM_BUFF_SIZE ] ;

int  GetFuncArgsNum( int  retAddrs)
{
    
if(0xC483 == *(WORD *)retAddrs) // 0xC483 'Add esp, xxx'
        return *(BYTE*)(retAddrs + 2>> 2;
    
else
        
return 0;
}


int  GetFuncArgVal( int  ebp,  int  index)
{
    
return *(int *)(ebp + ((index + 2<< 2));
}


int  GetCallFuncAddrs( int  retAddrs)
{
    
int callFuncAddrs = *(int *)(retAddrs - 4+ retAddrs;
    
    
//callFuncAddrs > 0x80000000 can cause the 'can not read the memory' error!
    if((DWORD)callFuncAddrs > 0x80000000)
    
{
        
return 0;
    }

    
    
//Jump over the JMP instruction to the real address of function
    while(JMP_INSTR == *(BYTE *)callFuncAddrs) 
    
{
        
int offset = callFuncAddrs + 1;
        callFuncAddrs 
= *(int *)(offset) + (callFuncAddrs + 5);
    }


    
return callFuncAddrs;
}


void  PrintCallFuncOffset( int  retAddrs)
{
    HANDLE hCurProcess 
= (HANDLE) ::GetCurrentProcessId();
    
static saved_retAddrs = 0;

    
if(0 == saved_retAddrs)
    
{
        saved_retAddrs 
= retAddrs;
        printf(
"%s"" ");
        
return;
    }


    DWORD dwDisp;
    IMAGEHLP_LINE stLine;

    FillMemory(
&stLine, NULL, sizeof(IMAGEHLP_LINE));
    stLine.SizeOfStruct 
= sizeof(stLine);
    
if(!::SymGetLineFromAddr(hCurProcess, (DWORD)saved_retAddrs, &dwDisp, &stLine))
    
{
        printf(
"%s"" ");
        
return;
    }


    printf(
"line %d + %d bytes ", stLine.LineNumber, dwDisp);
    saved_retAddrs 
= retAddrs;
}


void  PrintCallFunc( int  ebp)
{
    
int callFuncAddrs;
    DWORD dwDisp 
= 0;
    
int retAddrs = *(int *)(ebp + 4);
    HANDLE hCurProcess 
= (HANDLE) ::GetCurrentProcessId();
    PIMAGEHLP_SYMBOL pSym 
= (PIMAGEHLP_SYMBOL)&g_stSymbol ;
    
int argsNum = ::GetFuncArgsNum(retAddrs);

    callFuncAddrs 
= GetCallFuncAddrs(retAddrs);
    
if(!::SymGetSymFromAddr(hCurProcess, (DWORD)callFuncAddrs, &dwDisp, pSym))
    
{
        
return;
    }

    
    
//Print the name of call function
    printf("%s( ", pSym->Name);
    
    
//Print the args
    if(argsNum > 0)
    
{
        argsNum
--;
        
for(int i = 0; i < argsNum; i++)
        
{
            printf(
"0x%08X, ", GetFuncArgVal(ebp, i));
        }


        
//print the last arg.
        printf("0x%08X ) ", GetFuncArgVal(ebp, i));
    }

    
else
    
{
        printf(
"%s"" ) ");
    }


    
//Print the line number
    PrintCallFuncOffset(retAddrs);
}


void  StackTrace( void )
{
    
int saved_ebp;
    
int cur_ebp;
    HANDLE hCurProcess;

    
//Fill the IMAGEHLP_SYMBOL struct
    PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)&g_stSymbol ;
    FillMemory ( pSym , NULL , SYM_BUFF_SIZE ) ;
    pSym
->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL ) ;
    pSym
->MaxNameLength = SYM_BUFF_SIZE - sizeof ( IMAGEHLP_SYMBOL );

    
//Initialize & load the symbol table
    hCurProcess = (HANDLE)::GetCurrentProcessId();
    ::SymSetOptions ( SYMOPT_UNDNAME 
| SYMOPT_LOAD_LINES ) ;
    
if(!::SymInitialize(hCurProcess, NULL, TRUE))// Load the module automatically
    {
        
return;
    }


    __asm
    
{
        
//Get the current ebp
        mov cur_ebp, ebp
    }


    
//Get the saved ebp
    saved_ebp = *(int *)cur_ebp;

    
//Print the call stack
    printf("StackTrace dump begin... ");
    
while(saved_ebp != 0)
    
{
        PrintCallFunc(cur_ebp);

        
//Move to the next caller's stack frame
        cur_ebp = saved_ebp;
        saved_ebp 
= *(int *)saved_ebp;
    }


    printf(
" StackTrace dump end! ");
    ::SymCleanup(hCurProcess);
}


class  CTest
{
public:
    
void Hello()
    
{
        StackTrace();
    }
;
}
;

int  Call_C( int  a,  int  b)
{
    StackTrace();
    
return (a + b);
}


int  Call_B( int  a,  int  b)
{
    
return Call_C(a, b);
}


int  Call_A( int  a,  int  b)
{
    
return Call_B(a, b);
}


main()
{
    Call_A(
1020);
//    CTest test;
//    test.Hello();
    return 0;
}
浅析C++ StackTrace 堆栈轨迹 堆栈轨迹:如果你需要打印出某个时间的调用堆栈状态,你将产生一个堆栈轨迹。 stack trace 中包括三部分,分别为:.bss .text .data bss: 表示程序中未初始化的全局变量的一块内存区域 text: 表示程序中已初始化的全局变量的一块内存区域 data:表示存放程序执行代码的一块内存区域      我们在学习函数调用时,都知道每个函数都拥有自己的栈空间。一个函数被 阅读详情

相关推荐

Visual Studio中Linux C++项目配置AddressSanitizer实战指南

C++开发中,内存安全是保障程序稳定性的核心挑战。AddressSanitizer(ASan)作为编译器集成的运行时检测工具,通过插桩技术实时监控内存访问,能够精准捕获缓冲区溢出、内存泄漏等常见问题。其技术价值在于以约2倍的性能开销,提供比传统工具更快的检测速度和更详细的错误报告,包括精确的文件名、行号和调用堆栈。这一特性使其成为开发测试阶段提升代码质量的关键工具,尤其适用于持续集成环境。在Visual Studio中配置Linux C++项目的ASan,解决了跨平台开发环境割裂的痛点,实现了编码、编译、

weixin_30265103的博客 328

调试技巧之调用堆栈 - Call stack

简单介绍   调试是程序开发者必备技巧。如果不会调试,自己写的程序一旦出问题,往往无从下手。本人总结10年使用VC经验,对调试技巧做一个粗浅的介绍。希望对大家有所帮助。      今天简单的介绍介绍调用堆栈。调用堆栈在我的专栏的文章VC调试入门提了一下,但是没有详细介绍。      首先介绍一下什么叫调用堆栈:假设我们有几个函数,分别是function1,function2,functi

zhg598242449的专栏 2万+

cpp-stacktrace:快速简单的C ++堆栈跟踪

概要 快速/简单的stacktrace hack。 动机 博客文章。

c++23中的新功能之五堆栈信息库

越是学习,越会发现,很多东西其实都是互相影响的。前有Boost,又有gcc扩展相关的堆栈处理,STL对这种有利于自己的事情不可能无动于衷。所以,现在看学习时,有些前辈和牛人推荐大家多看外面的世界,跨一些语言甚至学科,不是没有道理的。但是怎么掌握其中的度,就需要自己把握了。总不能一个学c++的学着学着最后成了一个捕鱼的。

fpcc的专栏 1657

C++中记录并解析函数调用栈callstack

glibc中提供了backtrace()和backtrace_symbols()两个函数来输出和解析程序的call stack, 输出程序运行时调用栈信息 可以通过命令man backtrace查看具体帮忙信息。 #include <execinfo.h> int backtrace(void **buffer, int size); char **backtrace_symbols(void *const *buffer, int size); 使用backtrace()函数获取调用栈

halazi100 3578

GCC damangling stack traces

GCC damangling stack tracesTons of useful info: this URL is no useful http://www.acsu.buffalo.edu/~charngda/backtrace.html BTW:I’m afraid to lose these pages,so i’ve copied these pages at hereCALL ST

lyc 1868

PyTorch 调试环境变量实战指南:从 C++ 堆栈到分布式 NaN 检查

PyTorch 的 Python 前端之下是大量由 C++ 实现的算子、调度器与分布式通信内核,当错误发生时,用户往往只能看到被截断的 Python 栈而难以定位底层调用。本文围绕当前仓库中的官方调试文档 [docs/source/debugging_environment_variables.md](https://link.gitcode.com/i/7c566022b34f22acbb923

gitblog_00354的博客 743

Sentry Native SDK终极指南:高效构建C/C++应用崩溃监控系统

Sentry Native SDK是为C和C++应用程序优化的专业级错误和崩溃报告客户端,支持原生应用程序开发,允许开发者添加标签、面包屑和自定义上下文来丰富错误报告。在本文中,我们将深入探讨如何使用Sentry Native SDK构建完整的崩溃监控系统,涵盖从基础集成到高级配置的完整实战流程。 ## 🚀 核心特性与架构解析 Sentry Native SDK提供了一套完整的崩溃监控解决方

gitblog_00386的博客 268

【VS2022实战】C/C++内存泄漏精准定位:Visual Leak Detector进阶配置与调试技巧

本文详细介绍了在VS2022环境下使用Visual Leak Detector(VLD)进行C/C++内存泄漏检测的进阶配置与调试技巧。内容涵盖VLD核心原理、多工程解决方案配置、第三方库干扰排除、深度解读VLD报告、性能优化及自动化集成等实用技术,帮助开发者精准定位和解决内存泄漏问题,提升代码质量与稳定性。

weixin_30564785的博客 222

Android NDK开发核心技巧与高频问题解决方案

在移动应用开发中,NDK(Native Development Kit)是实现高性能计算和复用C/C++代码的关键技术。其核心原理是通过JNI(Java Native Interface)实现Java与本地代码的交互,在游戏引擎、音视频处理、机器学习等场景中发挥重要作用。开发者常遇到的编译错误、链接问题和内存泄漏等痛点,往往源于对JNI引用管理、ABI兼容性等机制理解不足。通过合理配置CMake构建脚本、掌握ndk-stack调试工具以及优化JNI调用开销,可以显著提升开发效率和运行时性能。本文特别针对ND

weixin_33933118的博客 999

Android C++ 学习笔记5

Android强弱指针/C++设计模式 单例模式-桥接模式

zhuguanlin121的博客 722

ClickHouse 内存分配 Profile 深度分析方法论:从 jemalloc collapsed 栈到可执行的内存定位工作流

> 本文以仓库 [.claude/skills/alloc-profile/SKILL.md](https://link.gitcode.com/i/cc91110aebb83f30a744370e24280601) 为骨架,系统讲解如何分析一份 jemalloc(或 async-profiler / perf)生成的 **collapsed 栈格式**内存分配 profile:如何从单行 `fr

gitblog_00282的博客 691

Unity原生内存泄漏排查利器:全堆栈追踪NativeLeakDetection实战指南

内存泄漏是软件开发中常见且棘手的问题,尤其在涉及原生代码交互的复杂应用中。其原理在于程序分配了内存却未能正确释放,导致可用内存持续减少,最终可能引发应用崩溃或性能严重下降。在Unity开发领域,处理托管内存(C#)尚有垃圾回收机制作为屏障,但原生内存(C++/插件)的泄漏则更为隐蔽和危险。传统的性能分析工具往往只能揭示内存增长的趋势,却难以精确定位泄漏的根源代码行。为此,Unity引擎内置了强大的诊断工具——NativeLeakDetection。该工具的核心技术价值在于其“全堆栈追踪”能力,它通过挂钩底层

weixin_34290096的博客 485

gRPC C 核心故障排查完全指南:absl 日志、GRPC_TRACE 追踪标志与日志噪音治理

本文基于 gRPC 仓库根目录的 [TROUBLESHOOTING.md](https://link.gitcode.com/i/bc8c2cea0b2363d3250c59c81607ab13) 展开,系统讲解如何为基于 C 核心库的 gRPC 实现(C++、Python、Objective-C、PHP、Ruby 等语言绑定)开启额外的日志与追踪能力:包括通过 absl 标志精确控制日志严重级别

gitblog_00030的博客 469

OpenTelemetry OBI - 非侵入式监控各种语言应用的神器

非侵入式监控是解决分布式系统监控难题的关键技术。OpenTelemetry eBPF Instrumentation (OBI) 利用Linux内核的eBPF机制,在不修改应用代码的情况下实现零侵入监控。相比传统监控方式,OBI具有性能优势(CPU开销低)、语言无关性(支持所有编程语言,包括C/C++、Go等二进制编译的语言)和安全性(避免引入生产事故)。文章还演示了如何搭建OBI监控系统。这种技术为老系统、闭源系统、二进制编译语言等难以监控的场景提供了理想的解决方案。

liu_rui_的博客 1252

ClickHouse v22.8.5.29-lts 更新解读:解除 Kafka 消费者数量限制、修复关键内存泄漏与 10 项变更全解析

本文基于 [v22.8.5.29-lts 官方 changelog](https://link.gitcode.com/i/628e4cbd10ebbafa1632732fc0157931),系统梳理该 LTS 维护版本相对 v22.8.4.7-lts(提交 `baad27bcd2f` → `74ffb843807`)的全部变更:包括新增的 `kafka_disable_num_consumers

gitblog_00077的博客 994

brpc CPU Profiler 使用指南:基于 SIGPROF 采样的热点函数分析

brpc 内置了基于 gperftools 的 CPU profiler,可以直接通过 builtin service 页面(`/hotspots/cpu`)或 pprof 工具对运行中的 server 进行采样,找出程序中的热点函数。本文以 [docs/cn/cpu_profiler.md](https://link.gitcode.com/i/fde82c2ed9fd009073d98693b

gitblog_00085的博客 407
上一篇: C++ Thunk技术(初学版)
下一篇: WBINVD--Write Back and Invalidate Cache
maplewasp
博客等级 码龄22年 22粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值