WINCE下‘Prefetch Abort’、'Raised Exception'如何定位
在WINCE执行程序,报错如下
Exception 'Prefetch Abort' (3): Thread-Id=06b90002(pth=8f6e5d50), Proc-Id=043500c6(pprc=8741c000) 'SeriesSample.exe', VM-active=043500c6(pprc=8741c000) 'SeriesSample.exe'
PC=19930520(???+0x19930520) RA=000124c4(SeriesSample.exe+0x000024c4) SP=0047ec84, BVA=19930520
Exception 'Raised Exception' (-1): Thread-Id=06b90002(pth=8f6e5d50), Proc-Id=00400002(pprc=86809308) 'NK.EXE', VM-active=043500c6(pprc=8741c000) 'SeriesSample.exe'
PC=c003bc70(k.coredll.dll+0x0001bc70) RA=801674c8(kernel.dll+0x000064c8) SP=d490f3b8, BVA=ffffffff
不知如何定位 WinCE 异常 VC Abort EXception
[解决办法]
使用 MAP 文件
[解决办法]
不过就 LZ 提供的错误信息,PC 指针指向内核,应该是指针越界了吧
[解决办法]
这种方法,网上是可以 google 到的。
DATA abort定位方法
该方法只能定位显性泄漏,定位到的C语句一定产生泄漏了,但可能这个位置是“理论上”不会出问题的代码.那么这是由于同进程内其他代码泄漏而影响了进程的堆区或栈区(隐性泄漏,这个地方不会产生data abort exception),然后被定位出来的代码才被动地显性泄露,产生data abort。
首先在DEBUG版本中定位DATA ABORT的方法,地球人应该都知道了吧,我就不废话了。Platform Builder或VS2005、EVC这类IDE工具会在DEBUG模式下自动停在出错的那句,情况就很显然了。
RELEASE版本下的泄漏就要稍微麻烦一点,如何快速定位呢?
案例一:用EVC编译的应用程序泄漏
首先我做了一个内存泄漏的程序MemoryLeakTest.exe,里面做了一个泄漏的函数:
void MemoryLeak(){
int *p = (int *)0x81000000;*p = 10;
}
编译的时候注意先在project settings里的Link页里勾选“Generate mapfile”,会生成一个map文件。如下图:
把编译出来的RELEASE版本可执行文件放到CE5平台下运行。出错的时候串口打印了一句
Data Abort: Thread=83ad1d38 Proc=820266d0 'MemoryLeakTest.exe'AKY=00000021 PC=00011008(MemoryLeakTest.exe+0x00001008) RA=00011030(MemoryLeakTest.exe+0x00001030) BVA=81000000 FSR=0000000d
这句是系统自动输出的。我们得到了一个关键的信息:PC指针。和PC指针在MemoryLeakTest.exe中的偏移量。然后打开编译时生成的MemoryLeakTest.map文件,文件内容如下:
MemoryLeakTest
Timestamp is 46fcbb17 (Fri Sep 28 16:28:07 2007)
Preferred load address is 00010000
Start???????? Length???? Name?????????????????? Class0001:00000000 00000258H .text?????????????????? CODE0002:00000000 00000014H .xdata????????????????? DATA0002:00000014 00000014H .idata$2??????????????? DATA0002:00000028 00000014H .idata$3??????????????? DATA0002:0000003c 00000010H .idata$4??????????????? DATA0002:0000004c 0000000cH .idata$6??????????????? DATA0002:00000058 00000000H .edata????????????????? DATA0003:00000000 00000010H .idata$5??????????????? DATA0003:00000010 00000004H .CRT$XCA??????????????? DATA0003:00000014 00000004H .CRT$XCZ??????????????? DATA0003:00000018 00000004H .CRT$XIA??????????????? DATA0003:0000001c 00000004H .CRT$XIZ??????????????? DATA0003:00000020 00000004H .CRT$XPA??????????????? DATA0003:00000024 00000004H .CRT$XPZ??????????????? DATA0003:00000028 00000004H .CRT$XTA??????????????? DATA0003:0000002c 00000004H .CRT$XTZ??????????????? DATA0003:00000030 00000009H .bss??????????????????? DATA0004:00000000 00000038H .pdata????????????????? DATA0005:00000000 00000010H .rsrc$01??????????????? DATA0005:00000010 00000000H .rsrc$02??????????????? DATA
? Address???????? Publics by Value????????????? Rva+Base???? Lib:Object
?0001:00000000?????? ?MemoryLeak@@YAXXZ????????00011000 f?? MemoryLeakTest.obj0001:00000010?????? WinMain??????????????????? 00011010 f?? MemoryLeakTest.obj0001:0000002c?????? WinMainCRTStartup????????? 0001102c f?? corelibc:pegwmain.obj0001:000000a0?????? _cinit???????????????????? 000110a0 f?? corelibc:crt0dat.obj0001:00000210?????? exit?????????????????????? 00011210 f?? corelibc:crt0dat.obj0001:00000228?????? _XcptFilter??????????????? 00011228 f?? coredll:COREDLL.dll0001:00000238?????? __C_specific_handler?????? 00011238 f?? coredll:COREDLL.dll0001:00000248?????? LocalFree????????????????? 00011248 f?? coredll:COREDLL.dll0002:00000014?????? __IMPORT_DESCRIPTOR_COREDLL 00012014???? coredll:COREDLL.dll0002:00000028?????? __NULL_IMPORT_DESCRIPTOR?? 00012028???? coredll:COREDLL.dll0003:00000000?????? __imp___C_specific_handler 00013000???? coredll:COREDLL.dll0003:00000004?????? __imp_LocalFree??????????? 00013004???? coredll:COREDLL.dll0003:00000008?????? __imp__XcptFilter????????? 00013008???? coredll:COREDLL.dll0003:0000000c?????? \177COREDLL_NULL_THUNK_DATA 0001300c???? coredll:COREDLL.dll0003:00000010?????? __xc_a???????????????????? 00013010???? corelibc:crt0init.obj0003:00000014?????? __xc_z???????????????????? 00013014???? corelibc:crt0init.obj0003:00000018?????? __xi_a???????????????????? 00013018???? corelibc:crt0init.obj0003:0000001c?????? __xi_z???????????????????? 0001301c???? corelibc:crt0init.obj0003:00000020?????? __xp_a???????????????????? 00013020???? corelibc:crt0init.obj0003:00000024?????? __xp_z???????????????????? 00013024???? corelibc:crt0init.obj0003:00000028?????? __xt_a???????????????????? 00013028???? corelibc:crt0init.obj0003:0000002c?????? __xt_z???????????????????? 0001302c???? corelibc:crt0init.obj0003:00000030?????? __onexitend??????????????? 00013030???? <common>0003:00000034?????? __onexitbegin????????????? 00013034???? <common>0003:00000038?????? _exitflag????????????????? 00013038???? <common>
?entry point at??????? 0001:0000002c
?Static symbols
?0001:0000010c?????? doexit???????????????????? 0001110c f?? corelibc:crt0dat.obj
OK,首先,里面有一句“Preferred load address is 00010000”,这意味着DATA ABORT那句的PC=00011008(MemoryLeakTest.exe+0x00001008) 我们必须把括号里的0x1008加上这个load address的偏移量,得到0x11008(注意不能直接用PC,一会儿再给个案例就知道了),然后我们在函数偏移列表里看Rva+Base这栏,找到0x11008落在了MemoryLeak函数的地址范围里,所以是MemoryLeak函数泄漏了。
案例二:在OAL层做了一个泄漏的函数,用Platform Builder进行RELEASE版编译并LINK到NK.exe里,然后应用程序MemoryLeakPB.exe调用该函数导致泄漏
步骤类似,只是Platform Builder默认就会在RELEASE目录下生成.map文件。应用程序去调用泄漏的OAL函数时,出现
Data Abort: Thread=822fc000 Proc=820267c0 'MemoryLeakPB.exe'AKY=00000041 PC=8023e3c8(NK.EXE+0x0003e3c8) RA=8023e1d4(NK.EXE+0x0003e1d4) BVA=8e000000 FSR=00000005
注意我们这次是NK.EXE里制造泄漏,所以PC指针不是在0x00011008这样的Slot 0低地址了,而是在0x80000000以上的KERNEL区域了。nk.map很长,我选择关键段落来贴
************************************************************kern
?Timestamp is 46fca696 (Fri Sep 28 15:00:38 2007)
?Preferred load address is 00010000
?...0001:0003d2c8?????? OALIoCtlHal_GetDeviceId??? 0004e2c8 f?? oal_ioctl:deviceid.obj0001:0003d398?????? OALIoCtlHal_MemoryLeak?? ?0004e398 f?? oal_ioctl:leaktest.obj0001:0003d438?????? OALIoCtlHal_DdkCall??????? 0004e438 f?? oal_io:ioctl.obj...************************************************************
所以 NK.EXE + 0x0003e3c8 = 0x10000(Preferred load address) + 0x0003e3c8 = 0x4e3c8, 落在了OALIoCtlHal_MemoryLeak函数里
结论:原理上很简单,就是利用DATA ABORT消息中的PC值,配合MAP文件可以快速定位到泄漏的函数。定位到之后,嘿嘿,谁LEAK谁请客咯。
用上文的方法,不但网友kevin没有成功定位到泄漏的原因(见后面回帖,他定位到微软USBFN的MDD层代码),我这项目组里的人也没抓到原因(定位到PRIVATE下LoadLibrary函数相关的代码)。昨晚我想了下,这个方法的确有漏洞,早上我做了个试验,建立个DialogBox,主处理函数如下static DWORD *gpTest = NULL;
int CALLBACK LeakProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){??? switch(uMsg){case WM_INITDIALOG:gpTest = (DWORD*)malloc(1);break;case WM_COMMAND:switch(wParam){case ID_LEAK:*((int*)0x1A013860) = 0x1C000000;break;case ID_CLOSE:if(gpTest){*gpTest = 1;}EndDialog(hDlg, 0);break;}break;}return FALSE;}我运行了几次,每次该进程的BaseAddr = 0x1A000000, 而?&gpTest = 0x1A013860,在进程的全局变量区;malloc之后gpTest = 0x00030230在进程的堆区。所以我:(1) 在ID_LEAK按钮按下后,模拟一次内存泄漏,直接对&gpTest地址上的数值改写,把malloc后的0x00030230改成0x00000000, 注意虽然该处已经泄漏了,但是并没有产生data abort exception.
(此处当然不会出错!如果出错,那么给gpTest赋值为空时一样会出错)(2) 然后在ID_CLOSE按钮按下后,装做不知道前面那处泄漏,代码风格严谨地先判断下指针是否为空,然后试图在malloc得到的堆区 0x00030230地址上写个值,但是由于gpTest这个指针已经被改写为指向其他进程空间了,所以在*gpTest = 1这句产生data abort exception并停下来了。
(说明 gpTest 的值被误修改了,否则不会出错)(3) 所以,按照上文的方法,只能抓到*gpTest = 1这句泄露,而实际上这句是无辜的,真正的元凶*((int*)0x1A013860) = 0x1C000000这句却没有被抓出来。
[解决办法]
给你个网址 我就看这个 定位的 照着一步步做就可以了
http://blog.csdn.net/wskelan/article/details/7584843