MiniDump (Error Dump System)

MiniDump (Error Dump System) is a feature provided by ProudNet that allows you to collect error information directly from the developer when a game client or game server crashes while playing an online game service in order to track the cause and take quick action.

Error information is collected in a *.DMP file, and developers can open the collected dump file in a development tool (such as Visual Studio) to see on which lines of the source file the crash occurred.

Tutorial on building an error dump system

The Building an Error Dump System tutorial included a game server and client.

(1) Install the DbgHelp library

Copy dbghelp.dll to the folder where your OS is installed (C:\Windows\system32) or to the program's current folder. dbghelp.dll is located in (ProudNet installation path)\ProudNet\Sample\Bin.

(2) Setting Visual Studio Compilation Options

For MiniDump (Error Dump System), you need to configure C++ compilation as below.

For Visual Studio 2003, you cannot set it as above, so you need to select 'No' and add /EHa in Configuration Properties -> C/C++ -> Command Line -> Additional Options.

(3) Example of creating a dump server

Responsible for collecting error information dump files sent by the dump client.

#include "../../../include/ProudNetServer.h"
#include "../../../include/DumpServer.h"
#include "MyDumpServer.h"
class CMyDumpServer : public IDumpServerDelegate
{
    ...
    virtual void OnStartServer(CStartServerParameter &refParam) override;
    String GetDumpFilePath(HostID clientEnid, const Proud::AddrPort& clientAddr, CPnTime dumpTime) override;
    void Run();
}
void main(int argc, char* argv[])
{
    CMyDumpServer srv;
    srv.Run();
}

(4) Example of creating a dump client

The dump client is responsible for sending dump files containing error information to the dump server.

If the process crashes, a temporary *.DMP dump file is automatically created and the error level information is attached to the Command Argument as the process runs again.

The error level information received as a command argument must be branched according to its severity, and in the case of MiniDumpAction_AlarmCrash, it is a critical error, so you must write code to send the dump file to the dump server.

(5) Creating a Dump Client for a Game Server

Since game servers typically do not provide a user UI, you will need to write code to send the *.DMP dump file directly to the dump server without popping up an error reporting dialog.

(6) Creating a Dump Client for Game Clients

Game clients typically have a user UI, so you will need to write code to prompt the game user if they want to send the generated *.DMP dump file to the dump server with an error report or skip it before proceeding.

Caution

  • We do not support dump clients on all mobile environments.

  • You must initialize the dump system using Proud.CMiniDumpParameter.

  • For the Unicode programming model, you can define a wide-character version of the main function, where the argv and envp parameters to the wmain function must use the wchar_t* format.

  • For MiniDumpAction_AlarmCrash, MiniDumpAction_DoNothing, you must terminate the program after branching because you can get stuck in an infinite loop by calling return.

#include "stdafx.h"
#include <atlpath.h>
#include "../../include/MiniDumper.h"
#include "../../include/DumpCommon.h"
 
using namespace Proud;
 
const int _MAX_PATH2 = 8192;
#define _COUNTOF(array) (sizeof(array)/sizeof(array[0]))
 
void GetDumpFilePath(LPWSTR output)
{
    WCHAR path[_MAX_PATH2];
    WCHAR drive[_MAX_PATH2];
    WCHAR dir[_MAX_PATH2];
    WCHAR fname[_MAX_PATH2];
    WCHAR ext[_MAX_PATH2];
    WCHAR module_file_name[_MAX_PATH2];
    
    GetModuleFileNameW(NULL, module_file_name, _COUNTOF(module_file_name));
    _tsplitpath_s(module_file_name, drive, _MAX_PATH2, dir, _MAX_PATH2, fname, _MAX_PATH2, ext, _MAX_PATH2);
    _tmakepath_s(path, _MAX_PATH2, drive, dir, L"", L"");
    wsprintf(output, L"%s%s.DMP", path, fname);
};
 
void AccessViolation()
{
    int* a = 0;
    *a = 1;
}
 
