如何读取调用堆栈?

时间:2009-07-31 10:45:39

标签: winapi visual-c++ native memory-address callstack

我们在Windows 2003服务器上通过COM +运行本机C ++应用程序。我最近从事件查看器中注意到它抛出异常,特别是C0000005异常,根据http://blogs.msdn.com/calvin_hsia/archive/2004/06/30/170344.aspx意味着该进程正在尝试写入不在其地址空间内的内存,即访问冲突。

事件查看器中的条目提供了一个调用堆栈:

  

LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate&)+ 0xa26c   LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate&)+ 0x8af4   LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate&)+ 0x13a1   LibFmwk!utilCLogController :: GetFLFInfoLevel(void)const + 0x1070   LibFmwk!utilCLogController :: GetFLFInfoLevel(void)const + 0x186

现在,我明白它给我的方法名称去看看,但我感觉每行末尾的地址(例如+ 0xa26c)试图指向我在该方法中的特定行或指令

所以我的问题是:

  1. 有没有人知道我如何使用这个地址或调用堆栈中的任何其他信息来确定代码中的哪一行掉进来?
  2. 有没有我可以阅读的资源,以便更好地理解调用堆栈,
  3. 是否有任何免费软件/开源工具可以帮助分析调用堆栈,可能附加到调试符号文件和/或二进制文件?
  4. 编辑: 根据要求,以下是导致问题的方法:

    BOOL UTIL_GetDateFromLogByDayDirectory(LPCSTR pszDir, utilCDate& oDate)
    {
    BOOL bRet = FALSE;
    
    if ((pszDir[0] == '%') &&
        ::isdigit(pszDir[1]) && ::isdigit(pszDir[2]) &&
        ::isdigit(pszDir[3]) && ::isdigit(pszDir[4]) &&
        ::isdigit(pszDir[5]) && ::isdigit(pszDir[6]) &&
        ::isdigit(pszDir[7]) && ::isdigit(pszDir[8]) &&
        !pszDir[9])
    {
        char acCopy[9];
        ::memcpy(acCopy, pszDir + 1, 8);
        acCopy[8] = '\0';
    
        int iDay = ::atoi(&acCopy[6]);
        acCopy[6] = '\0';
        int iMonth = ::atoi(&acCopy[4]);
        acCopy[4] = '\0';
        int iYear = ::atoi(&acCopy[0]);
    
        oDate.Set(iDay, iMonth, iYear);
    
        bRet = TRUE;
    }
    
    return (bRet);
    

    }

    这是10年前由我们公司的一位成员编写的代码,该代码早已消失,所以我不认为确切知道这是做什么但是我知道它涉及重命名日志目录的过程从'今天'到具体日期,例如%20090329。运算符的数组索引,memcpy和地址确实让它看起来很可疑。

    我们似乎遇到的另一个问题是,这只发生在生产系统上,我们从未在我们的测试系统或开发系统上重现它,这将允许我们附加调试器。

    非常感谢! 安迪

3 个答案:

答案 0 :(得分:5)

其他人已经在这些线之间说过,但没有明确说明。看看:

LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate &) + 0xa26c

0xa26c偏移是巨大,超过了函数的结尾。调试器显然没有LibFmwk的正确符号,所以它依赖于DLL导出并显示相对于它能找到的最接近的偏移量。

所以,是的,得到适当的符号然后它应该是轻而易举的。 UTIL_GetDateFromLogByDayDirectory在这里没有错。

答案 1 :(得分:3)

如果你真的需要将这些地址映射到你的函数 - 你需要使用.MAP文件并查看这些地址真正指向的位置。

但是在你的情况下,我宁愿在调试器下调查这个问题(例如MSVS调试器或windbg);作为替代方案(如果在客户的站点发生崩溃),您可以生成故障转储并在本地进行研究 - 可以通过Windows MiniDumpWriteDump API或SysInternals ProcDump实用程序(http://download.sysinternals.com/Files/procdump.zip)完成。

确保生成并且可用所有必需的符号文件(还设置了微软符号服务器路径,以便Windows DLL的入口点也得到解决)。

恕我直言,这只是您需要的网站:http://www.dumpanalysis.org - 这是涵盖您所有问题的最佳资源。 考虑一下这个PDF - http://windbg.info/download/doc/pdf/WinDbg_A_to_Z_color.pdf

答案 2 :(得分:1)

第2点和第3点很容易回答:

第三点。任何调试器。这就是他们的目标。设置调试器以打破此特殊异常。您应该能够通过callstack单击自己并在堆栈上找到不同的调用(至少delphi可以执行此操作,因此Visual Studio也应该能够执行此操作)。如果可能,编译时不进行优化。 OllyDBG可能也可以正常工作 - 可能与其跟踪功能结合使用。

第二点。有关x86 Assembler,Reverseengineering的任何信息......请尝试:OpenRCENASM DocumentationASM Community

第一点。 callstack告诉你函数。我不知道它是按顺序写入还是以相反的顺序写入 - 所以它可能是第一行是最后一个被调用的函数或第一个被调用的函数。在调试器的帮助下跟随调用。有时你可以在asm和代码之间进行更改(取决于调试器,映射文件......)。如果您没有源 - 学习汇编程序,请阅读逆向工程。阅读您在第三方组件中调用的功能的文档。也许你不满足前提条件。

如果你能详细介绍一下这个程序(你有源代码的哪些部分,是否涉及到库调用?,...)


现在有些代码阅读:

该函数接受指向零终止字符串的指针和对日期对象的引用。假定指针有效!

该函数检查字符串是否为特定格式(%后跟8位后跟\ 0)。如果不是这种情况,则返回false。此检查(大if)访问指针而不进行任何有效性检查。不检查长度,如果指针指向野外某处,则访问此空间。我不知道更短的字符串是否会导致问题。它不应该因为方式&&被评估。

然后在堆栈上分配一些内存。字符串的数字部分被复制到它(这是好的),缓冲区得到\ 0终止。 atois提取数字。这将起作用,因为使用了不同的起始位置和每个部件后的\ 0-终止。不知何故,狡猾但很好。一些评论会使一切都清楚。

然后将这些数字插入到对象中。它应该是有效的,因为它通过引用传递给函数。我不知道你是否可以传递对已删除对象的引用,但如果是这种情况,这也可能是你的问题。

无论如何 - 除了缺少对字符串指针的检查外,这个功能是合理的,不是你问题的原因。它只是抛出异常的地方。搜索传递给此函数的参数。它们总是有效吗?做一些记录。

我希望自己没有犯任何重大错误,因为我是Delphi程序员。如果我这样做 - 随意评论。