动机:
在游戏开发过程中,我们利用 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 值 ( 也就是当前函数的栈顶 ) 就能够遍历得到所有调用堆栈层次.
我们将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, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hSession, server,
INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", action, NULL, NULL, &accept, 0, 1);
HttpSendRequest(hRequest, hdrs, strlen(hdrs), frmdata, strlen(frmdata));
此后我们只需要定期观察所提交的内容,便可以立即得知是否有异常出现.根据同一异常出现的几率可以得知是否是致命的错误,是否需要紧急更新.
没有评论:
发表评论