// void wmain(int argc, wchar_t* argv[])
int main(int argc, char* argv[])
{
    int nRetCode = 0;
    int *data = 0;
    int menu = 0;
 
    WCHAR dumpFileName[_MAX_PATH2] = { 0, };
    GetDumpFilePath(dumpFileName);
 
    CMiniDumpParameter parameter;
    parameter.m_dumpFileName = dumpFileName;
    parameter.m_miniDumpType = SmallMiniDumpType;
 
    switch (CMiniDumper::Instance().Startup(parameter))
    {
    case MiniDumpAction_AlarmCrash:
        // 오류 발생으로 새로운 프로세스에서 덤프 파일을 생성한 후, 이 값이 return이 됩니다.
        // 생성된 덤프 파일을 메일로 보내거나 에러 창을 보이는 등 유저가 덤프 파일 생성 후, 처리해야할 작업을 처리해주시면 됩니다.
 
        // A dump file is created at a new process due to error occurrence and then this value will be returned.
        // After a user create a dump file, do works that need to be done such as sending a created dump file by email or showing an error window.
 
        // 因出现错误,在新的process中生成转储文件后该值将被返还。
        // 将生成的转储文件以邮件的形式发送,或可以看到 Error对话框的用户生成转存文件后,处理应处理的事即可
 
        // エラー発生により新しいプロセスからダンプファイルを生成した後、この値がreturnされます。
        // 生成されたダンプファイルをメールで送ったり、エラーメッセージが提示されるなどユーザーがダンプファイル生成後、処理すべきの作業をしてください。
        ...
        return nRetCode;
 
    case MiniDumpAction_DoNothing:
        // 유저 호출로 새로운 프로세스에서 덤프 파일을 생성한 후, 이 값이 반환됩니다.
        // 이 경우에는 아무것도 하지 말아야합니다.
    
        // After creating a dump file at a new process by calling a user, this value will be returned.
        // In this case, you should not do anything.
 
        // 因用户呼叫,在新的process中生成转储文件后,该值将被返还。
        // 在这种情况,不要做任何事情。.
 
        // ユーザー呼び出しにより新しいプロセスからダンプファイルを生成した後、この値が返還されます。
        // この場合何もしないでください。
        ...
        return nRetCode;
 
    default:
        // MiniDumpAction_None
        // 일반적으로 앱 실행 시, 이 값이 반환됩니다.
        // 여기서는 일반적으로 처리해야할 일을 처리해주시면 됩니다.
 
        // When executing apps, this value will be returned.
        // In this case, do works that generally need to be done.
 
        // 一般运行App时,该值将被返还。
        //在这里处理一般应处理的事情即可。
 
        // 一般的にアプリ実行後、この値が返還されます。
        // ここでは一般的に処理すべきの事を処理してください。
        ...
        break;
    }
 
    while (1)
    {
        puts("MENU: 1. Access Violation('a')");
        printf("> ");
        
        menu = getchar();
 
        switch (menu)
        {
        case 'a':
            AccessViolation();
            break;
        default:
            break;
        }
    }
 
    return 0;
}

Utilize an error dump system

- Intercepting C Runtime Errors from the Error Dump System

Out-of-range errors in the STL or errors where a purely virtual function is called at runtime are not detected by the error dump system by default.

The error dump system only handles Structured Exceptions, because these errors are digested by the C runtime library. Therefore, you must divert them to Structured Exceptions before they can be digested by the C runtime library to leave an error dump.

Below is how to leave an error dump.

// Pure virtual function called" Bypassing errors to be received by the error dump system.
void myPurecallHandler(void)
{
    printf("In _purecall_handler.");
 
    int* a = 0;
    *a = 1; // Causing a crash. Bypassing the error dump system.
}
 
int main()
{
    /* Bypasses the pure virtual function called error handler to a user-defined function.
    You only need to put it in at the beginning of the program.*/
    _set_purecall_handler(myPurecallHandler);
    ...
}
// Bypassing "out of the range errors" in the STL to be received by the error dump system.
// Caution!!! The _CrtSetReportHook can enter the function you set using ATLTRACE, so retportType == _CRT_WARN should be ignored.
 
