crashrpt

I was going to google today for articles dealing with SEH so that I can generate a MiniDump file when any thread throws SEH without wrapping the code with a __try{}__except() block in each thread . However, the final result was that I didn't find a few articles dealing with SEH, but luckily I found a tool crashrpt that met my needs.

crashrpt is a tool that includes the ability to generate program error reports when various types of unhandled exceptions occur in the program, then send the report to the developer in a specified way (such as HTTP or SMTP), and finally analyze the information. crashrpt consists of 3 parts, the error report generation library CrashRpt, we need to use this library in our own program to capture exceptions that our program does not handle, after the library captures these unhandled exceptions, CrashRpt will generate MiniDump files, It will be packaged into a bug report along with the information you specify using the library (such as log files and screenshots, etc.). The CrashRpt library supports handling all kinds of exceptions thrown by all Windows C/C++ programs I know, such as the SEH I mentioned earlier, and it can also catch C++ exceptions, signals, and errors when calling functions in various CRT libraries. . The exception information sending tool CrashSender, which can send the generated error report to us according to the method we specified (HTTP, SMTP or MAPI) according to the method we set using CrashRpt. The automatic exception information processing tool crprober, which can receive the error report sent to us by CrashSender in the background, and output the exception information of the program in the form of text after analyzing the error report. For a more detailed introduction to crashrpt, you can refer to https://code.google.com/p/crashrpt/ and http://crashrpt.sourceforge.net/docs/html/getting_started.html .

In order to use crashrpt, we first need to download the latest version of crashrpt at https://code.google.com/p/crashrpt , the latest version at the time of this writing is 1.3.0. The downloaded and decompressed directory is as shown below:

The bin directory contains all crashrpt-related libraries and programs compiled with vc10, and the include and lib directories contain the header files and lib files required for development. If you don't mind that the program is released with the vc10 runtime library, or your program is developed with vc10, you can use these compiled binaries directly. If you want crashrpt and your program to depend on the same vc runtime library, then you need to recompile crashrpt with your vc. For friends who use other vc versions except vc10, if you want to compile crashrpt, you need to use the open source cross-compilation tool cmake, we need to use cmake to generate the same solution and project file as your vc version. First download and install the latest version of cmake for your system at http://cmake.org/cmake/resources/software.html . After the installation is complete, run cmake-gui, enter the top-level directory of crashrpt in the where is the source code text box and where to build the binaries text box, which is the directory containing the file CMakeLists.txt, then click the Configure button, and then pop up In the dialog box, select your vc version and click finish, and the output as shown in the figure appears:


Select the option that matches your vc version in the list box, and finally click the Gnerate button to generate a solution and project file that matches your vc version. Use vc to open the generated solution file, the vc version I use here is vc9, and the solution has the project shown in the following figure:


Click build in the right-click menu at the solution to generate crashrpt.

Let's take a look at how to use the crashrpt library to generate error reports. First, we need to declare a CR_INSTALgL_INFO structure, and then set it according to our own needs, we can use the crInstall function to install the exception handling function in crashrpt into the program. After calling this function, if an uncaught exception occurs in the program, crashrpt will capture the exception and generate a MiniDump file. For a detailed description of the CR_INSTALgL_INFO structure, please refer to http://crashrpt.sourceforge.net/docs/html/ struct_c_r___i_n_s_t_a_l_l___i_n_f_o_a.html . In addition to the MiniDump file, we can also add the specified file to the error report by calling the crAddFile2 function. For example, we can add program-related log files to the error report, so that we can better analyze the internal state of the program, Then use this information to find the cause of the program error faster; in addition to adding the specified file, we can also add screenshots by calling the crAddScreenshot function, so that when the program crashes, we can include the screenshot at that time in the error report In; sometimes the hardware running the program may also be the cause of the program crash, we can add custom information to the xml description file included in the error report generated by crashrpt by calling the crAddProperty function; finally, we can also call crAddRegKey Function to include information about the registry into the error report; remember to call before the program ends The crUninstall function cleans up related resources used by crashrpt. For a more detailed introduction to the use of crashrpt, please refer to http://crashrpt.sourceforge.net/docs/html/using_crashrpt_api.html

   Now let's look at an example MyApp using the crashrpt library, the program MyApp has 2 threads, the main thread is responsible for interacting with the user, and another worker thread is responsible for some operations that take a lot of time to complete. The program will also create a log file, which we can use the crashrpt library to package and send to us together with the MiniDump file when the program crashes, so that we can better analyze the cause of the program crash. The code of the program looks like this:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "CrashRpt.h" <span style="font-family: monospace, fixed;">// 包含crashrpt库使用所需要的头文件 </span>

FILE* g_hLog = NULL; // 日志文件句柄
// 程序崩溃时由crashrpt调用的回调函数
BOOL WINAPI CrashCallback(LPVOID /*lpvState*/)
{  
  // 需要在这里关闭日志文件句柄,否则crashrpt无法对处于占用状态的文件进行操作
  if(g_hLog!=NULL)
  {
    fclose(g_hLog);
    g_hLog = NULL;
  }
  // 返回TRUE, 由crashrpt生成错误报告
  return TRUE;
}

