Jump to content

Dump Generator for Windows Server


Recommended Posts

  • Active Member

M2 Download Center

This is the hidden content, please
( Internal )

Hello, I want share a simple addon for more effective debugging crashes and exceptions on Windows.

Technical details about mini dump: https://docs.microsoft.com/en-us/windows/desktop/debug/minidump-files

 

winminidump.c

#define __LIBTHECORE__
#include "stdafx.h"
#include "winminidump.h"

#ifdef __WIN32__
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")

// Custom minidump callback 
BOOL CALLBACK MiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
	BOOL bRet = FALSE;

	if (!pInput || !pOutput)
		return FALSE;

	switch (pInput->CallbackType)
	{
		case IncludeModuleCallback:
		{
			bRet = TRUE;
		} break;

		case IncludeThreadCallback:
		{
			bRet = TRUE;
		} break;

		case ModuleCallback:
		{
			if (!(pOutput->ModuleWriteFlags & ModuleReferencedByMemory)) 
			{
				pOutput->ModuleWriteFlags &= (~ModuleWriteModule);
			}
			bRet = TRUE;
		} break;

		case ThreadCallback:
		{
			bRet = TRUE;
		} break;

		case ThreadExCallback:
		{
			bRet = TRUE;
		} break;

		case MemoryCallback:
		{
			bRet = FALSE;
		} break;

		case CancelCallback:
			break;
	}

	return bRet;
}

bool CreateMiniDump(EXCEPTION_POINTERS* pExceptionInfo)
{
	fprintf(stderr, "Exception handled: %p", pExceptionInfo);
	if (IsDebuggerPresent())
		DebugBreak();

	char szProcessName[MAX_PATH];
	GetModuleFileNameA(NULL, szProcessName, MAX_PATH);
	std::string strFileName = std::string(szProcessName);
	if (strFileName.size() > 0)
	{
		size_t iLastSlash = strFileName.find_last_of("\\/");
		strFileName = strFileName.substr(iLastSlash + 1, strFileName.length() - iLastSlash);
	}

	time_t t;
	time(&t);
	struct tm *tinfo;
	tinfo = localtime(&t);

	char szDumpName[128];
	strftime(szDumpName, sizeof(szDumpName), "dump%Y%m%d_%H%M%S.dmp", tinfo);

	char szDumpPath[256];
	sprintf(szDumpPath, "%s_%s", strFileName.c_str(), szDumpName);

	HANDLE hFile = CreateFileA(szDumpPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (!hFile || hFile == INVALID_HANDLE_VALUE)
	{
		fprintf(stderr, "Exception dump file is not created. Error code: %u Path: %s", GetLastError(), szDumpPath);
		return false;
	}

	// Create the minidump 
	MINIDUMP_EXCEPTION_INFORMATION mdei;
	mdei.ThreadId = GetCurrentThreadId();
	mdei.ExceptionPointers = pExceptionInfo;
	mdei.ClientPointers = FALSE;

	MINIDUMP_CALLBACK_INFORMATION mci;
	mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
	mci.CallbackParam = 0;

	MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory);

	BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (pExceptionInfo != 0) ? &mdei : 0, 0, &mci);
	if (!rv)
	{
		fprintf(stderr, "Exception dump can not created. Error code: %u", GetLastError());
	}
	else
	{
		fprintf(stderr, "Exception dump successfully created.");
	}

	// Close the file 
	CloseHandle(hFile);
	return true;
}

LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS * pExceptionInfo)
{
	if (pExceptionInfo && pExceptionInfo->ExceptionRecord)
	{
		if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
		{
			HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateMiniDump, pExceptionInfo, 0, NULL);
			if (hThread && hThread != INVALID_HANDLE_VALUE)
			{
				WaitForSingleObject(hThread, INFINITE);
				CloseHandle(hThread);
			}
		}
		else
		{
			CreateMiniDump(pExceptionInfo);
		}
	}
	return EXCEPTION_EXECUTE_HANDLER;
}

