Как записать дамп при краше

111
19 января 2021, 00:00

Выпустили мы программу в продажу,у кого-то крашит у кого-то нет. У разработчика - нет,но у некоторых юзеров - да. Как мне записывать\получать логи крашей их? Язык с++ То есть типо так, произошел краш,создается файл туда идет запись стека,адреса где был краш и т.д

Answer 1

Вот перевод ответа How to write a sample code that will crash and produce dump file? с комментариями и дополнениями:

#include <csignal>
#include <Windows.h>
#include <Dbghelp.h>
#include <crtdbg.h>
// Путь к программе. Инициализируется на старте.
static wchar_t appPath[MAX_PATH];
// Функция, которая пишет дамп в файл |fileName|.
static bool make_dump(EXCEPTION_POINTERS* e, const wchar_t* fileName) {
  // Грузим системную библиотеку создания дампов.
  // Возможно её не будет в системе, тогда надо положить её рядом с программой.
  auto hDbgHelp = LoadLibraryW(L"dbghelp.dll");
  if (!hDbgHelp)
    return false;
  auto pMiniDumpWriteDump =
      (decltype(&MiniDumpWriteDump))GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
  if (!pMiniDumpWriteDump)
    return false;
  // Открываем файл.
  HANDLE hFile = ::CreateFileW(fileName, GENERIC_WRITE, FILE_SHARE_READ, 0,
                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  if (hFile == INVALID_HANDLE_VALUE || hFile == nullptr)
      return false;
  MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
  exceptionInfo.ThreadId = ::GetCurrentThreadId();
  exceptionInfo.ExceptionPointers = e;
  exceptionInfo.ClientPointers = FALSE;
  const DWORD DUMP_FLAGS = MiniDumpWithDataSegs | MiniDumpWithPrivateReadWriteMemory;
  // Пишем дамп в файл.
  BOOL dumped = pMiniDumpWriteDump(::GetCurrentProcess(),
                                   ::GetCurrentProcessId(),
                                   hFile,
                                   MINIDUMP_TYPE(DUMP_FLAGS),
                                   e ? &exceptionInfo : NULL,
                                   NULL,
                                   NULL);
  // Если не удалось сделать дамп - пишем ошибку.
  if (!dumped) {
    DWORD err = GetLastError();
    DWORD written;
    ::WriteFile(hFile, &err, 4, &written, 0);
  }
  ::CloseHandle(hFile);
  return true;
}
// Старый обработчик исключений.
static LPTOP_LEVEL_EXCEPTION_FILTER s_oldEH;
// Наш обработчик исключений.
static LONG CALLBACK unhandled_exception_handler(EXCEPTION_POINTERS* e) {
  // Формируем имя файла.
  SYSTEMTIME t;
  GetSystemTime(&t);
  static wchar_t fileName[1024];
  wsprintfW(fileName, L"%s\\dump_%d%02d%02d_%02d%02d%02d.dmp", appPath,
            t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
  // Пишем дамп в файл.
  auto dumpOk = make_dump(e, fileName);
  // Если он не записался, передаем исключение дальше.
  if (!dumpOk)
      return s_oldEH ? s_oldEH(e) : EXCEPTION_CONTINUE_SEARCH;
  // Показываем пользователю куда записался дамп.
  static wchar_t buf[1024];
  wsprintfW(buf,
            L"Application will be closed due to a fatal error.\r\n"
            L"Crash dump was saved to\r\n%s",
            fileName);
  MessageBox(nullptr, buf, nullptr, MB_ICONERROR | MB_OK);
  // Убиваем процесс.
  ::TerminateProcess(::GetCurrentProcess(), 5);
  return 0;
}
// Обработка |abort()|.
static void abort_handler(int) {
  ::RaiseException(0x00BAD00A, 0, 0, NULL);
}
// Обработка вызова pure virtual функции.
static void purecall_handler() {
  ::RaiseException(0x00BAD00C, 0, 0, NULL);
}
// Еще какие-то ошибки CRT.
static int crt_report_handler(int reportType, wchar_t*, int* pRet) {
  if (reportType == _CRT_WARN) {
    if (pRet)
      *pRet = 0;
    return 0;
  }
  ::RaiseException(0x00BAD00E, 0, 0, NULL);
  return 0;
}
// Функция инициализации.
static void init() {
  // Сохраняем путь к .exe , на случай если креш не даст его получить потом.
  ::GetModuleFileNameW(GetModuleHandle(nullptr), appPath, MAX_PATH);
  // Обрезаем до папки.
  *wcsrchr(appPath, L'\\') = '\0';
  // Устанавливаем обработчики.
  s_oldEH = ::SetUnhandledExceptionFilter(unhandled_exception_handler);
  ::_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
  ::signal(SIGABRT, abort_handler);
  ::_set_purecall_handler(purecall_handler);
  _CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, crt_report_handler);
  (void)&crt_report_handler;
}
// Инициализируемся до main()
bool g_crashdump_initialized = (init(), false);

А вообще просто используйте crashpad.

READ ALSO
Возврат auto значения шаблона

Возврат auto значения шаблона

Как вернуть неизвестный заранее тип переменной шаблона при помощи ключевого слова auto ?

130
QGridLayout и QMenuBar

QGridLayout и QMenuBar

У меня класс наследуется от QWidgetВ этом классе я создал grid_layout и меню классы

114
С# WPF Работа с реестром через WMI

С# WPF Работа с реестром через WMI

Необходимо получить список всех установленных приложений на удаленном компьютереКласс WMI Win32_Pdoduct показывает только программы установленные...

102