// 日志函数
void log_write(LPCTSTR szFormat, ...)
{
  if (g_hLog == NULL) 
    return;
  va_list args; 
  va_start(args); 
  _vftprintf_s(g_hLog, szFormat, args);
  fflush(g_hLog);
}
// 线程处理函数
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
  // 在该线程中安装crashrpt库对未处理异常的处理

  crInstallToCurrentThread2(0);
  log_write(_T("Entering the thread proc\n"));
  for(;;)
  {
    // 在这里模拟一处内存越界
    int* p = NULL;
    *p = 13;
  }    
   
  log_write(_T("Leaving the thread proc\n"));
  // 清理crashrpt资源
  crUninstallFromCurrentThread();   
  return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{  
  // 设置crashrpt的各项参数
  CR_INSTALL_INFO info;  
  memset(&info, 0, sizeof(CR_INSTALL_INFO));  
  info.cb = sizeof(CR_INSTALL_INFO);    
  info.pszAppName = _T("MyApp");  
  info.pszAppVersion = _T("1.0.0");  
  info.pszEmailSubject = _T("MyApp 1.0.0 Error Report");  
  info.pszEmailTo = _T("[email protected]");    
  info.pszUrl = _T("http://myapp.com/tools/crashrpt.php");  
  info.pfnCrashCallback = CrashCallback;   
  info.uPriorities[CR_HTTP] = 3;  // 首先使用HTTP的方式发送错误报告
  info.uPriorities[CR_SMTP] = 2;  // 然后使用SMTP的方式发送错误报告  
  info.uPriorities[CR_SMAPI] = 1; //最后尝试使用SMAPI的方式发送错误报告    
  // 捕获所有能够捕获的异常, 使用HTTP二进制编码的方式传输
  info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;
  info.dwFlags |= CR_INST_HTTP_BINARY_ENCODING; 
  info.dwFlags |= CR_INST_APP_RESTART; 
  info.dwFlags |= CR_INST_SEND_QUEUED_REPORTS; 
  info.pszRestartCmdLine = _T("/restart");
  // 隐私策略URL
  info.pszPrivacyPolicyURL = _T("http://myapp.com/privacypolicy.html"); 
  
  int nResult = crInstall(&info);    
  if(nResult!=0)  
  {    
    TCHAR szErrorMsg[512] = _T("");        
    crGetLastErrorMsg(szErrorMsg, 512);    
    _tprintf_s(_T("%s\n"), szErrorMsg);    
    return 1;
  }
  // 添加日志文件到错误报告中
  crAddFile2(_T("log.txt"), NULL, _T("Log File"), CR_AF_MAKE_FILE_COPY);   
  // 添加程序崩溃时的截屏到错误报告中
  crAddScreenshot(CR_AS_VIRTUAL_SCREEN);  
  // 添加任意的信息到错误报告中,这里以显卡信息作为示例
  crAddProperty(_T("VideoCard"), _T("nVidia GeForce 8600 GTS"));
  errno_t err = _tfopen_s(&g_hLog, _T("log.txt"), _T("wt"));
  if(err!=0 || g_hLog==NULL)
  {
    _tprintf_s(_T("Error opening log.txt\n"));
    return 1; // Couldn't open log file
  }
  log_write(_T("Started successfully\n"));
  HANDLE hWorkingThread = CreateThread(NULL, 0, 
           ThreadProc, (LPVOID)NULL, 0, NULL);
  log_write(_T("Created working thread\n"));
  TCHAR* szFormatString = NULL;
  _tprintf_s(szFormatString);
  WaitForSingleObject(hWorkingThread, INFINITE);
  log_write(_T("Working thread has exited\n"));
  if(g_hLog!=NULL)
  {
    fclose(g_hLog);
    g_hLog = NULL;
  }
  crUninstall();
  return 0;
}

  crInstallToCurrentThread2(0);

  log_write(_T("Entering the thread proc\n"));

  for(;;)
  {
    // 在这里模拟一处内存越界
    int* p = NULL;
    *p = 13;
  }    
   
  log_write(_T("Leaving the thread proc\n"));

  // 清理crashrpt资源
  crUninstallFromCurrentThread();    

  return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{  
  // 设置crashrpt的各项参数
  CR_INSTALL_INFO info;  
  memset(&info, 0, sizeof(CR_INSTALL_INFO));  
  info.cb = sizeof(CR_INSTALL_INFO);    
  info.pszAppName = _T("MyApp");  
  info.pszAppVersion = _T("1.0.0");  
  info.pszEmailSubject = _T("MyApp 1.0.0 Error Report");  
  info.pszEmailTo = _T("[email protected]");    
  info.pszUrl = _T("http://myapp.com/tools/crashrpt.php");  
  info.pfnCrashCallback = CrashCallback;   
  info.uPriorities[CR_HTTP] = 3;  // 首先使用HTTP的方式发送错误报告
  info.uPriorities[CR_SMTP] = 2;  // 然后使用SMTP的方式发送错误报告  
  info.uPriorities[CR_SMAPI] = 1; //最后尝试使用SMAPI的方式发送错误报告    
  // 捕获所有能够捕获的异常, 使用HTTP二进制编码的方式传输
  info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;
  info.dwFlags |= CR_INST_HTTP_BINARY_ENCODING; 
  info.dwFlags |= CR_INST_APP_RESTART; 
  info.dwFlags |= CR_INST_SEND_QUEUED_REPORTS; 
  info.pszRestartCmdLine = _T("/restart");
  // 隐私策略URL
  info.pszPrivacyPolicyURL = _T("http://myapp.com/privacypolicy.html"); 
  
  int nResult = crInstall(&info);    
  if(nResult!=0)  
  {    
    TCHAR szErrorMsg[512] = _T("");        
    crGetLastErrorMsg(szErrorMsg, 512);    
    _tprintf_s(_T("%s\n"), szErrorMsg);    
    return 1;
  } 

  // 添加日志文件到错误报告中
  crAddFile2(_T("log.txt"), NULL, _T("Log File"), CR_AF_MAKE_FILE_COPY);    

  // 添加程序崩溃时的截屏到错误报告中
  crAddScreenshot(CR_AS_VIRTUAL_SCREEN);   

  // 添加任意的信息到错误报告中,这里以显卡信息作为示例
  crAddProperty(_T("VideoCard"), _T("nVidia GeForce 8600 GTS"));

  errno_t err = _tfopen_s(&g_hLog, _T("log.txt"), _T("wt"));
  if(err!=0 || g_hLog==NULL)
  {
    _tprintf_s(_T("Error opening log.txt\n"));
    return 1; // Couldn't open log file
  }

  log_write(_T("Started successfully\n"));

  HANDLE hWorkingThread = CreateThread(NULL, 0, 
           ThreadProc, (LPVOID)NULL, 0, NULL);

  log_write(_T("Created working thread\n"));

  TCHAR* szFormatString = NULL;
  _tprintf_s(szFormatString);

  WaitForSingleObject(hWorkingThread, INFINITE);

  log_write(_T("Working thread has exited\n"));

  if(g_hLog!=NULL)
  {
    fclose(g_hLog);
    g_hLog = NULL;
  }

  crUninstall();

  return 0;
}

