资源泄漏的调试
来源:互联网 发布:gta5卡顿优化 编辑:程序博客网 时间:2024/05/17 08:22
资源泄漏就是进程向系统申请了资源但是用完了没有还给系统,造成了系统资源一直被占用,无法重复利用。系统资源有限,资源泄漏的后果就是资源耗尽。
简单的句柄泄漏
以如下代码生成exe:
#ifndef __SRV_H#define __SRV_H#include <windows.h>class CServer{public: static BOOL GetTextFileContents(HANDLE hEvent, PWSTR pszFileName, PSTR pBuffer, DWORD dwBufferLen); private: CServer(){}; ~CServer(){}; static DWORD WINAPI RequestWorker(LPVOID lpParameter); struct WorkerData { PWSTR pszFileName; PSTR pBuffer; DWORD dwBufferLen; HANDLE hCompletionHandle; } ;} ;#endif
#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <time.h>#include "srv.h"BOOL CServer::GetTextFileContents(HANDLE hEvent, PWSTR pszFileName, PSTR pBuffer, DWORD dwBufferLen){ BOOL bRet=FALSE; if(hEvent!=NULL && pszFileName!=NULL && pBuffer!=NULL && dwBufferLen!=0) { WorkerData* pWorkerData=new WorkerData; if(pWorkerData!= NULL) { pWorkerData->dwBufferLen=dwBufferLen; pWorkerData->pBuffer=pBuffer; pWorkerData->pszFileName=pszFileName; pWorkerData->hCompletionHandle=hEvent; bRet=QueueUserWorkItem(RequestWorker, (LPVOID) pWorkerData, WT_EXECUTELONGFUNCTION); if(!bRet) { delete pWorkerData; } } } return bRet;}DWORD WINAPI CServer::RequestWorker(LPVOID lpParameter){ DWORD dwRet=0; WorkerData* pWorkerData=reinterpret_cast<WorkerData*>(lpParameter); HANDLE hFile=CreateFile(pWorkerData->pszFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile!=INVALID_HANDLE_VALUE) { DWORD dwBytesRead=0; BOOL bRet=ReadFile(hFile, (LPVOID) pWorkerData->pBuffer, (pWorkerData->dwBufferLen-1), &dwBytesRead, NULL); if(bRet==TRUE) { dwRet=1; } } SetEvent(pWorkerData->hCompletionHandle); delete pWorkerData; return dwRet;}
#include <windows.h>#include <strsafe.h>#include <stdio.h>#include "srv.h"#define MAX_LEN 255void __cdecl wmain(){ wprintf(L"Client application console menu\n"); wprintf(L"================================\n"); HANDLE hCompletionEvent=CreateEvent(NULL,FALSE,FALSE,NULL); if(hCompletionEvent==NULL) { wprintf(L"\nFailed to create a new event\n"); return; } BOOL bContinue=TRUE; while(bContinue) { wprintf(L"Enter filename to read > "); WCHAR pFileName[MAX_LEN]; StringCchGets(pFileName, MAX_LEN); if(wcslen(pFileName)) { CHAR szFiledata[1024];memset(szFiledata, 0, 1024); BOOL bRet=CServer::GetTextFileContents(hCompletionEvent, pFileName, szFiledata, 1024 ) ; if(bRet==FALSE) { wprintf(L"\nFailed to read file\n"); } else { wprintf(L"\nScheduled request successfully\n"); if(WaitForSingleObject(hCompletionEvent, INFINITE)!=WAIT_FAILED) { printf("\nData read:\n%s\n", szFiledata); } } } else { wprintf(L"Exiting application...\n"); bContinue=FALSE; } } CloseHandle(hCompletionEvent);}运行程序,读取文件多次后发现句柄数一直增长并空闲一段时候后并没有减少的情况,初步判断存在句柄泄漏:
通过process explorer能查看到更多的信息:
a.txt打开之后一直没有关闭,a.txt是用户的输入,跟踪参数的传递路径可以轻易找到函数RequestWorker存在泄漏。
更复杂的泄漏
我们使用模拟句柄随机泄漏的情况:
#ifndef __SRV_H#define __SRV_H#include <windows.h>class CServer{public: CServer(){}; ~CServer(){}; PSID GetSID(); DWORD GetPrivilegeCount(); DWORD GetGroupCount(); private: HANDLE GetToken();} ;#endif
#include "srv.h"#include <windows.h>#include <stdio.h>PSID CServer::GetSID(){ PSID pSid = NULL; HANDLE hToken = INVALID_HANDLE_VALUE; hToken = GetToken(); if(hToken!=INVALID_HANDLE_VALUE) { DWORD dwNeeded=0; BOOL bRes=GetTokenInformation(hToken, TokenUser, NULL, 0, &dwNeeded ); if(bRes==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_USER* pBuffer=(TOKEN_USER*) new BYTE[dwNeeded]; if(pBuffer!=NULL) { BOOL bRes=GetTokenInformation(hToken, TokenUser, (LPVOID)pBuffer, dwNeeded, &dwNeeded ); if(bRes==TRUE) { DWORD dwSidLen=GetLengthSid(pBuffer->User.Sid); pSid=static_cast<PSID>(new BYTE[dwSidLen]); if(pSid!=NULL) { if(CopySid(dwSidLen, pSid, pBuffer->User.Sid)==FALSE) { delete[] pSid; pSid=NULL; } } } } delete pBuffer; } } return pSid;}DWORD CServer::GetPrivilegeCount(){ DWORD dwPrivCount=0; HANDLE hToken = INVALID_HANDLE_VALUE; hToken = GetToken(); if(hToken!=INVALID_HANDLE_VALUE) { DWORD dwNeeded=0; BOOL bRes=GetTokenInformation(hToken, TokenStatistics, NULL, 0, &dwNeeded ); if(bRes==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_STATISTICS* pBuffer=reinterpret_cast<TOKEN_STATISTICS*>(new BYTE[dwNeeded]); if(pBuffer!=NULL) { BOOL bRes=GetTokenInformation(hToken, TokenStatistics, (LPVOID)pBuffer, dwNeeded, &dwNeeded ); if(bRes==TRUE) { dwPrivCount=pBuffer->PrivilegeCount; } } delete[] pBuffer; } CloseHandle(hToken); } return dwPrivCount;}DWORD CServer::GetGroupCount(){ DWORD dwGroupCount=0; HANDLE hToken = INVALID_HANDLE_VALUE; hToken = GetToken(); if(hToken!=INVALID_HANDLE_VALUE) { DWORD dwNeeded=0; BOOL bRes=GetTokenInformation(hToken, TokenStatistics, NULL, 0, &dwNeeded ); if(bRes==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_STATISTICS* pBuffer=reinterpret_cast<TOKEN_STATISTICS*>(new BYTE[dwNeeded]); if(pBuffer!=NULL) { BOOL bRes=GetTokenInformation(hToken, TokenStatistics, (LPVOID)pBuffer, dwNeeded, &dwNeeded ); if(bRes==TRUE) { dwGroupCount=pBuffer->GroupCount; } } delete[] pBuffer; } CloseHandle(hToken); } return dwGroupCount;}HANDLE CServer::GetToken(){ HANDLE hToken = INVALID_HANDLE_VALUE; BOOL bRet = OpenThreadToken (GetCurrentThread(), TOKEN_READ, TRUE, &hToken ); if(bRet==FALSE) { bRet=OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken ); } return hToken;}
#include <windows.h>#include "srv.h"#include <stdio.h>#include <stdlib.h>#include <conio.h>#include <time.h>#define NUM_ARGS 4#define NUM_OPS 3#define GETSID 1#define GETPC 2#define GETGC 3DWORD dwIterations=10;DWORD dwSleepTime=0;CServer serverInst;VOID DisplayUsage(){ printf ("stressclient.exe /t:<num_threads> /i:<iterations_per_thread> /s:<sleep_time_per_iteration>\n");}DWORD WINAPI ThreadWorker(LPVOID lpParameter){ srand ( (unsigned int)time ( NULL ) ) ; for(DWORD i=0;i<dwIterations;i++) { // // Randomly pick server operation to run against... // DWORD dwOperation = static_cast<DWORD> ( NUM_OPS * rand() / (RAND_MAX + 1.0 ) ) ; if(dwOperation==GETSID) { PSID pSid=serverInst.GetSID(); if(pSid==NULL) { wprintf(L"Failed to get SID!\n"); } else { wprintf(L"."); delete[] pSid; } } else if(dwOperation==GETPC) { DWORD dwPrivCount=serverInst.GetPrivilegeCount(); if(dwPrivCount==0) { wprintf(L"Failed to get privilege count!\n"); } else { wprintf(L"."); } } else { DWORD dwGroupCount=serverInst.GetGroupCount(); if(dwGroupCount==0) { wprintf(L"Failed to get group count!\n"); } else { wprintf(L"."); } } Sleep(dwSleepTime*1000); } return 0;}void __cdecl wmain (int argc, wchar_t* pArgs[]){ if(argc!=NUM_ARGS) { DisplayUsage(); } wprintf(L"Press any key to start stress application...\n"); _getch(); DWORD dwNumThreads=10; BOOL bFailed=FALSE; for(int i=1;i<NUM_ARGS && bFailed==FALSE;i++) { if(wcslen(pArgs[i])<4) { wprintf(L"Invalid switch: %s\n", pArgs[i]); bFailed=TRUE; break; } if(pArgs[i][0]!='/') { wprintf(L"Invalid switch: %s\n", pArgs[i]); bFailed=TRUE; break; } switch(pArgs[i][1]) { case 't': case 'T': dwNumThreads=_wtoi(pArgs[i]+3); break; case 'i': case 'I': dwIterations=_wtoi(pArgs[i]+3); break; case 's': case 'S': dwSleepTime=_wtoi(pArgs[i]+3); break; default: wprintf(L"Invalid switch: /%c\n", pArgs[i][1]); bFailed=TRUE; break; } } if(bFailed==FALSE) { DWORD dwActualThreadCount=0; HANDLE* pHandles=new HANDLE[dwNumThreads]; if(pHandles!=NULL) { wprintf(L"Running..."); for(DWORD i=0;i<dwNumThreads;i++,dwActualThreadCount++) { pHandles[i]=CreateThread(NULL, 0, ThreadWorker, NULL, 0, NULL ); if(pHandles[i]==NULL) { wprintf(L"Failed to create thread: %d\n", GetLastError()); break; } } if(dwActualThreadCount>0) { DWORD dwRes=WaitForMultipleObjects(dwActualThreadCount, pHandles, TRUE, INFINITE); if(dwRes==WAIT_OBJECT_0) { for(DWORD i=0;i<dwActualThreadCount;i++) { CloseHandle(pHandles[i]); pHandles[i]=NULL; } } else { wprintf(L"Failed to wait for thread completion: %d\n", GetLastError()); } } else { wprintf(L"Unable to start any threads\n"); } delete[] pHandles; } } wprintf(L"\nPress any key to exit stress application...\n"); _getch();}输入不同的参数发现资源可能有泄漏的情况,但是process explorer并没有提供更有效的线索:
可以通过windbg的htrace命令来查找句柄泄漏情况:
1.启动程序并打开句柄堆栈
2.运行并让泄漏发生
3.对比未关闭句柄堆栈确定哪些句柄发生泄漏了
检视代码发现程序中GetSid函数获取了token但并为释放,定位到问题。
简单的内存泄漏
如下实例:
#ifndef __SRV_H#define __SRV_H#include <windows.h>class CServer{public: CServer(){}; ~CServer(){}; PSID GetSID(); DWORD GetPrivilegeCount(); DWORD GetGroupCount(); private: HANDLE GetToken();} ;#endif
#include <windows.h>#include <stdio.h>#include "srv.h"PSID CServer::GetSID(){ PSID pSid = NULL; HANDLE hToken = INVALID_HANDLE_VALUE; hToken = GetToken(); if(hToken!=INVALID_HANDLE_VALUE) { DWORD dwNeeded=0; BOOL bRes=GetTokenInformation(hToken, TokenUser, NULL, 0, &dwNeeded ); if(bRes==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_USER* pBuffer=(TOKEN_USER*) new BYTE[dwNeeded]; if(pBuffer!=NULL) { BOOL bRes=GetTokenInformation(hToken, TokenUser, (LPVOID)pBuffer, dwNeeded, &dwNeeded ); if(bRes==TRUE) { DWORD dwSidLen=GetLengthSid(pBuffer->User.Sid); pSid=(PSID) new BYTE[dwSidLen]; if(pSid!=NULL) { if(CopySid(dwSidLen, pSid, pBuffer->User.Sid)==FALSE) { delete[] pSid; pSid=NULL; } } } delete[] pBuffer; } } CloseHandle(hToken); } return pSid;}DWORD CServer::GetPrivilegeCount(){ DWORD dwPrivCount=0; HANDLE hToken = INVALID_HANDLE_VALUE; hToken = GetToken(); if(hToken!=INVALID_HANDLE_VALUE) { DWORD dwNeeded=0; BOOL bRes=GetTokenInformation(hToken, TokenStatistics, NULL, 0, &dwNeeded ); if(bRes==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_STATISTICS* pBuffer=(TOKEN_STATISTICS*) new BYTE[dwNeeded]; if(pBuffer!=NULL) { BOOL bRes=GetTokenInformation(hToken, TokenStatistics, (LPVOID)pBuffer, dwNeeded, &dwNeeded ); if(bRes==TRUE) { dwPrivCount=pBuffer->PrivilegeCount; } } delete[] pBuffer; } CloseHandle(hToken); } return dwPrivCount;}DWORD CServer::GetGroupCount(){ DWORD dwGroupCount=0; HANDLE hToken = INVALID_HANDLE_VALUE; hToken = GetToken(); if(hToken!=INVALID_HANDLE_VALUE) { DWORD dwNeeded=0; BOOL bRes=GetTokenInformation(hToken, TokenStatistics, NULL, 0, &dwNeeded ); if(bRes==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { TOKEN_STATISTICS* pBuffer=(TOKEN_STATISTICS*) new BYTE[dwNeeded]; if(pBuffer!=NULL) { BOOL bRes=GetTokenInformation(hToken, TokenStatistics, (LPVOID)pBuffer, dwNeeded, &dwNeeded ); if(bRes==TRUE) { dwGroupCount=pBuffer->GroupCount; } } delete[] pBuffer; } CloseHandle(hToken); } return dwGroupCount;}HANDLE CServer::GetToken(){ HANDLE hToken = INVALID_HANDLE_VALUE; BOOL bRet = OpenThreadToken (GetCurrentThread(), TOKEN_READ, TRUE, &hToken ); if(bRet==FALSE) { bRet=OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken ); } return hToken;}
#include <windows.h>#include "srv.h"#include <stdio.h>#include <stdlib.h>#include <conio.h>#include <time.h>#define NUM_ARGS 4#define NUM_OPS 3#define GETSID 1#define GETPC 2#define GETGC 3DWORD dwIterations=10;DWORD dwSleepTime=0;CServer serverInst;VOID DisplayUsage(){ wprintf (L"stressclient.exe /t:<num_threads> /i:<iterations_per_thread> /s:<sleep_time_per_iteration>\n");}DWORD WINAPI ThreadWorker(LPVOID lpParameter){ srand ( (unsigned int)time ( NULL ) ) ; for(DWORD i=0;i<dwIterations;i++) { // // Randomly pick server operation to run against... // DWORD dwOperation = static_cast<DWORD> ( NUM_OPS * rand() / (RAND_MAX + 1.0 ) ) ; if(dwOperation==GETSID) { PSID pSid=serverInst.GetSID(); if(pSid==NULL) { wprintf(L"Failed to get SID!\n"); } else { wprintf(L"."); } } else if(dwOperation==GETPC) { DWORD dwPrivCount=serverInst.GetPrivilegeCount(); if(dwPrivCount==0) { wprintf(L"Failed to get privilege count!\n"); } else { wprintf(L"."); } } else { DWORD dwGroupCount=serverInst.GetGroupCount(); if(dwGroupCount==0) { wprintf(L"Failed to get group count!\n"); } else { wprintf(L"."); } } Sleep(dwSleepTime*1000); } return 0;}void __cdecl wmain (int argc, wchar_t* pArgs[]){ if(argc!=NUM_ARGS) { DisplayUsage(); return; } wprintf(L"Press any key to start stress application...\n"); _getch(); DWORD dwNumThreads=10; BOOL bFailed=FALSE; for(int i=1;i<NUM_ARGS && bFailed==FALSE;i++) { if(wcslen(pArgs[i])<4) { wprintf(L"Invalid switch: %s\n", pArgs[i]); bFailed=TRUE; break; } if(pArgs[i][0]!='/') { wprintf(L"Invalid switch: %s\n", pArgs[i]); bFailed=TRUE; break; } switch(pArgs[i][1]) { case 't': case 'T': dwNumThreads=_wtoi(pArgs[i]+3); break; case 'i': case 'I': dwIterations=_wtoi(pArgs[i]+3); break; case 's': case 'S': dwSleepTime=_wtoi(pArgs[i]+3); break; default: wprintf(L"Invalid switch: /%c\n", pArgs[i][1]); bFailed=TRUE; break; } } if(bFailed==FALSE) { DWORD dwActualThreadCount=0; HANDLE* pHandles=new HANDLE[dwNumThreads]; if(pHandles!=NULL) { wprintf(L"Running..."); for(DWORD i=0;i<dwNumThreads;i++,dwActualThreadCount++) { pHandles[i]=CreateThread(NULL, 0, ThreadWorker, NULL, 0, NULL ); if(pHandles[i]==NULL) { wprintf(L"Failed to create thread: %d\n", GetLastError()); break; } } if(dwActualThreadCount>0) { DWORD dwRes=WaitForMultipleObjects(dwActualThreadCount, pHandles, TRUE, INFINITE); if(dwRes==WAIT_OBJECT_0) { for(DWORD i=0;i<dwActualThreadCount;i++) { CloseHandle(pHandles[i]); pHandles[i]=NULL; } } else { wprintf(L"Failed to wait for thread completion: %d\n", GetLastError()); } } else { wprintf(L"Unable to start any threads\n"); } delete[] pHandles; } } wprintf(L"\nPress any key to exit stress application...\n"); _getch();}通过输入不同的参数观察内存情况,可以简单的判断存在内存泄漏的情况:
使用LeakDiag来查看发生内存泄漏的栈,来定位问题:
若设置了正确的符号文件可以直接找到代码的行数,没有也不要紧有addr和offset也可以通过反编译的方式找到。
当然还有其他的内存监视工具pageheap,windbg的!heap -l命令等等。
0 0
- 资源泄漏的调试
- 用Windbg调试.NET程序的资源泄漏
- CreateProcess导致的资源泄漏
- 调试内存泄漏的方法
- kernel内存泄漏的调试
- c# IDataReader造成的资源泄漏
- String变量引起的Loadrunner资源泄漏
- 避免异常发生时的资源泄漏
- 分析、调试内存泄漏的应用程序
- Android的内存泄漏和调试
- 一次调试内存泄漏的过程
- Android的内存泄漏和调试
- Android的内存泄漏和调试
- Android的内存泄漏和调试
- Android的内存泄漏和调试
- Android的内存泄漏和调试
- Android的内存泄漏和调试
- 【VS开发】关于内存泄漏的调试
- 动态规划练习一 06:登山
- 关于如何阅读源码
- quartz使用的初步以及持久化任务
- Jdbc中事务的保存点解析
- Could not install the app on the device, read the error above for details. Make sure you have an And
- 资源泄漏的调试
- Windows 快捷键
- set集合的实现和性能
- Spring框架总结:
- idea启动项目address localhost:1099 is already in use异常解决
- Spring学习笔记-组件扫描
- 微信JSSDK分享接口,wechat,share ,onMenuShareAppMessage wx.onMenuShareTimeline
- 利用静态内部类实现的单例模式
- Bundle源码解析