bool setup_minidump_generator()
{
	if (SetUnhandledExceptionFilter(ExceptionFilter))
	{
		fprintf(stderr, "Mini dump generator succesfully created!");
		return true;
	}
	fprintf(stderr, "Mini dump generator can NOT created! Error code: %u", GetLastError());
	return false;
}

#else

bool setup_minidump_generator()
{
	return true;
}

#endif

 

winminidump.h

#ifndef __INC_LIBTHECORE_WINMINIDUMP_H__
#define __INC_LIBTHECORE_WINMINIDUMP_H__

#ifdef __cplusplus
extern "C"
{
#endif
	extern bool setup_minidump_generator();

#ifdef __cplusplus
};
#endif

#endif

 

Copy .c file to ServerSrc\libthecore\src and .h file to ServerSrc\libthecore\include. Define new files to your Makefile or CMakeLists.txt, and to libthecore project from your server's visual studio solution

 

Open game\src\main.cpp and call new header file 

#include "../../libthecore/include/winminidump.h"


Now search for your entrypoint

int main(int argc, char **argv)


Add new lines at the beginning of the function

    if (setup_minidump_generator() == false)
        return 1;

 

Open your db\src\main.cpp and call new header file 

#include "../../libthecore/include/winminidump.h"


Now search for your entrypoint

int main()


Add new lines at the beginning of the function

    if (setup_minidump_generator() == false)
        return 1;


If you are using rubinum's auth core you can implement same routine to "int main(int argc, char** argv)" function from auth\src\main.cpp

 

If you take any exceptions after making these edits, The core will create a memory dump with the file name like CORE_FILE_NAME_dump_DATE.dmp and you can easily analyze this file via visual studio just like freebsd ".core" files.


 

  • Metin2 Dev 6
  • Good 2
  • Love 11
Link to comment
Share on other sites

  • Honorable Member

moved to: 

This is the hidden content, please

Few days ago, I did something similar for the client (EterLib\error.cpp) (instead of errorlog):


#define ENABLE_CRASH_MINIDUMP
#ifdef ENABLE_CRASH_MINIDUMP
#	include "../UserInterface/Version.h"
#	include <iomanip>
#	include <sstream>
void make_minidump(EXCEPTION_POINTERS * e)
{
	auto hDbgHelp = LoadLibraryA("dbghelp");
	if (hDbgHelp == nullptr)
		return;
	auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump)) GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
	if (pMiniDumpWriteDump == nullptr)
		return;
	// folder name
	std::string folder = "logs";
	CreateDirectoryA(folder.c_str(), nullptr);
	// time format
	auto t = std::time(nullptr);
	std::ostringstream timefmt;
	timefmt << std::put_time(std::localtime(&t), "%Y%m%d_%H%M%S");
	// filename
	std::string filename = fmt::format("{}\\metin2client_{}_{}.dmp", folder, METIN2_GET_VERSION(), timefmt.str());

	auto hFile = CreateFileA(filename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (hFile == INVALID_HANDLE_VALUE)
		return;

	MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
	exceptionInfo.ThreadId = GetCurrentThreadId();
	exceptionInfo.ExceptionPointers = e;
	exceptionInfo.ClientPointers = FALSE;

	auto dumped = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
									 MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
									 e ? &exceptionInfo : nullptr, nullptr, nullptr);

	CloseHandle(hFile);

	// std::string errMsg = "The application crashed. Send the generated file to the staff: "s + name;
	// MessageBox(nullptr, errMsg.c_str(), "Metin2Client", MB_ICONSTOP);

	return;
}
#endif

LONG __stdcall EterExceptionFilter(_EXCEPTION_POINTERS * pExceptionInfo)
{
#ifdef ENABLE_CRASH_MINIDUMP
	make_minidump(pExceptionInfo);
#else
	// eterlog trash
#endif
	return EXCEPTION_EXECUTE_HANDLER;
}

