你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:文章管理 / 本期精品文章
显示栈跟踪的断言
 

杨芳芳

摘要 本文详细分析了Win32下栈的结构,使用汇编代码演示了如何进行栈跟踪,通过MAP文件来确定和显示函数名称,并在最后提出了其他的实现方法,以供参考。

关键词 Assert,栈,MAP文件

一、概述

断言是保证编写正确程序的最有效的工具之一,主要原因是因为它简单易用。在保持其特性的基础上增强断言的功能还是非常有意义的,在《自定义Assert宏》一文中,笔者给Assert宏增加了一些新的功能,虽然增加了不少代码,但使用时还是一个宏贯穿始终,唯一需要做的事情就是把头文件包含到工程中即可。这里笔者在原文的基础上给Assert增加了显示函数堆栈信息的功能,大大增强了Assert的功能。

现假设编写了某个函数,其参数是个指针,并且在函数中用断言对传入的指针非空作了校验。进一步假设这个函数是一个低级别的工具函数,它在很多的地方都被使用。如果这时断言在运行时被触发了,仅仅能够知道某个调用者使用了空指针作为参数,导致断言的判断条件失败,而要找出究竟是哪个调用者是件很困难的事情。当然,编译器的开发商已经认识到把断言与调试器连接起来将更加有用,这样可以让开发者很容易地观察堆栈信息。因此,目前在Win32中使用的断言宏都可以从断言对话框中跳转到调试器中。然而,在某些情况下这不是可行的方案。例如beta测试,一般会保留断言用于捕获错误,但并不会发布源代码;或者在调试器外运行程序时出现了断言对话框,但又无法跳转到调试器中;或者更加严重的是运行程序的机器居然没有安装任何调试器,并且出现的错误很难再重现,这时将非常希望断言能够提供足够的信息,用于判断出错的原因。本文主要介绍如何在Assert中添加函数堆栈信息。

二、堆栈

Win32中调用函数时,先把返回地址和函数参数压入栈中,然后再跳转到函数入口地址,执行被调用的函数。被调函数从栈中便可取出传入的参数和返回地址,在函数结束时可以根据返回地址跳转到原调用处,这样就完成了整个函数的调用过程。因此只要函数未全部返回,栈中就保存了当前函数的调用过程,其中的信息对于调试非常重要,跟踪整个栈便能发现一些问题的根源所在。接下来将试图读取栈中的内容,图1显示了调用栈的典型结构。

前一个栈空间EBP指针

当前调用的返回地址

数据

...

前一个栈空间EBP指针

...

1 栈结构示意图

扩展基地址指针寄存器(Intel x86EBP寄存器,也就是在8086中的基地址指针,这里是32位的扩展寄存器)指向栈顶。最上面的元素包含了前一个栈空间的EBP值,通过该值可以找到前一个栈空间。紧接着下面的元素包含了当前栈空间的返回地址,也就是当前被调函数的返回地址。再往下就是函数的参数等数据。因此,只要获得了初始的EBP寄存器内容,就能根据每个栈空间的第一个值一直跟踪到整个栈结束。至于EBP寄存器的内容,只要使用内嵌汇编代码就能实现,核心代码如下所示:

    long caller;

    printf("\n函数返回地址:\n");

    for(int index=0; index<N; index++)

    {

        _asm

        {

            mov ebx, ebp

            mov ecx, index

            inc ecx

            xor eax, eax

StackTrace_next:

            mov eax, [ebx+4]

            mov ebx, [ebx]

            dec ecx

            jnz StackTrace_next

            mov caller, eax

        }

        printf("\t0x%08x\n",caller);

    }

三、MAP文件

上述代码中并不知道栈的内容究竟何时结束。单凭栈的内容无法知道栈何时结束,只能通过函数的返回地址进行判断,这个问题暂且搁置。但是现在还有一个函数返回地址的问题(如图2所示)。

2 函数返回地址

即使获得了函数的返回地址并显示出来,也没有多大用处,谁也不愿意直接观察函数地址,从中难以发现任何问题。进行汇编级调试的程序员很清楚,通过链接器产生的MAP文件可以找出对应地址的函数符号。MAP文件是程序在最后链接时产生的链接描述文件,记录了程序在链接后各个段(数据段、程序段等)的起始位置,每个符号所在的地址等信息。通过MAP文件,就能知道程序中的所有内容在内存中的分布情况,因此由MAP文件便能找到返回地址对应的函数了。大多数链接器都支持输出这种类型的文件,在Visual C++ 6.0的设置如图3所示。

3  MAP文件设置图示

 

  推荐精品文章

·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 
·2023年7月目录
·2023年6月目录 
·2023年5月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089