Skip to content

Commit

Permalink
Add: use breakpad to create crash.dmp on MacOS / Linux too
Browse files Browse the repository at this point in the history
Normally only the Windows platform could create a crash.dmp, making
analysing crash-reports from MacOS / Linux rather tricky.
  • Loading branch information
TrueBrain committed Aug 18, 2023
1 parent f2a9a30 commit 3687362
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 63 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ jobs:
${{ matrix.libraries }} \
zlib1g-dev \
# EOF
sudo vcpkg install \
breakpad \
# EOF
echo "::endgroup::"
env:
DEBIAN_FRONTEND: noninteractive
Expand Down Expand Up @@ -149,7 +153,7 @@ jobs:
cd build
echo "::group::CMake"
cmake .. ${{ matrix.extra-cmake-parameters }}
cmake .. -DCMAKE_TOOLCHAIN_FILE=/usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake ${{ matrix.extra-cmake-parameters }}
echo "::endgroup::"
echo "::group::Build"
Expand Down Expand Up @@ -205,6 +209,7 @@ jobs:
- name: Prepare vcpkg
run: |
vcpkg install --triplet=${{ matrix.arch }}-osx \
breakpad \
curl \
liblzma \
libpng \
Expand Down Expand Up @@ -290,6 +295,7 @@ jobs:
shell: bash
run: |
vcpkg install --triplet=${{ matrix.arch }}-windows-static \
breakpad \
liblzma \
libpng \
lzo \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ jobs:
ln -sf $(pwd)/installed/x64-linux/tools/python3/python3.[0-9][0-9] /usr/bin/python3
./vcpkg install \
breakpad \
curl[http2] \
fontconfig \
freetype \
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:
- name: Prepare vcpkg
run: |
vcpkg install \
breakpad:x64-osx \
breakpad:arm64-osx \
curl:x64-osx \
curl:arm64-osx \
liblzma:x64-osx \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
shell: bash
run: |
vcpkg install --triplet=${{ matrix.arch }}-windows-static \
breakpad \
liblzma \
libpng \
lzo \
Expand Down
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ else()
find_package(CURL)
endif()

# Breakpad doesn't support emscripten.
if(NOT EMSCRIPTEN)
find_package(unofficial-breakpad)
endif()

if(NOT OPTION_DEDICATED)
if(NOT WIN32)
find_package(Allegro)
Expand Down Expand Up @@ -310,6 +315,10 @@ if(NOT WIN32 AND NOT EMSCRIPTEN)
link_package(CURL ENCOURAGED)
endif()

if(NOT EMSCRIPTEN)
link_package(unofficial-breakpad TARGET unofficial::breakpad::libbreakpad_client ENCOURAGED)
endif()

if(NOT OPTION_DEDICATED)
link_package(Fluidsynth)
link_package(SDL)
Expand Down
2 changes: 1 addition & 1 deletion cmake/FindLZO.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ find_library(LZO_LIBRARY
# name as the optimized file. This is not always the case, but so far
# experiences has shown that in those case vcpkg CMake files do the right
# thing.
if(VCPKG_TOOLCHAIN AND LZO_LIBRARY)
if(VCPKG_TOOLCHAIN AND LZO_LIBRARY AND LZO_LIBRARY MATCHES "${VCPKG_INSTALLED_DIR}")
if(LZO_LIBRARY MATCHES "/debug/")
set(LZO_LIBRARY_DEBUG ${LZO_LIBRARY})
string(REPLACE "/debug/lib/" "/lib/" LZO_LIBRARY_RELEASE ${LZO_LIBRARY})
Expand Down
3 changes: 3 additions & 0 deletions cmake/LinkPackage.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ function(link_package NAME)

if(${NAME}_FOUND)
string(TOUPPER "${NAME}" UCNAME)
# Some libraries have a dash, which is not allowed in a preprocessor macro.
string(REPLACE "-" "_" UCNAME "${UCNAME}")

add_definitions(-DWITH_${UCNAME})
# Some libraries' cmake packages (looking at you, SDL2) leave trailing whitespace in the link commands,
# which (later) cmake considers to be an error. Work around this with by stripping the incoming string.
Expand Down
11 changes: 9 additions & 2 deletions src/crashlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,14 @@ bool CrashLog::WriteCrashLog()
return len == written;
}

