گزارش کراش سمت کلاینت در C++ - هفت خط کد انجمن پرسش و پاسخ برنامه نویسی

گزارش کراش سمت کلاینت در C++

+1 امتیاز

بارها براتون پیش اومده که کلاینت کرش می کنه برای رفع این کرش باید نقطه کرش تو دیباگرتون مشخص باشه که به خطای نقطه موردنظر پی ببرید.

یکی از راههای که وجود داره استفاده از لاگ چرخشی بر روی هارد دیسک هستس که تمامی هات اسپات مستعد یا ورودی و خروجی توابع مستعد رو لاگ بزنیم که بعد این فایل dump را بررسی کنیم ببینم کلاینت تو کدام تابع متوقف شده البته همانطوری که می دونید تو این حالت وضعیت حافظه در اختیارتون نیست که دقیقا متوجه بشید تو چه شرایطی همچنین مشکلی در آن نقطه خاص بوجود آمده

راه حل دیگر ذخیره سازی dump حافظه در صورت کرش به صورت minidump روی کلاینت هستش البته این کار تو پلتفرم های مختلف فرق داره که هر پلت فرمی API متفاوتی برای اینکار در نظر گرفته شده .جهت استفاده از راه حل کرس پلتفرم شرکت گوگل فریم ورکی به نام BreakPad طراحی کرده که فایل minidump با ساختار minidump ویژوال استدیو براتون میسازه که این فایل تو ویندوز با دیباگر vs و windbg سازگار هستش برای اینکار ابتدا Breakpad را clone کنید و سپس بیلدش کنید من این کار را با vcpkg نجام دادم.حالا برای گزارش گیری از کرش کلاینت به مراحل زیر نیاز دارید.

1-تو properties->linker->debugger->genrate debug info  را فعال کنید با اینکار فایل .pdb ساخته میشه که این فایل در مرحله بعد بهش نیاز داریم.

2-حالا باید یه کلاس crashhandler بسازید که به صورت زیر عمل کنید.

//CrashHandler.h

#pragma once

#pragma once
#include <QtCore/QString>

namespace Atomix
{
	class CrashHandlerPrivate;
	class CrashHandler
	{
	public:
		static CrashHandler* instance();
		void Init(const QString&  reportPath);

		void setReportCrashesToSystem(bool report);
		bool writeMinidump();

	private:
		CrashHandler();
		~CrashHandler();
		Q_DISABLE_COPY(CrashHandler)
			CrashHandlerPrivate* d;
	};
}

 

//CrashHandler.cpp

#include "CrashHandler.h"
#include <QtCore/QDir>
#include <QtCore/QProcess>
#include <QtCore/QCoreApplication>
#include <QString>

#if defined(Q_OS_MAC)
#include "client/mac/handler/exception_handler.h"
#elif defined(Q_OS_LINUX)
#include "client/linux/handler/exception_handler.h"
#elif defined(Q_OS_WIN32)
#include "client/windows/handler/exception_handler.h"
#endif

namespace Atomix
{
	/************************************************************************/
	/* CrashHandlerPrivate                                                  */
	/************************************************************************/
	class CrashHandlerPrivate
	{
	public:
		CrashHandlerPrivate()
		{
			pHandler = NULL;
		}

		~CrashHandlerPrivate()
		{
			delete pHandler;
		}

		void InitCrashHandler(const QString& dumpPath);
		static google_breakpad::ExceptionHandler* pHandler;
		static bool bReportCrashesToSystem;
	};

	google_breakpad::ExceptionHandler* CrashHandlerPrivate::pHandler = NULL;
	bool CrashHandlerPrivate::bReportCrashesToSystem = false;

	/************************************************************************/
	/* DumpCallback                                                         */
	/************************************************************************/
#if defined(Q_OS_WIN32)
	bool DumpCallback(const wchar_t* _dump_dir, const wchar_t* _minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool success)
#elif defined(Q_OS_LINUX)
	bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success)
#elif defined(Q_OS_MAC)
	bool DumpCallback(const char* _dump_dir, const char* _minidump_id, void *context, bool success)