int YourReportHook(int reportType, char *message, int *returnValue)
{
    //Ignore _CRT_WARN or 0.
    if (reprotType != _CRT_WARN)
    {
        int* a = 0;
        *a = 1; // Causing a crash. Bypassing the error dump system.
    }
 
    return 1;
}
 
int main()
{
    /* Bypasses the handler of a C runtime library error to a user-defined function.
    You only need to put it in at the beginning of the program.*/
    _CrtSetReportHook(YourReportHook);
 
    std::vector<int> a;
    a[1] = 0; // Test if an error handler has been bypassed
 
}

- Uninterrupted error dump system(Exception Logger)

ProudNet provides a feature that, when an error occurs in a program, continues to log the location of the error without terminating the program. This is called a uninterrupted error dump system(Exception Logger).

A typical game server will immediately dump the situation and restart the program when it crashes, but in some cases it may be necessary to force the game server to stay up and running without restarting the program.

However, it is dangerous to continue running a server program that has already corrupted its memory state.

Checklist for creating a uninterrupted error dump system

  • You need to install the DbgHelp library (dbghelp.dll).

  • You need to set the C++ Exception compilation option in Visual Studio.

  • Include : DumpCommon.h, MiniDumper.h

  • Link : ProudNetCommon.lib

Production examples

Make sure to initialize the CExceptionLogger instance by calling the CExceptionLogger::Instance().Init() function at the Main Entry Point of the program. Define the path to the dump file by inheriting from the IExceptionLoggerDelegate abstract class and overriding the GetDumpDirectory() member. If it returns a blank (""), the dumpfile is saved in the current folder.

Works on Windows XP and Windows 2003 Server or later versions of the operating system. If you mix the CExceptionLogger class with ATLTRACE() or OutputDebugString(), the load of log entries can degrade program performance.

The source files for this example are located in <install folder>\Sample\SimpleExceptionLogger.

The SimpleExceptionLogger example is not supported since version 1.7.33863.

#include "stdafx.h"
#include <atlpath.h>
#include "../../include/DumpCommon.h"
#include "../../include/MiniDumper.h"
 
using namespace Proud;
 
class CIExceptionLoggerDelegate : public IExceptionLoggerDelegate
{
    public:
    virtual String GetDumpDirectory()
    {
        return L"";
    }
};
 
CIExceptionLoggerDelegate g_loggerInfo;
 
void AccessViolation()
{
    try
    {
        int* a = 0;
 
        // 이 루틴은 크래쉬를 의도적으로 발생시킵니다.
        // This routine incurs crash on purpose.
        // 该例程将会故意造成崩溃。
        // このルーティンはクラッシュを意図的に発生させます
        *a = 1;
    }
    catch (...) // catch(...) syntax itself is the usable C++ keyword!
    {
        // 위 try 구문에 의해 크래쉬가 발생할 경우
        // 프로그램이 종료되지 않고 여기로 실행 지점이 오게 됩니다.
        // 한편 exception logger에 의해 오류 로그가 파일로 남게 됩니다.
        // When crash occurs by the above try syntax,
        // the execution point moves to here without terminating the program.
        // At the same time, exception logger leaves an error log file.
    }
}
 
void main(int argc, char* argv[])
{
    int menu = 0;
    CExceptionLogger::Instance().Init(&g_loggerInfo);
 
    while (1)
    {
        puts("MENU: 1. Access Violation('a')");
        printf("> ");
 
        menu = getchar();
 
        switch (menu)
        {
        case 'a':
            AccessViolation();
            break;
        default:
            break;
        }
    }
}

- Leave a dump file of the current state of the process

It has the ability to leave a dump file of the current state of the process, even if it is not in an error situation, which can be opened along with the debug information file (.pdb) that was generated when the program that it dumped was built, to see what the process was doing at the source level.

This feature allows you to leave a dump of the current state of the process, even in difficult debugging environments. Calling Proud.CMiniDumper.WriteDumpFromHere saves the call stack of all threads in the process at the time of the call to a dump file.

Last updated