I'm not using it since I'll use crashrpt2 instead.

 

Version without c++17&libfmt:


#define ENABLE_CRASH_MINIDUMP
#ifdef ENABLE_CRASH_MINIDUMP
#include "../UserInterface/Version.h"
#include <iomanip>
#include <sstream>
void make_minidump(EXCEPTION_POINTERS* e)
{
    auto hDbgHelp = LoadLibraryA("dbghelp");
    if(hDbgHelp == nullptr)
        return;
    auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
    if(pMiniDumpWriteDump == nullptr)
        return;
	// folder name
	std::string folder = "logs";
	CreateDirectoryA(folder.c_str(), nullptr);
	// time format
	auto t = std::time(nullptr);
	std::ostringstream timefmt;
	timefmt << std::put_time(std::localtime(&t), "%Y%m%d_%H%M%S");
	// filename
	std::string filename = folder + "\\"s + "metin2client_"s + std::to_string(METIN2_GET_VERSION()) + "_"s + timefmt.str() + ".dmp";

    auto hFile = CreateFileA(filename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if(hFile == INVALID_HANDLE_VALUE)
        return;

    MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
    exceptionInfo.ThreadId = GetCurrentThreadId();
    exceptionInfo.ExceptionPointers = e;
    exceptionInfo.ClientPointers = FALSE;

    auto dumped = pMiniDumpWriteDump(
        GetCurrentProcess(),
        GetCurrentProcessId(),
        hFile,
        MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
        e ? &exceptionInfo : nullptr,
        nullptr,
        nullptr);

    CloseHandle(hFile);

	// std::string errMsg = "The application crashed. Send the generated file to the staff: "s + name;
	// MessageBox(nullptr, errMsg.c_str(), "Metin2Client", MB_ICONSTOP);

    return;
}
#endif

LONG __stdcall EterExceptionFilter(_EXCEPTION_POINTERS * pExceptionInfo)
{
#ifdef ENABLE_CRASH_MINIDUMP
	make_minidump(pExceptionInfo);
#else
	// eterlog trash
#endif
	return EXCEPTION_EXECUTE_HANDLER;
}

 

Edited by martysama0134
  • Metin2 Dev 10
  • Good 7
  • Love 12
Link to comment
Share on other sites

  • Management
4 hours ago, martysama0134 said:

Few days ago, I did something similar for the client (EterLib\error.cpp) (instead of errorlog):



#define ENABLE_CRASH_MINIDUMP
#ifdef ENABLE_CRASH_MINIDUMP
#	include "../UserInterface/Version.h"
#	include <iomanip>
#	include <sstream>
void make_minidump(EXCEPTION_POINTERS * e)
{
	auto hDbgHelp = LoadLibraryA("dbghelp");
	if (hDbgHelp == nullptr)
		return;
	auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump)) GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
	if (pMiniDumpWriteDump == nullptr)
		return;
	// folder name
	std::string folder = "logs";
	CreateDirectoryA(folder.c_str(), nullptr);
	// time format
	auto t = std::time(nullptr);
	std::ostringstream timefmt;
	timefmt << std::put_time(std::localtime(&t), "%Y%m%d_%H%M%S");
	// filename
	std::string filename = fmt::format("{}\\metin2client_{}_{}.dmp", folder, METIN2_GET_VERSION(), timefmt.str());

	auto hFile = CreateFileA(filename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (hFile == INVALID_HANDLE_VALUE)
		return;

	MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
	exceptionInfo.ThreadId = GetCurrentThreadId();
	exceptionInfo.ExceptionPointers = e;
	exceptionInfo.ClientPointers = FALSE;

	auto dumped = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
									 MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
									 e ? &exceptionInfo : nullptr, nullptr, nullptr);

	CloseHandle(hFile);

	// std::string errMsg = "The application crashed. Send the generated file to the staff: "s + name;
	// MessageBox(nullptr, errMsg.c_str(), "Metin2Client", MB_ICONSTOP);

	return;
}
#endif

