客户端程序无响应,卡死,卡顿了?抓个dump看下卡在哪里了。

客户端程序无响应,卡死,卡顿了?抓个dump看下卡在哪里了。

原文链接

工具下载链接

为什么会无响应

windows的客户端程序一般都有一个主线程来处理消息,一般都在winmain函数中实现。主线程根据不同的消息做不同的任务,可以打开一个新的对话框,创建一个新线程等。用户的任何点击(鼠标),滚动(滚动条)操作,都会封装成一个消息,最终传给这个线程来处理。如果,这个线程在某个消息响应中,调用了一个阻塞或很耗时的函数,那么就没法及时获取并处理下一个消息了(因为还在那个函数里耗着呢),这个时候表现出来的就是无响应

如果在程序无响应时,抓一个dump,用windbg打印出堆栈来,卡在哪里会一目了然。

然而,无响应通常时转瞬即逝的。测试同学在测试时,如果出现了无响应,会叫你过去,如果运气好的话,你可以用任务管理器或其他的抓dump的工具生成一个dump;运气不好时,你看到的是程序运行正常。这样的抓dump,抓到了,很麻烦;抓不到,很抓狂。

自己写一个自动检测并抓dump工具吧

取个名字:dumphungwindow.exe
想想,工具应满足下面几个条件:

  • 好用,好上手
  • 静静的等待无响应出现,抓dump
  • 可以设定一个dump数量,抓够了就停止

有了上面的条件,工具如何工作基本就定下来了:
工具应该跑在一个无限循环中,每隔一段时间,就获取所有的top most window。遍历这些窗口,用 SendMessageTimeout 给窗口发一个消息,如果超时了(多久超时可以自己设置),就抓dump。如果dump数量够了,就退出循环,结束程序。

源代码

(原文里的代码,命令行的,自己可以写个带界面的,测试同学用起来比较方便)

/********************************************************************************************************************
Warranty Disclaimer
--------------------------
This sample code, utilities, and documentation are provided as is, without warranty of any kind. Microsoft further disclaims all
implied warranties including without limitation any implied warranties of merchantability or of fitness for a particular  purpose.
The entire risk arising out of the use or performance of the product and documentation remains with you.

In no event shall Microsoft be liable for any damages whatsoever  (including, without limitation, damages for loss of business
profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to
use the sample code, utilities, or documentation, even if  Microsoft has been advised of the possibility of such damages.
Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above
limitation may not apply to you.

********************************************************************************************************************/

#include <stdio.h>
#include <windows.h>
#include <dbghelp.h>
#include <psapi.h>

// don't warn about old school strcpy etc.
#pragma warning( disable : 4996 )

int iMaxDump=5;
int iDumpsTaken=0;
int iHangTime=5000;
int iDumpPause=1;
int iScanRate=5000;
HANDLE hEventLog;
char * szDumpLocation;
int FindHungWindows(void);
char * szDumpFileName = 0;
char * szEventInfo = 0;
char * szDumpFinalTarget = 0;
char * szModName = 0;
char * szAppname = 0;
DWORD dwExecOnHang = 0;

#define MAXDUMPFILENAME 1000
#define MAXEVENTINFO 5000
#define MAXDUMPFINALTARGET 2000
#define MAXDUMPLOCATION 1000
#define MAXAPPPATH 1000
#define MAXMODFILENAME 500
#define HMODSIZE 255

