Add Crashpad Support on Windows (#4351)

* feat: crashpad on windows

* feat: inline it

* feat: more crashpad

* chore: remove qBreakpad

* fix: add mention to crashpad

* refactor: version string

* feat: add crashpad module

* refactor: build crashpad from source

* fix: minor adjustments

* chore: add changelog entry

* fix: formatting and include

* fix: format

* build: use flags similar to release profile

* ci: build with crashpad on windows

* ci: recurse submodules

* ci: always include crashpad

* fix: try 7z

for some reason zstd just doesn't run

* fix: wrong path for symbols

* fix: copy

pls don't build

* ci: use new cache key

* fix: missing pragma

* ci: add workflow without crashpad

* docs: elevate changelog entry

* fix: add link to cmake issue

* fix: windows include issue

Someone (crashpad) includes Windows.h before winsock2.h

* fix: working directory

* fix: another working directory
This commit is contained in:
nerix
2023-02-12 20:36:58 +01:00
committed by GitHub
parent 8e24a29c85
commit e377c30192
17 changed files with 426 additions and 26 deletions
+41 -9
View File
@@ -191,6 +191,8 @@ set(SOURCE_FILES
messages/search/SubtierPredicate.cpp
messages/search/SubtierPredicate.hpp
providers/Crashpad.cpp
providers/Crashpad.hpp
providers/IvrApi.cpp
providers/IvrApi.hpp
providers/LinkResolver.cpp
@@ -630,6 +632,22 @@ else()
)
endif()
# Set the output of TARGET to be
# - CMAKE_BIN_DIR/lib for libraries
# - CMAKE_BIN_DIR/bin for BINARIES
# an additional argument specifies the subdirectory.
function(set_target_directory_hierarchy TARGET)
set_target_properties(${TARGET}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${ARGV1}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${ARGV1}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
)
endfunction()
if (BUILD_APP)
if (APPLE)
add_executable(${EXECUTABLE_PROJECT} ${MACOS_BUNDLE_ICON_FILE} main.cpp)
@@ -642,15 +660,7 @@ if (BUILD_APP)
target_link_libraries(${EXECUTABLE_PROJECT} PUBLIC ${LIBRARY_PROJECT})
set_target_properties(${EXECUTABLE_PROJECT}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin"
)
set_target_directory_hierarchy(${EXECUTABLE_PROJECT})
if (WIN32)
if (NOT WINDEPLOYQT_PATH)
@@ -830,8 +840,29 @@ if (LIBRT)
)
endif ()
if (BUILD_WITH_CRASHPAD)
target_compile_definitions(${LIBRARY_PROJECT} PUBLIC CHATTERINO_WITH_CRASHPAD)
target_link_libraries(${LIBRARY_PROJECT} PUBLIC crashpad::client)
set_target_directory_hierarchy(crashpad_handler crashpad)
endif()
# Configure compiler warnings
if (MSVC)
# Change flags for RelWithDebInfo
# Default: "/debug /INCREMENTAL"
# Changes:
# - Disable incremental linking to reduce padding
# - Enable all optimizations - by default when /DEBUG is specified,
# these optimizations will be disabled. We need /DEBUG to generate a PDB.
# See https://gitlab.kitware.com/cmake/cmake/-/issues/20812 for more details.
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /INCREMENTAL:NO /OPT:REF,ICF,LBR")
# Use the function inlining level from 'Release' mode (2).
string(REPLACE "/Ob1" "/Ob2" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
# Configure warnings
# Someone adds /W3 before we add /W4.
# This makes sure, only /W4 is specified.
string(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
@@ -860,6 +891,7 @@ if (MSVC)
/wd4100
/wd4267
)
# Disable min/max macros from Windows.h
target_compile_definitions(${LIBRARY_PROJECT} PUBLIC NOMINMAX)
else ()
target_compile_options(${LIBRARY_PROJECT} PUBLIC
+1 -1
View File
@@ -162,7 +162,7 @@ namespace {
// true.
void initSignalHandler()
{
#ifdef NDEBUG
#if defined(NDEBUG) && !defined(CHATTERINO_WITH_CRASHPAD)
signalsInitTime = std::chrono::steady_clock::now();
signal(SIGSEGV, handleSignal);
+3
View File
@@ -100,6 +100,9 @@ QStringList Version::buildTags() const
#ifdef _MSC_FULL_VER
tags.append("MSVC " + QString::number(_MSC_FULL_VER, 10));
#endif
#ifdef CHATTERINO_WITH_CRASHPAD
tags.append("Crashpad");
#endif
return tags;
}
+5 -1
View File
@@ -4,6 +4,7 @@
#include "common/Modes.hpp"
#include "common/QLogging.hpp"
#include "common/Version.hpp"
#include "providers/Crashpad.hpp"
#include "providers/IvrApi.hpp"
#include "providers/NetworkConfigurationProvider.hpp"
#include "providers/twitch/api/Helix.hpp"
@@ -11,7 +12,6 @@
#include "singletons/Paths.hpp"
#include "singletons/Settings.hpp"
#include "util/AttachToConsole.hpp"
#include "util/IncognitoBrowser.hpp"
#include <QApplication>
#include <QCommandLineParser>
@@ -59,6 +59,10 @@ int main(int argc, char **argv)
initArgs(a);
#ifdef CHATTERINO_WITH_CRASHPAD
const auto crashpadHandler = installCrashHandler();
#endif
// run in gui mode or browser extension host mode
if (getArgs().shouldRunBrowserExtensionHost)
{
+95
View File
@@ -0,0 +1,95 @@
#ifdef CHATTERINO_WITH_CRASHPAD
# include "providers/Crashpad.hpp"
# include "common/QLogging.hpp"
# include "singletons/Paths.hpp"
# include <QApplication>
# include <QDir>
# include <QString>
# include <memory>
# include <string>
namespace {
/// The name of the crashpad handler executable.
/// This varies across platforms
# if defined(Q_OS_UNIX)
const QString CRASHPAD_EXECUTABLE_NAME = QStringLiteral("crashpad_handler");
# elif defined(Q_OS_WINDOWS)
const QString CRASHPAD_EXECUTABLE_NAME = QStringLiteral("crashpad_handler.exe");
# else
# error Unsupported platform
# endif
/// Converts a QString into the platform string representation.
# if defined(Q_OS_UNIX)
std::string nativeString(const QString &s)
{
return s.toStdString();
}
# elif defined(Q_OS_WINDOWS)
std::wstring nativeString(const QString &s)
{
return s.toStdWString();
}
# else
# error Unsupported platform
# endif
} // namespace
namespace chatterino {
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler()
{
// Currently, the following directory layout is assumed:
// [applicationDirPath]
// │
// ├─chatterino
// │
// ╰─[crashpad]
// │
// ╰─crashpad_handler
// TODO: The location of the binary might vary across platforms
auto crashpadBinDir = QDir(QApplication::applicationDirPath());
if (!crashpadBinDir.cd("crashpad"))
{
qCDebug(chatterinoApp) << "Cannot find crashpad directory";
return nullptr;
}
if (!crashpadBinDir.exists(CRASHPAD_EXECUTABLE_NAME))
{
qCDebug(chatterinoApp) << "Cannot find crashpad handler executable";
return nullptr;
}
const auto handlerPath = base::FilePath(nativeString(
crashpadBinDir.absoluteFilePath(CRASHPAD_EXECUTABLE_NAME)));
// Argument passed in --database
// > Crash reports are written to this database, and if uploads are enabled,
// uploaded from this database to a crash report collection server.
const auto databaseDir =
base::FilePath(nativeString(getPaths()->crashdumpDirectory));
auto client = std::make_unique<crashpad::CrashpadClient>();
// See https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/handler/crashpad_handler.md
// for documentation on available options.
if (!client->StartHandler(handlerPath, databaseDir, {}, {}, {}, {}, true,
false))
{
qCDebug(chatterinoApp) << "Failed to start crashpad handler";
return nullptr;
}
qCDebug(chatterinoApp) << "Started crashpad handler";
return client;
}
} // namespace chatterino
#endif
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#ifdef CHATTERINO_WITH_CRASHPAD
# include <client/crashpad_client.h>
# include <memory>
namespace chatterino {
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler();
} // namespace chatterino
#endif
@@ -3,7 +3,7 @@
#include "common/QLogging.hpp"
#include <QNetworkProxy>
#include <websocketpp/connection.hpp>
#include <websocketpp/error.hpp>
#include <string>
@@ -25,9 +25,7 @@ public:
*/
static void applyFromEnv(const Env &env);
template <class C>
static void applyToWebSocket(
const std::shared_ptr<websocketpp::connection<C>> &connection)
static void applyToWebSocket(const auto &connection)
{
const auto applicationProxy = QNetworkProxy::applicationProxy();
if (applicationProxy.type() != QNetworkProxy::HttpProxy)
+1
View File
@@ -141,6 +141,7 @@ void Paths::initSubDirectories()
this->messageLogDirectory = makePath("Logs");
this->miscDirectory = makePath("Misc");
this->twitchProfileAvatars = makePath("ProfileAvatars");
this->crashdumpDirectory = makePath("Crashes");
//QDir().mkdir(this->twitchProfileAvatars + "/twitch");
}
+3
View File
@@ -25,6 +25,9 @@ public:
// Directory for miscellaneous files. Same as <appDataDirectory>/Misc
QString miscDirectory;
// Directory for crashdumps. Same as <appDataDirectory>/Crashes
QString crashdumpDirectory;
// Hash of QCoreApplication::applicationFilePath()
QString applicationFilePathHash;
+5
View File
@@ -113,6 +113,11 @@ AboutPage::AboutPage()
addLicense(form.getElement(), "miniaudio",
"https://github.com/mackron/miniaudio",
":/licenses/miniaudio.txt");
#ifdef CHATTERINO_WITH_CRASHPAD
addLicense(form.getElement(), "sentry-crashpad",
"https://github.com/getsentry/crashpad",
":/licenses/crashpad.txt");
#endif
}
// Attributions