/**
* Write the (crash) dump to a file.
*
* @note Sets \c crashdump_filename when there is a successful return.
* @return 1 iff the crashdump was successfully created, -1 if it failed, 0 if not implemented.
*/
/* virtual */ int CrashLog::WriteCrashDump()
{
/* Stub implementation; not all OSes support this. */
return 0;
}

Expand Down Expand Up @@ -458,13 +463,15 @@ bool CrashLog::MakeCrashLog()
ret = false;
}

/* Don't mention writing crash dumps because not all platforms support it. */
fmt::print("Writing crash dump to disk...\n");
int dret = this->WriteCrashDump();
if (dret < 0) {
fmt::print("Writing crash dump failed.\n\n");
ret = false;
} else if (dret > 0) {
fmt::print("Crash dump written to {}. Please add this file to any bug reports.\n\n", this->crashdump_filename);
} else {
fmt::print("Skipped; missing dependency to create crash dump.\n");
}

fmt::print("Writing crash savegame...\n");
Expand Down
6 changes: 0 additions & 6 deletions src/crashlog.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ class CrashLog {
void FillCrashLog(std::back_insert_iterator<std::string> &output_iterator) const;
bool WriteCrashLog();

/**
* Write the (crash) dump to a file.
* @note Sets \c crashdump_filename when there is a successful return.
* @return if less than 0, error. If 0 no dump is made, otherwise the dump
* was successful (not all OSes support dumping files).
*/
virtual int WriteCrashDump();
bool WriteSavegame();
bool WriteScreenshot();
Expand Down
25 changes: 23 additions & 2 deletions src/os/macosx/crashlog_osx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "../../stdafx.h"
#include "../../crashlog.h"
#include "../../fileio_func.h"
#include "../../string_func.h"
#include "../../gamelog.h"
#include "../../saveload/saveload.h"
Expand All @@ -20,6 +21,10 @@
#include <dlfcn.h>
#include <cxxabi.h>

#ifdef WITH_UNOFFICIAL_BREAKPAD
# include <client/mac/handler/exception_handler.h>
#endif

#include "../../safeguards.h"


Expand Down Expand Up @@ -139,6 +144,22 @@ class CrashLogOSX : public CrashLog {
fmt::format_to(output_iterator, "\n");
}

#ifdef WITH_UNOFFICIAL_BREAKPAD
static bool MinidumpCallback(const char* dump_dir, const char* minidump_id, void* context, bool succeeded)
{
CrashLogOSX *crashlog = reinterpret_cast<CrashLogOSX *>(context);

crashlog->crashdump_filename = crashlog->CreateFileName(".dmp");
std::rename(fmt::format("{}/{}.dmp", dump_dir, minidump_id).c_str(), crashlog->crashdump_filename.c_str());
return succeeded;
}

int WriteCrashDump() override
{
return google_breakpad::ExceptionHandler::WriteMinidump(_personal_dir, MinidumpCallback, this) ? 1 : -1;
}
#endif

public:
/**
* A crash log is always generated by signal.
Expand All @@ -155,8 +176,8 @@ class CrashLogOSX : public CrashLog {
std::string message = fmt::format(
"Please send the generated crash information and the last (auto)save to the developers. "
"This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues.\n\n"
"Generated file(s):\n{}\n{}\n{}",
this->crashlog_filename, this->savegame_filename, this->screenshot_filename);
"Generated file(s):\n{}\n{}\n{}\n{}",
this->crashlog_filename, this->crashdump_filename, this->savegame_filename, this->screenshot_filename);

ShowMacDialog(crash_title, message.c_str(), "Quit");
}
Expand Down
22 changes: 22 additions & 0 deletions src/os/unix/crashlog_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "../../stdafx.h"
#include "../../crashlog.h"
#include "../../fileio_func.h"
#include "../../string_func.h"
#include "../../gamelog.h"
#include "../../saveload/saveload.h"
Expand All @@ -28,6 +29,10 @@
#include <unistd.h>
#endif

#ifdef WITH_UNOFFICIAL_BREAKPAD
# include <client/linux/handler/exception_handler.h>
#endif

#include "../../safeguards.h"

/**
Expand Down Expand Up @@ -128,6 +133,23 @@ class CrashLogUnix : public CrashLog {
#endif
fmt::format_to(output_iterator, "\n");
}

#ifdef WITH_UNOFFICIAL_BREAKPAD
static bool MinidumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
{
CrashLogUnix *crashlog = reinterpret_cast<CrashLogUnix *>(context);

crashlog->crashdump_filename = crashlog->CreateFileName(".dmp");
std::rename(descriptor.path(), crashlog->crashdump_filename.c_str());
return succeeded;
}

int WriteCrashDump() override
{
return google_breakpad::ExceptionHandler::WriteMinidump(_personal_dir, MinidumpCallback, this) ? 1 : -1;
}
#endif

public:
/**
* A crash log is always generated by signal.
Expand Down
73 changes: 22 additions & 51 deletions src/os/windows/crashlog_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
#include <mmsystem.h>
#include <signal.h>
#include <psapi.h>
#include <dbghelp.h>

#ifdef WITH_UNOFFICIAL_BREAKPAD
# include <client/windows/handler/exception_handler.h>
#endif

#include "../../safeguards.h"

Expand All @@ -38,12 +43,24 @@ class CrashLogWindows : public CrashLog {
void LogRegisters(std::back_insert_iterator<std::string> &output_iterator) const override;
void LogModules(std::back_insert_iterator<std::string> &output_iterator) const override;
public:
#if defined(_MSC_VER)
int WriteCrashDump() override;

#ifdef WITH_UNOFFICIAL_BREAKPAD
static bool MinidumpCallback(const wchar_t *dump_dir, const wchar_t *minidump_id, void *context, EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool succeeded)
{
CrashLogWindows *crashlog = reinterpret_cast<CrashLogWindows *>(context);

crashlog->crashdump_filename = crashlog->CreateFileName(".dmp");
std::rename(fmt::format("{}/{}.dmp", FS2OTTD(dump_dir), FS2OTTD(minidump_id)).c_str(), crashlog->crashdump_filename.c_str());
return succeeded;
}

int WriteCrashDump() override
{
return google_breakpad::ExceptionHandler::WriteMinidump(OTTD2FS(_personal_dir), MinidumpCallback, this) ? 1 : -1;
}
#endif

void AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const;
#else
void AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const {}
#endif /* _MSC_VER */

/**
* A crash log is always generated when it's generated.
Expand Down Expand Up @@ -328,14 +345,9 @@ static void PrintModuleInfo(std::back_insert_iterator<std::string> &output_itera
fmt::format_to(output_iterator, "\n");
}

#if defined(_MSC_VER)
static const uint MAX_SYMBOL_LEN = 512;
static const uint MAX_FRAMES = 64;

#pragma warning(disable:4091)
#include <dbghelp.h>
#pragma warning(default:4091)

void CrashLogWindows::AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const
{
DllLoader dbghelp(L"dbghelp.dll");
Expand Down Expand Up @@ -449,47 +461,6 @@ void CrashLogWindows::AppendDecodedStacktrace(std::back_insert_iterator<std::str
fmt::format_to(output_iterator, "\n*** End of additional info ***\n");
}

/* virtual */ int CrashLogWindows::WriteCrashDump()
{
int ret = 0;
DllLoader dbghelp(L"dbghelp.dll");
if (dbghelp.Success()) {
typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE,
MINIDUMP_TYPE,
CONST PMINIDUMP_EXCEPTION_INFORMATION,
CONST PMINIDUMP_USER_STREAM_INFORMATION,
CONST PMINIDUMP_CALLBACK_INFORMATION);
MiniDumpWriteDump_t funcMiniDumpWriteDump = dbghelp.GetProcAddress("MiniDumpWriteDump");
if (funcMiniDumpWriteDump != nullptr) {
this->crashdump_filename = this->CreateFileName(".dmp");
HANDLE file = CreateFile(OTTD2FS(this->crashdump_filename).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0);
HANDLE proc = GetCurrentProcess();
DWORD procid = GetCurrentProcessId();
MINIDUMP_EXCEPTION_INFORMATION mdei;
MINIDUMP_USER_STREAM userstream;
MINIDUMP_USER_STREAM_INFORMATION musi;

userstream.Type = LastReservedStream + 1;
userstream.Buffer = (void*)this->crashlog.data();
userstream.BufferSize = (ULONG)this->crashlog.size() + 1;

musi.UserStreamCount = 1;
musi.UserStreamArray = &userstream;

mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = ep;
mdei.ClientPointers = false;

funcMiniDumpWriteDump(proc, procid, file, MiniDumpWithDataSegs, &mdei, &musi, nullptr);
ret = 1;
} else {
ret = -1;
}
}
return ret;
}
#endif /* _MSC_VER */

extern bool CloseConsoleLogIfActive();
static void ShowCrashlogWindow();

Expand Down

0 comments on commit 3687362

Please sign in to comment.