There are a few things to note in this sample program:

1. If you want to include the log file in the error report, remember to use the CrashCallBack function similar to the example to set the pfnCrashCallback field in CR_INSTALL_INFO, and close the handle to the log file in the function.

2. According to my experience, it is not necessary to use the pair of functions crInstallToCurrentThread2/ crUninstallFromCurrentThread to install the exception handling process in the thread, as long as crInstall is called in the main thread. You can catch unhandled exceptions in all threads in the program.

3. The reason for the error when calling crInstall is generally that CrashRptXXXX.dll, CrashSenderXXXX.exe and crashrpt_lang.ini are not placed in the correct path. By default, this path is the same path as the application. Where XXXX refers to the version number of crashrpt, the version number in this article is 1300.

For the original introduction of this example, please refer to http://crashrpt.sourceforge.net/docs/html/simple_example.html

Finally, I will post a code block where I use crashrpt. The purpose of my use is to hand the program to the tester for testing. If the program crashes, crashrpt will save the program's error report to the local. After the tester finds that the program crashes, the report will be Send it to me for debugging. The code looks like this:

 

int main(int argc, char **argv)
{
#if defined(WIN32) && defined(USE_CRASHRPT)
	CR_INSTALL_INFO info = {0};
	info.cb = sizeof(CR_INSTALL_INFO);
	info.pszAppName = TEXT("xxx");
	info.pszAppVersion = TEXT("0.1.0");   
	info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS;
	info.dwFlags |= CR_INST_DONT_SEND_REPORT;
	info.pszErrorReportSaveDir = TEXT("./xxx");
	if (EXIT_SUCCESS != crInstall(&info))
	{
		TCHAR errorMsg[512];
		crGetLastErrorMsg(errorMsg, 512);
		std::cerr << errorMsg;
		return EXIT_FAILURE;
	}
#endif

   int ret = mainImpl(argc, argv);

#if defined(WIN32) && defined(USE_CRASHRPT)
   crUninstall();
#endif

   return ret;
}


    crashrpt is a powerful error report generation, sending and analysis tool. Since my use is relatively simple, what I will introduce here is only a small part of the crashrpt function. According to the description in the crashrpt documentation, crashrpt can fully link with the bug management system we use when sending error reports using http. I I think this can greatly improve the modification efficiency of BUG. If you are interested in crashrpt, you can refer to http://crashrpt.sourceforge.net/docs/html/index.html for more in-depth study.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325454075&siteId=291194637