LONG __stdcall EterExceptionFilter(_EXCEPTION_POINTERS * pExceptionInfo)
{
#ifdef ENABLE_CRASH_MINIDUMP
	make_minidump(pExceptionInfo);
#else
	// eterlog trash
#endif
	return EXCEPTION_EXECUTE_HANDLER;
}

I'm not using it since I'll use crashrpt2 instead.

 

Version without c++17&libfmt:



#define ENABLE_CRASH_MINIDUMP
#ifdef ENABLE_CRASH_MINIDUMP
#include "../UserInterface/Version.h"
#include <iomanip>
#include <sstream>
void make_minidump(EXCEPTION_POINTERS* e)
{
    auto hDbgHelp = LoadLibraryA("dbghelp");
    if(hDbgHelp == nullptr)
        return;
    auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
    if(pMiniDumpWriteDump == nullptr)
        return;
	// folder name
	std::string folder = "logs";
	CreateDirectoryA(folder.c_str(), nullptr);
	// time format
	auto t = std::time(nullptr);
	std::ostringstream timefmt;
	timefmt << std::put_time(std::localtime(&t), "%Y%m%d_%H%M%S");
	// filename
	std::string filename = folder + "\\"s + "metin2client_"s + std::to_string(METIN2_GET_VERSION()) + "_"s + timefmt.str() + ".dmp";

    auto hFile = CreateFileA(filename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if(hFile == INVALID_HANDLE_VALUE)
        return;

    MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
    exceptionInfo.ThreadId = GetCurrentThreadId();
    exceptionInfo.ExceptionPointers = e;
    exceptionInfo.ClientPointers = FALSE;

    auto dumped = pMiniDumpWriteDump(
        GetCurrentProcess(),
        GetCurrentProcessId(),
        hFile,
        MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
        e ? &exceptionInfo : nullptr,
        nullptr,
        nullptr);

    CloseHandle(hFile);

	// std::string errMsg = "The application crashed. Send the generated file to the staff: "s + name;
	// MessageBox(nullptr, errMsg.c_str(), "Metin2Client", MB_ICONSTOP);

    return;
}
#endif

LONG __stdcall EterExceptionFilter(_EXCEPTION_POINTERS * pExceptionInfo)
{
#ifdef ENABLE_CRASH_MINIDUMP
	make_minidump(pExceptionInfo);
#else
	// eterlog trash
#endif
	return EXCEPTION_EXECUTE_HANDLER;
}

 

hY6luEf.png

I've this error, I'm using the version without  c++17 & libfmt

Oc2Aboa.png

Edited by Metin2 Dev
Core X - External 2 Internal

raw

raw

Link to comment
Share on other sites

  • Honorable Member
24 minutes ago, charparodar said:

hY6luEf.png

I've this error, I'm using the version without  c++17 & libfmt

Oc2Aboa.png

ah yes, replace Version.h with

#pragma once
#define VER_FILE_VERSION 1, 0, 40999, 0
#define VER_FILE_VERSION_STR "1.0.40999.1"
inline int METIN2_GET_VERSION()
{
	return 40999;
}

inline and pragma once were missing.

I used 40999 but anything is fine.

.dmp files can be debugged with visual studio, but the exact .exe, source files and .pdb files are needed, otherwise the result is junk.

Edited by Metin2 Dev
Core X - External 2 Internal
  • Good 1
  • Love 1
Link to comment
Share on other sites

Announcements



  • Similar Content

  • Activity

    1. 113

      Ulthar SF V2 (TMP4 Base)

    2. 2

      Feeding game source to LLM

    3. 0

      Target Information System

    4. 2

      Feeding game source to LLM

    5. 2

      anti exp explanation pls

    6. 2

      Feeding game source to LLM

    7. 2

      anti exp explanation pls

  • Recently Browsing

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.