2007年4月3日星期二

Dump 调用堆栈的原理以及异常信息的反馈

动机:

在游戏开发过程中,我们利用 QA 部门来做产品的质量保证,尽可能将绝大部分错误消化在内部,保证游戏的版本质量,但是 QA 部门毕竟有他的局限性,尽管经过严格的测试也很难保证将所有的问题一网打尽.

 

通过在 Log 中转储的错误信息,我们可以进一步找出问题,但是 Log 文件产生在终端,我们拿到的也仅仅是公司内部测试部门产生的 Log 文件,显然公司内部得到的信息是很有限的,如果能从玩家那里拿到异常信息,我们才能最快的去解决问题,尽可能在错误产生重大影响之前将其解决,所以我们有必要从被动的获取异常信息,转为主动去获取.

 

可行性 :

       在错误发生时 Dump 调用堆栈,可以让我们知道错误发生的位置,这比已往普通的 LOG 更加有效的多.我们可以将出错的堆栈地址反馈回来.这一切在终端出现异常的时候自动进行. Windows 操作系统提供的 SEH 结构化异常机制可能让我们在程序崩溃的瞬间处理这些事情.

 

效率问题 :

       SHE windows 的异常机制,除非在编译时候特别指定不使用,否则总有默认的 SEH 处理机制, kernel32.dll 中有默认的 SEH 处理接口,当我们需要自己处理异常的时候,我们的处理点会挂接在异常处理链的最前端,这种链类似 Hook 的链.链的头部放在 fs[0] 的位置.也就是说效率的问题是可以不必考虑,

 

 

具体实现 :

       通过阅读反汇编代码可以了解函数调用过程中堆栈的结构 :

      

       1 函数调用时 CALL 将下一行指令地址压入堆栈

       2 函数运行第一行会将 EBP 压入堆栈

       3 保存当前堆栈地址到 EBP (mov ebp,esp)

      

       再遇到 call 时从第一步执行,所以每次第二步压入堆栈的都是上一层函数调用的 ESP 地址,而这个地址 +4 字节偏移则是当前调用函数返回后的下一条指令,也就是上一层函数的地址,所以我们只要知道当前函数的 EBP ( 也就是当前函数的栈顶 ) 就能够遍历得到所有调用堆栈层次.

       dumpebp.jpg

我们将windows SEH 结构化异常引入后,可以在异常发生的时候得到当前的EBP值,从而通过这个值得到整个调用堆栈的地址.

 

在发布工程的时候,我们只需要生成map文件,就可以通过这个地址得到崩溃位置.使用HTTP GET 或POST方式可以将我们所需要的崩溃信息提交到我们指定的网站.这种方式只是通过URL参数来提交数据,只需要使用API InternetOpenUrl就可以很方便的将信息提交.此外如果不使用HTTP方式,我们也可以在这个时候创建新的socket 对指定的服务器进行连接来传输数据.

    
    static TCHAR hdrs[] 
= _T("Content-Type: application/x-www-form-urlencoded"
); 
    static 
const TCHAR* accept= _T("Accept: */*"
); 
        static TCHAR action[]=_T("datecomit.aspx"
);//预提交的页面
        static TCHAR server[]=_T("192.168.9.119");//提交的server地址

    static TCHAR frmdata[
1024={0}; 
    _tcscpy(frmdata,_T("message=this is a test message");  //提交数据, message为提交名字   
    
    
// for clarity, error-
checking has been removed 
    HINTERNET hSession 
= InternetOpen("MyAgent"

    INTERNET_OPEN_TYPE_PRECONFIG, 
NULLNULL0
); 
    HINTERNET hConnect 
= InternetConnect(hSession, server

    INTERNET_DEFAULT_HTTP_PORT, 
NULLNULL, INTERNET_SERVICE_HTTP, 01
); 
    HINTERNET hRequest 
= HttpOpenRequest(hConnect, "POST", actionNULLNULL&accept, 01
); 
    HttpSendRequest(hRequest, hdrs, strlen(hdrs), frmdata, strlen(frmdata)); 

 

此后我们只需要定期观察所提交的内容,便可以立即得知是否有异常出现.根据同一异常出现的几率可以得知是否是致命的错误,是否需要紧急更新.


张志强
2007-04-03

没有评论: