代码改变世界

利用压力测试来保证软件的质量(四) 内存泄露问题

2010-09-10 21:15 王克伟 阅读(...) 评论(...) 编辑 收藏

内存泄露

这是实际产品中的一个严重的内存泄漏的Bug,这样描述的:

[MemoryLeak] Memory comsuption keeps inscreasing when open/close applications. We saw similar issue on several different applications. Here's some data for WMP and Calculator:

WMP:
Iteration                    0    1    2    3    4    5    6    7    8    9    10
Free Mem (MB)    133.83    133.51    133.33    133.17    132.94    132.80    132.64    132.48    132.32    132.16    132.00

Calculator:
Iteration                     0    1    2    3    4    5    6    7    8    9    10
Free Mem (MB)    133.88    133.13    132.92    132.80    132.66    132.55    132.46    132.33    132.21    132.10    131.98

泄露发生在private/shellw/gserver/shell32g/san_cmn.cpp L275:

static BOOL FreeAppData( SANMSG *psm ) 
{ 
    BOOL dwRes = FALSE; 
    DWORD dwExitCode; 
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, psm->dwProcessID); 

    if( psm->pvAppData && 
        (hProcess && GetExitCodeProcess( hProcess, &dwExitCode)) && 
        STILL_ACTIVE == dwExitCode ) 
    { 
        switch (psm->dwNotify) 
        { 
        case SAN_NOTIFY_LAUNCH: 
            __try { 

                dwRes = !RemoteHeap::LocalFree(psm->pvAppData, psm->dwProcessID); 
                psm->pvAppData = NULL; 
            } __except(WatsonReportFault(GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER)) { 
                DEBUGMSG(ZONE_SAN,(TEXT("freeappdata threw exception.\r\n"))); 
                dwRes = FALSE; 
            } 
            break; 
        } 
        //CloseHandle(hProcess); 
    } 
    if (hProcess) 
        CloseHandle(hProcess); //应该在此位置关闭进程的句柄!一个疏忽造成了很严重的内存泄露。 

    return dwRes; 
} 

我查一下,这个问题在移植过程中疏忽造成的(不断的代码修改造成的,看来纵有百虑总有一疏啊)。

FreeAppData API在程序启动时会调用,而在关闭程序时不会调用,比如启动WMP时:

SHELL32!FreeAppData(SANMSG * 0x007ab720)  line 253 
SHELL32!XSHAppNotifyDone(HWND__ * 0x700578a0, long 0x00000000, unsigned long 0x00000001, void * 0x009dfc30)  line 176 + 12 bytes 
AYGSHELL!xxx_SHAppNotifyDone(HWND__ *, long, unsigned long, void *)  line 40 + 23 bytes 
WMPOCX!CPlayerWindow::OnSANMessage(HWND__ *, unsigned int, long, bool &)  line 1104 + 20 bytes 
WMPOCX!CPlayerWindow::WndProc(HWND__ *, unsigned int, unsigned int, long)  line 143 + 44 bytes 
GWES!WindowProcCallback(void * 0x07f60102, long (HWND__ *, unsigned int, unsigned int, long)* 0x423637c0, CWindow * 0x700578a0, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, bool * 0xd890fb53)  line 3198 + 21 bytes 
GWES!CWindow::CallWindowProcW_I(CePtr_t<long (__cdecl*)(HWND__ *,unsigned int,unsigned int,long)> {...}, HWND__ * 0x700578a0, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, SendMsgEntry_t * 0x00000000)  line 3403 + 33 bytes 
GWES!MsgQueue::DispatchMessageW_I(const tagMSG * 0x0003fb50)  line 4967 + 38 bytes 
GWES!PixelDoubled_t::DispatchMessageW_I(const tagMSG * 0x0003fb50)  line 2083 + 15 bytes 
COREDLL!DispatchMessageW(const tagMSG *)  line 2947 + 9 bytes 
WMPOCX!StartThePlayer(HINSTANCE__ *, int)  line 92 + 9 bytes 
WMPLAYER!PlayerEntry()  line 99 + 9 bytes 
COREDLL!MainThreadBaseFunc(void *, const wchar_t *, const wchar_t *, HINSTANCE__ *, HINSTANCE__ *, HINSTANCE__ *)  line 1072 + 60 bytes 

 

奇怪的是启动IE时不会调用到这个API,而启动闹铃时会:

SHELL32!FreeAppData(SANMSG * 0x009b2940)  line 253 
SHELL32!XSHAppNotifyDone(HWND__ * 0x70053000, long 0x00000000, unsigned long 0x00000001, void * 0x009dfc30)  line 176 + 12 bytes 
AYGSHELL!xxx_SHAppNotifyDone(HWND__ *, long, unsigned long, void *)  line 40 + 23 bytes 
CLOCKDLL!HandleSAN_NOTIFY(tagAppState *, HWND__ *, unsigned int, long)  line 1095 + 20 bytes 
CLOCKDLL!WndProc(HWND__ *, unsigned int, unsigned int, long)  line 1390 + 21 bytes 
GWES!WindowProcCallback(void * 0x011a002a, long (HWND__ *, unsigned int, unsigned int, long)* 0x417a34a0, CWindow * 0x70053000, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, bool * 0xd76ffb53)  line 3198 + 21 bytes 
GWES!CWindow::CallWindowProcW_I(CePtr_t<long (__cdecl*)(HWND__ *,unsigned int,unsigned int,long)> {...}, HWND__ * 0x70053000, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, SendMsgEntry_t * 0x00000000)  line 3403 + 33 bytes 
GWES!MsgQueue::DispatchMessageW_I(const tagMSG * 0x0002f9dc)  line 4967 + 38 bytes 
GWES!PixelDoubled_t::DispatchMessageW_I(const tagMSG * 0x0002f9dc)  line 2083 + 15 bytes 
COREDLL!DispatchMessageW(const tagMSG *)  line 2947 + 9 bytes 
COMMCTRL!_RealPropertySheet(PROPDATA *)  line 3821 + 9 bytes 
COMMCTRL!PropertySheetW(const _PROPSHEETHEADERW *)  line 3992 + 9 bytes 
CLOCKDLL!DisplayPropSheet(HWND__ *, tagAppState *)  line 187 + 13 bytes 
CLOCKDLL!StartClock(HINSTANCE__ *, unsigned short *, int, HINSTANCE__ *)  line 754 + 16 bytes 
CLOCK!WinMain(HINSTANCE__ *, HINSTANCE__ *, unsigned short *, int)  line 27 + 21 bytes 
COREDLL!MainThreadBaseFunc(void *, const wchar_t *, const wchar_t *, HINSTANCE__ *, HINSTANCE__ *, HINSTANCE__ *)  line 1072 + 60 bytes


当然健全的测试机制是发现这些内存泄漏的问题的前提。