int main(int argc, char * argv[])
{
      int i;
      int z;
      size_t j;
      char scan;

      // check to make sure we have dbghelp.dll on the machine.
      if(!LoadLibrary("dbghelp.dll"))
      {
            printf("dbghelp.dll not found please install the debugger tools and place this tool in \r\nthe debugging tools directory or a copy of dbghelp.dll in this tools directory\r\n");
            return 0;
      }

      // Allocate a buffer for our dump location
      szDumpLocation = (char *)malloc(MAXDUMPLOCATION);
      {
            if(!szDumpLocation)
            {
            printf("Failed to alloc buffer for szdumplocation %d",GetLastError());
            return 0;
            }
      }

      szAppname = (char *)malloc(MAXAPPPATH);
      {
            if(!szAppname)
            {
            printf("Failed to alloc buffer for szAppname  %d",GetLastError());
            return 0;
            }
      }

      // We use temp path because if we are running under terminal server sessions we want the dump to go to each
      // users secure location, ie. there private temp dir. 
      GetTempPath(MAXDUMPLOCATION, szDumpLocation );

      for (z=0;z<argc;z++)
      {
            switch(argv[z][1])
            {
            case '?':
                  {
                  printf("\n This sample application shows you how to use the debugger \r\n help api to dump an application if it stop responding.\r\n\r\n");
                  printf("\n This tool depends on dbghelp.dll, this comes with the Microsoft debugger tools on www.microsoft.com");
                  printf("\n Please make sure you have the debugger tools installed before running this tool.");
                  printf("\n This tool is based on sample source code and is provided as is without warranty.");
                  printf("\n feel free to contact jeffda@microsoft.com to provide feedback on this sample application\r\n\r\n");
                  printf(" /m[Number] Default is 5 dumps\r\n The max number of dumps to take of hung windows before exiting.\r\n\r\n");
                  printf(" /t[Seconds]  Default is 5 seconds\r\n The number of seconds a window must hang before dumping it. \r\n\r\n");
                  printf(" /p[Seconds] Default is 0 seconds\r\n The number of seconds to pause when dumping before continuing scan. \r\n\r\n");
                  printf(" /s[Seconds] Default is 5 seconds.\r\n The scan interval in seconds to wait before rescanning all windows.\r\n\r\n");
                  printf(" /d[DUMP_FILE_PATH] The default is the SystemRoot folder\r\n The path or location to place the dump files.  \r\n\r\n");
                  printf(" /e[EXECUTABLE NAME] This allows you to start another program if an application hangs\r\n\r\n");

                  return 0;
                  }
            case 'm':
            case 'M':
                  {
                        iMaxDump = atoi(&argv[z][2]);
                        break;
                  }
            case 't':
            case 'T':
                  {
                        iHangTime= atoi(&argv[z][2]);
                        iHangTime*=1000;
                        break;
                  }
            case 'p':
            case 'P':
                  {
                        iDumpPause= atoi(&argv[z][2]);
                        iDumpPause*=1000;
                        break;           
                  }
            case 's':
            case 'S':
                  {
                        iScanRate = atoi(&argv[z][2]);
                        iScanRate*=1000;             
                        break;
                  }
            case 'd':
            case 'D':
                  { // Dump file directory path
                        strcpy(szDumpLocation,&argv[z][2]);
                        j = strlen(szDumpLocation);

                        if (szDumpLocation[j-1]!='\\')
                        {
                              szDumpLocation[j]='\\';
                              szDumpLocation[j+1]=NULL;
                        }
                        break;
                  }
            case 'e':
            case 'E':
                  { // applicaiton path to exec if hang happens
                        strcpy(szAppname,&argv[z][2]);
                        dwExecOnHang = 1;
                        break;
                  }
            }
      }


      printf("Dumps will be saved in %s\r\n",szDumpLocation);
      puts("scanning for hung windows\n");

      hEventLog = OpenEventLog(NULL, "HungWindowDump");

      i=0;
      scan='*';
      while(1)
      {
            if(i>20)
            {
                  if ('*'==scan)
                  {
                  scan='.';
            }
                  else
                  {
                  scan='*';
            }
                  printf("\r");
            i=0;
            }
            i++;
            putchar(scan);
            if(!FindHungWindows())
            {
                  return 0;
            }
            if (iMaxDump == iDumpsTaken)
            {
                  printf("\r\n%d Dumps taken, exiting\r\n",iDumpsTaken);
                  return 0;
            }
            Sleep(iScanRate);
      }

      free(szDumpLocation);
      return 0;
}

