Change: store crash logs in JSON format (#11232)

This commit is contained in:
Patric Stout
2023-09-14 20:13:27 +02:00
committed by GitHub
parent a0353af223
commit 37e2f99c09
14 changed files with 729 additions and 751 deletions

View File

@@ -49,53 +49,24 @@ class CrashLogOSX : public CrashLog {
/** Signal that has been thrown. */
int signum;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override
void SurveyCrash(nlohmann::json &survey) const override
{
int ver_maj, ver_min, ver_bug;
GetMacOSVersion(&ver_maj, &ver_min, &ver_bug);
const NXArchInfo *arch = NXGetLocalArchInfo();
fmt::format_to(output_iterator,
"Operating system:\n"
" Name: Mac OS X\n"
" Release: {}.{}.{}\n"
" Machine: {}\n"
" Min Ver: {}\n"
" Max Ver: {}\n",
ver_maj, ver_min, ver_bug,
arch != nullptr ? arch->description : "unknown",
MAC_OS_X_VERSION_MIN_REQUIRED,
MAC_OS_X_VERSION_MAX_ALLOWED
);
survey["id"] = signum;
survey["reason"] = strsignal(signum);
}
void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override
void SurveyStacktrace(nlohmann::json &survey) const override
{
fmt::format_to(output_iterator,
"Crash reason:\n"
" Signal: {} ({})\n"
" Message: {}\n\n",
strsignal(this->signum),
this->signum,
message
);
}
void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override
{
fmt::format_to(output_iterator, "\nStacktrace:\n");
void *trace[64];
int trace_size = backtrace(trace, lengthof(trace));
survey = nlohmann::json::array();
char **messages = backtrace_symbols(trace, trace_size);
for (int i = 0; i < trace_size; i++) {
fmt::format_to(output_iterator, "{}\n", messages[i]);
survey.push_back(messages[i]);
}
free(messages);
fmt::format_to(output_iterator, "\n");
}
#ifdef WITH_UNOFFICIAL_BREAKPAD
@@ -153,7 +124,7 @@ public:
"A serious fault condition occurred in the game. The game will shut down.";
std::string message = fmt::format(
"Please send crash.log, crash.dmp, and crash.sav to the developers. "
"Please send crash.json.log, crash.dmp, and crash.sav to the developers. "
"This will greatly help debugging.\n\n"
"https://github.com/OpenTTD/OpenTTD/issues.\n\n"
"{}\n{}\n{}\n{}",

View File

@@ -10,7 +10,7 @@
#include "../../stdafx.h"
#include "../../3rdparty/fmt/format.h"
#include "../../3rdparty/nlohmann/json.hpp"
#include "../../survey.h"
#include "macos.h"
#include <mach-o/arch.h>
@@ -18,8 +18,6 @@
#include "../../safeguards.h"
extern std::string SurveyMemoryToText(uint64_t memory);
void SurveyOS(nlohmann::json &json)
{
int ver_maj, ver_min, ver_bug;

View File

@@ -49,55 +49,26 @@ class CrashLogUnix : public CrashLog {
/** Signal that has been thrown. */
int signum;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override
void SurveyCrash(nlohmann::json &survey) const override
{
struct utsname name;
if (uname(&name) < 0) {
fmt::format_to(output_iterator, "Could not get OS version: {}\n", strerror(errno));
return;
}
fmt::format_to(output_iterator,
"Operating system:\n"
" Name: {}\n"
" Release: {}\n"
" Version: {}\n"
" Machine: {}\n",
name.sysname,
name.release,
name.version,
name.machine
);
survey["id"] = signum;
survey["reason"] = strsignal(signum);
}
void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override
void SurveyStacktrace(nlohmann::json &survey) const override
{
fmt::format_to(output_iterator,
"Crash reason:\n"
" Signal: {} ({})\n"
" Message: {}\n\n",
strsignal(this->signum),
this->signum,
message
);
}
void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override
{
fmt::format_to(output_iterator, "Stacktrace:\n");
#if defined(__GLIBC__)
void *trace[64];
int trace_size = backtrace(trace, lengthof(trace));
survey = nlohmann::json::array();
char **messages = backtrace_symbols(trace, trace_size);
for (int i = 0; i < trace_size; i++) {
fmt::format_to(output_iterator, " [{:02}] {}\n", i, messages[i]);
survey.push_back(messages[i]);
}
free(messages);
#else
fmt::format_to(output_iterator, " Not supported.\n");
#endif
fmt::format_to(output_iterator, "\n");
}
#ifdef WITH_UNOFFICIAL_BREAKPAD

View File

@@ -8,8 +8,7 @@
/** @file survey_unix.cpp Unix implementation of OS-specific survey information. */
#include "../../stdafx.h"
#include "../../3rdparty/nlohmann/json.hpp"
#include "../../survey.h"
#include <sys/utsname.h>
#include <thread>
@@ -17,8 +16,6 @@
#include "../../safeguards.h"
extern std::string SurveyMemoryToText(uint64_t memory);
void SurveyOS(nlohmann::json &json)
{
struct utsname name;

View File

@@ -38,6 +38,33 @@
/** Exception code used for custom abort. */
static constexpr DWORD CUSTOM_ABORT_EXCEPTION = 0xE1212012;
/** A map between exception code and its name. */
static const std::map<DWORD, std::string> exception_code_to_name{
{EXCEPTION_ACCESS_VIOLATION, "EXCEPTION_ACCESS_VIOLATION"},
{EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"},
{EXCEPTION_BREAKPOINT, "EXCEPTION_BREAKPOINT"},
{EXCEPTION_DATATYPE_MISALIGNMENT, "EXCEPTION_DATATYPE_MISALIGNMENT"},
{EXCEPTION_FLT_DENORMAL_OPERAND, "EXCEPTION_FLT_DENORMAL_OPERAND"},
{EXCEPTION_FLT_DIVIDE_BY_ZERO, "EXCEPTION_FLT_DIVIDE_BY_ZERO"},
{EXCEPTION_FLT_INEXACT_RESULT, "EXCEPTION_FLT_INEXACT_RESULT"},
{EXCEPTION_FLT_INVALID_OPERATION, "EXCEPTION_FLT_INVALID_OPERATION"},
{EXCEPTION_FLT_OVERFLOW, "EXCEPTION_FLT_OVERFLOW"},
{EXCEPTION_FLT_STACK_CHECK, "EXCEPTION_FLT_STACK_CHECK"},
{EXCEPTION_FLT_UNDERFLOW, "EXCEPTION_FLT_UNDERFLOW"},
{EXCEPTION_GUARD_PAGE, "EXCEPTION_GUARD_PAGE"},
{EXCEPTION_ILLEGAL_INSTRUCTION, "EXCEPTION_ILLEGAL_INSTRUCTION"},
{EXCEPTION_IN_PAGE_ERROR, "EXCEPTION_IN_PAGE_ERROR"},
{EXCEPTION_INT_DIVIDE_BY_ZERO, "EXCEPTION_INT_DIVIDE_BY_ZERO"},
{EXCEPTION_INT_OVERFLOW, "EXCEPTION_INT_OVERFLOW"},
{EXCEPTION_INVALID_DISPOSITION, "EXCEPTION_INVALID_DISPOSITION"},
{EXCEPTION_INVALID_HANDLE, "EXCEPTION_INVALID_HANDLE"},
{EXCEPTION_NONCONTINUABLE_EXCEPTION, "EXCEPTION_NONCONTINUABLE_EXCEPTION"},
{EXCEPTION_PRIV_INSTRUCTION, "EXCEPTION_PRIV_INSTRUCTION"},
{EXCEPTION_SINGLE_STEP, "EXCEPTION_SINGLE_STEP"},
{EXCEPTION_STACK_OVERFLOW, "EXCEPTION_STACK_OVERFLOW"},
{STATUS_UNWIND_CONSOLIDATE, "STATUS_UNWIND_CONSOLIDATE"},
};
/**
* Forcefully try to terminate the application.
*
@@ -57,9 +84,17 @@ class CrashLogWindows : public CrashLog {
/** Information about the encountered exception */
EXCEPTION_POINTERS *ep;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override;
void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override;
void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override;
void SurveyCrash(nlohmann::json &survey) const override
{
survey["id"] = ep->ExceptionRecord->ExceptionCode;
if (exception_code_to_name.count(ep->ExceptionRecord->ExceptionCode) > 0) {
survey["reason"] = exception_code_to_name.at(ep->ExceptionRecord->ExceptionCode);
} else {
survey["reason"] = "Unknown exception code";
}
}
void SurveyStacktrace(nlohmann::json &survey) const override;
public:
#ifdef WITH_UNOFFICIAL_BREAKPAD
@@ -136,41 +171,11 @@ public:
/* static */ CrashLogWindows *CrashLogWindows::current = nullptr;
/* virtual */ void CrashLogWindows::LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const
{
_OSVERSIONINFOA os;
os.dwOSVersionInfoSize = sizeof(os);
GetVersionExA(&os);
fmt::format_to(output_iterator,
"Operating system:\n"
" Name: Windows\n"
" Release: {}.{}.{} ({})\n",
os.dwMajorVersion,
os.dwMinorVersion,
os.dwBuildNumber,
os.szCSDVersion
);
}
/* virtual */ void CrashLogWindows::LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const
{
fmt::format_to(output_iterator,
"Crash reason:\n"
" Exception: {:08X}\n"
" Location: {:X}\n"
" Message: {}\n\n",
ep->ExceptionRecord->ExceptionCode,
(size_t)ep->ExceptionRecord->ExceptionAddress,
message
);
}
#if defined(_MSC_VER)
static const uint MAX_SYMBOL_LEN = 512;
static const uint MAX_FRAMES = 64;
/* virtual */ void CrashLogWindows::LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const
/* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
{
DllLoader dbghelp(L"dbghelp.dll");
struct ProcPtrs {
@@ -195,7 +200,7 @@ static const uint MAX_FRAMES = 64;
dbghelp.GetProcAddress("SymGetLineFromAddr64"),
};
fmt::format_to(output_iterator, "Stack trace:\n");
survey = nlohmann::json::array();
/* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
if (dbghelp.Success()) {
@@ -246,7 +251,7 @@ static const uint MAX_FRAMES = 64;
hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
fmt::format_to(output_iterator, " <infinite loop>\n");
survey.push_back("<infinite loop>");
break;
}
@@ -260,33 +265,31 @@ static const uint MAX_FRAMES = 64;
}
/* Print module and instruction pointer. */
fmt::format_to(output_iterator, "[{:02}] {:20s} {:X}", num, mod_name, frame.AddrPC.Offset);
std::string message = fmt::format("{:20s} {:X}", mod_name, frame.AddrPC.Offset);
/* Get symbol name and line info if possible. */
DWORD64 offset;
if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
fmt::format_to(output_iterator, " {} + {}", sym_info->Name, offset);
message += fmt::format(" {} + {}", sym_info->Name, offset);
DWORD line_offs;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
fmt::format_to(output_iterator, " ({}:{})", line.FileName, line.LineNumber);
message += fmt::format(" ({}:{})", line.FileName, line.LineNumber);
}
}
fmt::format_to(output_iterator, "\n");
survey.push_back(message);
}
proc.pSymCleanup(hCur);
}
fmt::format_to(output_iterator, "\n");
}
#else
/* virtual */ void CrashLogWindows::LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const
/* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
{
fmt::format_to(output_iterator, "Stack trace:\n");
fmt::format_to(output_iterator, " Not supported.\n");
/* Not supported. */
}
#endif /* _MSC_VER */
@@ -430,7 +433,7 @@ static bool _expanded;
static const wchar_t _crash_desc[] =
L"A serious fault condition occurred in the game. The game will shut down.\n"
L"Please send crash.log, crash.dmp, and crash.sav to the developers.\n"
L"Please send crash.json.log, crash.dmp, and crash.sav to the developers.\n"
L"This will greatly help debugging.\n\n"
L"https://github.com/OpenTTD/OpenTTD/issues\n\n"
L"%s\n%s\n%s\n%s\n";
@@ -462,9 +465,10 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA
{
switch (msg) {
case WM_INITDIALOG: {
size_t crashlog_length = CrashLogWindows::current->crashlog.size() + 1;
std::string crashlog = CrashLogWindows::current->survey.dump(4);
size_t crashlog_length = crashlog.size() + 1;
/* Reserve extra space for LF to CRLF conversion. */
crashlog_length += std::count(CrashLogWindows::current->crashlog.begin(), CrashLogWindows::current->crashlog.end(), '\n');
crashlog_length += std::count(crashlog.begin(), crashlog.end(), '\n');
const size_t filename_count = 4;
const size_t filename_buf_length = MAX_PATH + 1;
@@ -486,7 +490,7 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA
char *crashlog_dos_nl = reinterpret_cast<char *>(filename_buf + filename_buf_length * filename_count);
/* Convert unix -> dos newlines because the edit box only supports that properly. */
const char *crashlog_unix_nl = CrashLogWindows::current->crashlog.data();
const char *crashlog_unix_nl = crashlog.data();
char *p = crashlog_dos_nl;
char32_t c;
while ((c = Utf8Consume(&crashlog_unix_nl))) {

View File

@@ -10,15 +10,13 @@
#include "../../stdafx.h"
#include "../../3rdparty/fmt/format.h"
#include "../../3rdparty/nlohmann/json.hpp"
#include "../../survey.h"
#include <thread>
#include <windows.h>
#include "../../safeguards.h"
extern std::string SurveyMemoryToText(uint64_t memory);
void SurveyOS(nlohmann::json &json)
{
_OSVERSIONINFOA os;