#endif
	{
		Q_UNUSED(context);
#if defined(Q_OS_WIN32)
		Q_UNUSED(_dump_dir);
		Q_UNUSED(_minidump_id);
		Q_UNUSED(assertion);
		Q_UNUSED(exinfo);
#endif
		qDebug("BreakpadQt crash");

		/*
		NO STACK USE, NO HEAP USE THERE !!!
		Creating QString's, using qDebug, etc. - everything is crash-unfriendly.
		*/
		return CrashHandlerPrivate::bReportCrashesToSystem ? success : true;
	}

	void CrashHandlerPrivate::InitCrashHandler(const QString& dumpPath)
	{
		if (pHandler != NULL)
			return;

#if defined(Q_OS_WIN32)
		std::wstring pathAsStr = (const wchar_t*)dumpPath.utf16();
		pHandler = new google_breakpad::ExceptionHandler(
			pathAsStr,
			/*FilterCallback*/ 0,
			DumpCallback,
			/*context*/
			0,
			true
		);
#elif defined(Q_OS_LINUX)
		std::string pathAsStr = dumpPath.toStdString();
		google_breakpad::MinidumpDescriptor md(pathAsStr);
		pHandler = new google_breakpad::ExceptionHandler(
			md,
			/*FilterCallback*/ 0,
			DumpCallback,
			/*context*/ 0,
			true,
			-1
		);
#elif defined(Q_OS_MAC)
		std::string pathAsStr = dumpPath.toStdString();
		pHandler = new google_breakpad::ExceptionHandler(
			pathAsStr,
			/*FilterCallback*/ 0,
			DumpCallback,
			/*context*/
			0,
			true,
			NULL
		);
#endif
	}

	/************************************************************************/
	/* CrashHandler                                                         */
	/************************************************************************/
	CrashHandler* CrashHandler::instance()
	{
		static CrashHandler globalHandler;
		return &globalHandler;
	}

	CrashHandler::CrashHandler()
	{
		d = new CrashHandlerPrivate();
	}

	CrashHandler::~CrashHandler()
	{
		delete d;
	}

	void CrashHandler::setReportCrashesToSystem(bool report)
	{
		d->bReportCrashesToSystem = report;
	}

	bool CrashHandler::writeMinidump()
	{
		bool res = d->pHandler->WriteMinidump();
		if (res) {
			qDebug("BreakpadQt: writeMinidump() successed.");
		}
		else {
			qWarning("BreakpadQt: writeMinidump() failed.");
		}
		return res;
	}

	void CrashHandler::Init(const QString& reportPath)
	{
		d->InitCrashHandler(reportPath);
	}
}

 

 

سوال شده مهر 26, 1397  بوسیله ی مصطفی ساتکی (امتیاز 21,998)   24 34 75
ویرایش شده مهر 26, 1397 بوسیله ی مصطفی ساتکی

1 پاسخ

+1 امتیاز

3- همانطور که دیدید کلاس CrashHandler یک کلاس singlton هستش که از طریق متد init مشخص می کنیم که فایل minidump در چه مسیری ذخیره شه من مسیر c:\\dump را انتخاب کردم به صورت زیر:

#if defined(Q_OS_WIN32)
	Atomix::CrashHandler::instance()->Init("c:\\dump");
#elif defined(Q_OS_LINUX)
	Atomix::CrashHandler::instance()->Init("/Users/dev/dump");
#elif defined(Q_OS_MAC)
	Atomix::CrashHandler::instance()->Init("/Users/User/dump");
#endif

4- در این مرحله نوبت به ساخت فایل symbol هسنش چون اگر ساخته نشه symbol ها توی کد به صورت آدرس حافظه دیده میشه .برای اینکار برنامه رو بیلد کنید تا فایل exe , pdb مجدد ساخته شه حال از یکی از utility های breakpad به نام  dump_syms.exe  که هنگام بیلدش ساخته شده فایل sym را ایجاد کنید به صورت زیر:

dump_syms.exe MyClient.exe > MyClient.sym

5- کلاینت را در سیستم مقصد اجرا کنید در صورت کرش کردن برنامه در مسیر از قبل مشخص شده (c:\\dump) فایل minidump ساخته میشه 

6- این فایل را سمت دیباگر انتقال بدید و سپس تو vs منوی new->file فایل minidump موردنظر را انتخاب کنید پنجره ای باز میشه که اطلاعات در مورد کلاینت و مجموعه کتبخانه های مورد استفاده میده سپس در ایتدا گزینه ste symbol path را انتخاب کنید و مسیر فایل sym را معرفی کنید و سپس گزینه debug with native only را انتخاب کنید حالا می تونید نقطه کرش را از طریق call stack بررسی کنید.

 

موفق باشید

پاسخ داده شده مهر 26, 1397 بوسیله ی مصطفی ساتکی (امتیاز 21,998)   24 34 75
خیلی کاربردی این تکنیک علی الخصوص وقتی به symbole ها که name mangling شدن در سیستم عاملهای مختلف دسترسی داریم.
فقط یک نقطه ضعف داره که اونهم استفاده از فریم وورک کیوت هست،
...