int FindHungWindows(void)
{
DWORD dwResult = 0;
DWORD ProcessId = 0;
DWORD tid = 0;
DWORD dwEventInfoSize = 0;

// Handles
HWND hwnd = 0;
HANDLE hDumpFile = 0;
HANDLE hProcess = 0;
HRESULT hdDump = 0;

SYSTEMTIME SystemTime;
MINIDUMP_TYPE dumptype = (MINIDUMP_TYPE) (MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData);

// These buffers are presistant.

// security stuff to report the SID of the dumper to the event log.
PTOKEN_USER pInstTokenUser;
HANDLE ProcessToken;
TOKEN_INFORMATION_CLASS TokenInformationClass = TokenUser;
DWORD ReturnLength =0;

// This allows us to get the first window in the chain of top windows.
hwnd = GetTopWindow(NULL);
if(!hwnd)
{
      printf("Could not GetTopWindow\r\n");
      return 0;
}

// We will iterate through all windows until we get to the end of the list.
while(hwnd)
{
      // Get the process ID for the current window   
      tid = GetWindowThreadProcessId(hwnd, &ProcessId);

      // Sent a message to this window with our timeout. 
      // If it times out we consider the window hung
      if (!SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_BLOCK, iHangTime, &dwResult))
      {
            // SentMessageTimeout can fail for other reasons, 
            // if it's not a timeout we exit try again later
            if(ERROR_TIMEOUT != GetLastError())
            {
                  printf("SendMessageTimeout has failed with error %d\r\n",GetLastError());
                  return 1;
            }
                  // Iint our static buffers points.
                  // On our first trip through if we have not
                  // malloced memory for our buffers do so now.
                  if(!szModName)
                  {
                        szModName = (char *)malloc(MAXMODFILENAME);
                        {
                              if(!szModName)
                              {
                              printf("Failed to alloc buffer for szModName %d",GetLastError());
                              return 0;
                              }
                        }
                  }
                  if(!szDumpFileName)// first time through malloc a buffer.
                  {
                        szDumpFileName = (char *)malloc(MAXDUMPFINALTARGET);
                        {
                              if(!szDumpFileName)
                              {
                                    printf("Failed to alloc buffer for dumpfilename %d",GetLastError());
                                    return 0;
                              }
                        }
                  }
                  if(!szDumpFinalTarget)// first time through malloc a buffer.
                  {
                        szDumpFinalTarget= (char *)malloc(MAXDUMPFINALTARGET);
                        {
                              if(!szDumpFinalTarget)
                              {
                              printf("Failed to alloc buffer for dumpfiledirectory %d",GetLastError());
                              return 0;
                              }
                        }
                  }
                  if(!szEventInfo)
                  {
                        szEventInfo= (char *)malloc(MAXEVENTINFO);
                        {
                              if(!szEventInfo)
                              {
                              printf("Failed to alloc buffer for szEventInfo %d",GetLastError());
                              return 0;
                              }
                        }
                  }
                  // End of initial buffer allocations.

            GetLocalTime (&SystemTime);

            // Using the process id we open the process for various tasks.
            hProcess = OpenProcess(PROCESS_ALL_ACCESS,NULL,ProcessId);
            if(!hProcess )
            {
                  printf("Open process of hung window failed with error %d\r\n",GetLastError());
                  return 1;
            }
            // What is the name of the executable?
            GetModuleBaseName( hProcess, NULL, szModName,MAXMODFILENAME);

            printf("\r\n\r\nHung Window found dumping process (%d) %s\n",ProcessId,szModName);

            // Here we build the dump file name time, date, pid and binary name
            sprintf(szDumpFileName,"HWNDDump_Day%d_%d_%d_Time%d_%d_%d_Pid%d_%s.dmp",SystemTime.wMonth,SystemTime.wDay,SystemTime.wYear,SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond,ProcessId,szModName);
            strcpy(szDumpFinalTarget,szDumpLocation);
            strcat(szDumpFinalTarget,szDumpFileName);

            // We have to create the file and then pass it's handle to the dump api
            hDumpFile = CreateFile(szDumpFinalTarget,FILE_ALL_ACCESS,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
            if(!hDumpFile)
            {
                  printf("CreateFile failed to open dump file at location %s, with error %d\r\n",szDumpLocation,GetLastError());
                  return 0;
            }

            printf("Dumping unresponsive process\r\n%s",szDumpFinalTarget);

            // This dump api will halt the target process while it writes it's
            // image to disk in the form a dump file.
            // this can be opened later by windbg or cdb for debugging.
            if(!MiniDumpWriteDump(hProcess,ProcessId,hDumpFile,dumptype ,NULL,NULL,NULL))
            {
                  // We do this on failure
                  hdDump = HRESULT_FROM_WIN32(GetLastError());
                  printf("MiniDumpWriteDump failed with a hresult of %d last error %d\r\n",hdDump,GetLastError());
                  CloseHandle (hDumpFile);
                  return 0;
            }
            else
            {
                  // If we are here the dump worked.  Now we need to notify the machine admin by putting a event in
                  // the application event log so someone knows a dump was taken and where it is stored.
                  sprintf(szEventInfo,"An application hang was caught by findhungwind.exe, the process was dumped to %s",szDumpFinalTarget);

                  // We need to get the process token so we can get the user sit so ReportEvent will have the
                  // User name / account in the event log.
                  if (OpenProcessToken(hProcess,      TOKEN_QUERY,&ProcessToken ) )
                  {
                        // Make the firt call to findout how big the sid needs to be.    
                        GetTokenInformation(ProcessToken,TokenInformationClass, NULL,NULL,&ReturnLength);
                        pInstTokenUser = (PTOKEN_USER) malloc(ReturnLength);
                        if(!pInstTokenUser)
                        {
                              printf("Failed to malloc buffer for InstTokenUser exiting error %d\r\n",GetLastError());
                              return 0;
                        }
                        if(!GetTokenInformation(ProcessToken,TokenInformationClass, (VOID *)pInstTokenUser,ReturnLength,&ReturnLength))
                        {
                              printf("GetTokenInformation failed with error %d\r\n",GetLastError());
                              return 0;
                        }
                  }
                  // write the application event log message. 
                  // This will show up as source DumpHungWindow
                  dwEventInfoSize=(DWORD)strlen(szEventInfo);

                  ReportEvent(hEventLog,EVENTLOG_WARNING_TYPE,1,1,pInstTokenUser->User.Sid,NULL,dwEventInfoSize,NULL,szEventInfo);

                  // Free to token buffer, we don't want to leak anything.
                  free(pInstTokenUser);

                  // In additon to leaking a handle if you don't close the handle
                  // you may not get the dump to flush to the hard drive.
                  CloseHandle (hDumpFile);
                  printf("\r\nDump complete");

                  // This allows you to execute something if you get a hang like crash.exe
                  if (dwExecOnHang)
                  {
                        system(szAppname);
                  }

                  //  The Sleep is here so in the event you want to wait N seconds
                  //  before collecting another dump
                  //  you can pause.  This is helpful if you want to see if any
                  //  forward progress is happening over time

                  Sleep(iDumpPause);
            }
            // Once we are at our threadshold for max dumps
            // we exit so we do not fill up the hard drive.
            iDumpsTaken++;
            if (iMaxDump == iDumpsTaken)
            {
                  return 0;
            }
        }
        // This is where we traverse to the next window.
            hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
      }
      return 1;
}

转载于:https://www.cnblogs.com/ConfuciusPei/p/5118412.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值