crash: add crash reporter
This commit is contained in:
parent
5040f3796c
commit
fe1d15e8f6
23 changed files with 1118 additions and 315 deletions
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: true
|
72
.github/ISSUE_TEMPLATE/crash.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/crash.yml
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
name: Crash Report
|
||||
description: Quickshell has crashed
|
||||
labels: ["bug", "crash"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: crashinfo
|
||||
attributes:
|
||||
label: General crash information
|
||||
description: |
|
||||
Paste the contents of the `info.txt` file in your crash folder here.
|
||||
value: "<details> <summary>General information</summary>
|
||||
|
||||
|
||||
```
|
||||
|
||||
<Paste the contents of the file here inside of the triple backticks>
|
||||
|
||||
```
|
||||
|
||||
|
||||
</details>"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: userinfo
|
||||
attributes:
|
||||
label: What caused the crash
|
||||
description: |
|
||||
Any information likely to help debug the crash. What were you doing when the crash occurred,
|
||||
what changes did you make, can you get it to happen again?
|
||||
- type: textarea
|
||||
id: dump
|
||||
attributes:
|
||||
label: Minidump
|
||||
description: |
|
||||
Attach `minidump.dmp` here. If it is too big to upload, compress it.
|
||||
|
||||
You may skip this step if quickshell crashed while processing a password
|
||||
or other sensitive information. If you skipped it write why instead.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Log file
|
||||
description: |
|
||||
Attach `log.qslog` here. If it is too big to upload, compress it.
|
||||
|
||||
You can preview the log if you'd like using `quickshell read-log <path-to-log>`.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: |
|
||||
Attach your configuration here, preferrably in full (not just one file).
|
||||
Compress it into a zip, tar, etc.
|
||||
|
||||
This will help us reproduce the crash ourselves.
|
||||
- type: textarea
|
||||
id: bt
|
||||
attributes:
|
||||
label: Backtrace
|
||||
description: |
|
||||
If you have gdb installed and use systemd, or otherwise know how to get a backtrace,
|
||||
we would appreciate one. (You may have gdb installed without knowing it)
|
||||
|
||||
1. Run `coredumpctl debug <pid>` where `pid` is the number shown after "Crashed process ID"
|
||||
in the crash reporter.
|
||||
2. Once it loads, type `bt -full` (then enter)
|
||||
3. Copy the output and attach it as a file or in a spoiler.
|
|
@ -9,6 +9,7 @@ option(BUILD_TESTING "Build tests" OFF)
|
|||
option(ASAN "Enable ASAN" OFF) # note: better output with gcc than clang
|
||||
option(FRAME_POINTERS "Always keep frame pointers" ${ASAN})
|
||||
|
||||
option(CRASH_REPORTER "Enable the crash reporter" ON)
|
||||
option(USE_JEMALLOC "Use jemalloc over the system malloc implementation" ON)
|
||||
option(SOCKETS "Enable unix socket support" ON)
|
||||
option(WAYLAND "Enable wayland support" ON)
|
||||
|
@ -29,6 +30,7 @@ option(SERVICE_UPOWER "UPower service" ON)
|
|||
option(SERVICE_NOTIFICATIONS "Notification server" ON)
|
||||
|
||||
message(STATUS "Quickshell configuration")
|
||||
message(STATUS " Crash reporter: ${CRASH_REPORTER}")
|
||||
message(STATUS " Jemalloc: ${USE_JEMALLOC}")
|
||||
message(STATUS " Build tests: ${BUILD_TESTING}")
|
||||
message(STATUS " Sockets: ${SOCKETS}")
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
ninja,
|
||||
qt6,
|
||||
cli11,
|
||||
breakpad,
|
||||
jemalloc,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
|
@ -28,6 +29,7 @@
|
|||
else "unknown"),
|
||||
|
||||
debug ? false,
|
||||
withCrashReporter ? true,
|
||||
withJemalloc ? true, # masks heap fragmentation
|
||||
withQtSvg ? true,
|
||||
withWayland ? true,
|
||||
|
@ -55,6 +57,7 @@
|
|||
qt6.qtdeclarative
|
||||
cli11
|
||||
]
|
||||
++ (lib.optional withCrashReporter breakpad)
|
||||
++ (lib.optional withJemalloc jemalloc)
|
||||
++ (lib.optional withQtSvg qt6.qtsvg)
|
||||
++ (lib.optionals withWayland [ qt6.qtwayland wayland ])
|
||||
|
@ -67,6 +70,7 @@
|
|||
cmakeBuildType = if debug then "Debug" else "RelWithDebInfo";
|
||||
|
||||
cmakeFlags = [ "-DGIT_REVISION=${gitRev}" ]
|
||||
++ lib.optional (!withCrashReporter) "-DCRASH_REPORTER=OFF"
|
||||
++ lib.optional (!withJemalloc) "-DUSE_JEMALLOC=OFF"
|
||||
++ lib.optional (!withWayland) "-DWAYLAND=OFF"
|
||||
++ lib.optional (!withPipewire) "-DSERVICE_PIPEWIRE=OFF"
|
||||
|
|
|
@ -5,6 +5,10 @@ install(TARGETS quickshell RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|||
add_subdirectory(core)
|
||||
add_subdirectory(io)
|
||||
|
||||
if (CRASH_REPORTER)
|
||||
add_subdirectory(crash)
|
||||
endif()
|
||||
|
||||
if (DBUS)
|
||||
add_subdirectory(dbus)
|
||||
endif()
|
||||
|
|
|
@ -41,9 +41,19 @@ qt_add_library(quickshell-core STATIC
|
|||
clock.cpp
|
||||
logging.cpp
|
||||
paths.cpp
|
||||
crashinfo.cpp
|
||||
common.cpp
|
||||
)
|
||||
|
||||
set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
|
||||
if (CRASH_REPORTER)
|
||||
set(CRASH_REPORTER_DEF 1)
|
||||
endif()
|
||||
|
||||
add_library(quickshell-build INTERFACE)
|
||||
configure_file(build.hpp.in build.hpp)
|
||||
target_include_directories(quickshell-build INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_link_libraries(quickshell-core PRIVATE quickshell-build)
|
||||
|
||||
qt_add_qml_module(quickshell-core URI Quickshell VERSION 0.1)
|
||||
|
||||
target_link_libraries(quickshell-core PRIVATE ${QT_DEPS} Qt6::QuickPrivate CLI11::CLI11)
|
||||
|
|
6
src/core/build.hpp.in
Normal file
6
src/core/build.hpp.in
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
// NOLINTBEGIN
|
||||
#define GIT_REVISION "@GIT_REVISION@"
|
||||
#define CRASH_REPORTER @CRASH_REPORTER_DEF@
|
||||
// NOLINTEND
|
9
src/core/common.cpp
Normal file
9
src/core/common.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "common.hpp"
|
||||
|
||||
#include <qdatetime.h>
|
||||
|
||||
namespace qs {
|
||||
|
||||
const QDateTime Common::LAUNCH_TIME = QDateTime::currentDateTime();
|
||||
|
||||
}
|
11
src/core/common.hpp
Normal file
11
src/core/common.hpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <qdatetime.h>
|
||||
|
||||
namespace qs {
|
||||
|
||||
struct Common {
|
||||
static const QDateTime LAUNCH_TIME;
|
||||
};
|
||||
|
||||
} // namespace qs
|
19
src/core/crashinfo.cpp
Normal file
19
src/core/crashinfo.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "crashinfo.hpp"
|
||||
|
||||
#include <qdatastream.h>
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
|
||||
stream << info.configPath << info.shellId << info.launchTime << info.noColor;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
|
||||
stream >> info.configPath >> info.shellId >> info.launchTime >> info.noColor;
|
||||
return stream;
|
||||
}
|
||||
|
||||
namespace qs::crash {
|
||||
|
||||
CrashInfo CrashInfo::INSTANCE = {}; // NOLINT
|
||||
|
||||
}
|
26
src/core/crashinfo.hpp
Normal file
26
src/core/crashinfo.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <qdatetime.h>
|
||||
#include <qstring.h>
|
||||
|
||||
struct InstanceInfo {
|
||||
QString configPath;
|
||||
QString shellId;
|
||||
QString initialWorkdir;
|
||||
QDateTime launchTime;
|
||||
bool noColor = false;
|
||||
bool sparseLogsOnly = false;
|
||||
};
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info);
|
||||
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info);
|
||||
|
||||
namespace qs::crash {
|
||||
|
||||
struct CrashInfo {
|
||||
int logFd = -1;
|
||||
|
||||
static CrashInfo INSTANCE; // NOLINT
|
||||
};
|
||||
|
||||
} // namespace qs::crash
|
|
@ -24,6 +24,7 @@
|
|||
#include <sys/mman.h>
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
#include "crashinfo.hpp"
|
||||
#include "logging_p.hpp"
|
||||
#include "logging_qtprivate.cpp" // NOLINT
|
||||
#include "paths.hpp"
|
||||
|
@ -198,14 +199,16 @@ void ThreadLogging::init() {
|
|||
|
||||
if (logMfd != -1) {
|
||||
this->file = new QFile();
|
||||
this->file->open(logMfd, QFile::WriteOnly, QFile::AutoCloseHandle);
|
||||
this->file->open(logMfd, QFile::ReadWrite, QFile::AutoCloseHandle);
|
||||
this->fileStream.setDevice(this->file);
|
||||
}
|
||||
|
||||
if (dlogMfd != -1) {
|
||||
crash::CrashInfo::INSTANCE.logFd = dlogMfd;
|
||||
|
||||
this->detailedFile = new QFile();
|
||||
// buffered by WriteBuffer
|
||||
this->detailedFile->open(dlogMfd, QFile::WriteOnly | QFile::Unbuffered, QFile::AutoCloseHandle);
|
||||
this->detailedFile->open(dlogMfd, QFile::ReadWrite | QFile::Unbuffered, QFile::AutoCloseHandle);
|
||||
this->detailedWriter.setDevice(this->detailedFile);
|
||||
|
||||
if (!this->detailedWriter.writeHeader()) {
|
||||
|
@ -245,7 +248,7 @@ void ThreadLogging::initFs() {
|
|||
auto* file = new QFile(path);
|
||||
auto* detailedFile = new QFile(detailedPath);
|
||||
|
||||
if (!file->open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
if (!file->open(QFile::ReadWrite | QFile::Truncate)) {
|
||||
qCCritical(logLogging
|
||||
) << "Could not start filesystem logger as the log file could not be created:"
|
||||
<< path;
|
||||
|
@ -256,7 +259,7 @@ void ThreadLogging::initFs() {
|
|||
}
|
||||
|
||||
// buffered by WriteBuffer
|
||||
if (!detailedFile->open(QFile::WriteOnly | QFile::Truncate | QFile::Unbuffered)) {
|
||||
if (!detailedFile->open(QFile::ReadWrite | QFile::Truncate | QFile::Unbuffered)) {
|
||||
qCCritical(logLogging
|
||||
) << "Could not start detailed filesystem logger as the log file could not be created:"
|
||||
<< detailedPath;
|
||||
|
@ -287,6 +290,8 @@ void ThreadLogging::initFs() {
|
|||
sendfile(detailedFile->handle(), oldFile->handle(), nullptr, oldFile->size());
|
||||
}
|
||||
|
||||
crash::CrashInfo::INSTANCE.logFd = detailedFile->handle();
|
||||
|
||||
this->detailedFile = detailedFile;
|
||||
this->detailedWriter.setDevice(detailedFile);
|
||||
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
#include "main.hpp"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/CLI.hpp> // NOLINT: Need to include this for impls of some CLI11 classes
|
||||
#include <CLI/Error.hpp>
|
||||
#include <CLI/Validators.hpp>
|
||||
#include <qapplication.h>
|
||||
#include <qcoreapplication.h>
|
||||
#include <qcryptographichash.h>
|
||||
#include <qdatastream.h>
|
||||
#include <qdatetime.h>
|
||||
#include <qdir.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qguiapplication.h>
|
||||
|
@ -24,162 +28,164 @@
|
|||
#include <qtextstream.h>
|
||||
#include <qtpreprocessorsupport.h>
|
||||
|
||||
#include "build.hpp"
|
||||
#include "common.hpp"
|
||||
#include "crashinfo.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "paths.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "rootwrapper.hpp"
|
||||
#if CRASH_REPORTER
|
||||
#include "../crash/handler.hpp"
|
||||
#include "../crash/main.hpp"
|
||||
#endif
|
||||
|
||||
int qs_main(int argc, char** argv) {
|
||||
struct CommandInfo {
|
||||
QString configPath;
|
||||
QString manifestPath;
|
||||
QString configName;
|
||||
QString& initialWorkdir;
|
||||
int& debugPort;
|
||||
bool& waitForDebug;
|
||||
bool& printInfo;
|
||||
bool& noColor;
|
||||
bool& sparseLogsOnly;
|
||||
};
|
||||
|
||||
auto qArgC = 1;
|
||||
auto* qArgV = argv;
|
||||
void processCommand(int argc, char** argv, CommandInfo& info) {
|
||||
auto app = CLI::App("");
|
||||
|
||||
auto noColor = !qEnvironmentVariableIsEmpty("NO_COLOR");
|
||||
class QStringOption {
|
||||
public:
|
||||
QStringOption() = default;
|
||||
QStringOption& operator=(const std::string& str) {
|
||||
this->str = QString::fromStdString(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString workingDirectory;
|
||||
QString configFilePath;
|
||||
QString shellId;
|
||||
auto printInfo = false;
|
||||
QString& operator*() { return this->str; }
|
||||
|
||||
auto debugPort = -1;
|
||||
auto waitForDebug = false;
|
||||
private:
|
||||
QString str;
|
||||
};
|
||||
|
||||
auto useQApplication = false;
|
||||
auto nativeTextRendering = false;
|
||||
auto desktopSettingsAware = true;
|
||||
QHash<QString, QString> envOverrides;
|
||||
class QStringRefOption {
|
||||
public:
|
||||
QStringRefOption(QString* str): str(str) {}
|
||||
QStringRefOption& operator=(const std::string& str) {
|
||||
*this->str = QString::fromStdString(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
{
|
||||
auto app = CLI::App("");
|
||||
private:
|
||||
QString* str;
|
||||
};
|
||||
|
||||
class QStringOption {
|
||||
public:
|
||||
QStringOption() = default;
|
||||
QStringOption& operator=(const std::string& str) {
|
||||
this->str = QString::fromStdString(str);
|
||||
return *this;
|
||||
}
|
||||
/// ---
|
||||
QStringRefOption path(&info.configPath);
|
||||
QStringRefOption manifest(&info.manifestPath);
|
||||
QStringRefOption config(&info.configName);
|
||||
QStringRefOption workdirRef(&info.initialWorkdir);
|
||||
|
||||
QString& operator*() { return this->str; }
|
||||
auto* selection = app.add_option_group(
|
||||
"Config Selection",
|
||||
"Select a configuration to run (defaults to $XDG_CONFIG_HOME/quickshell/shell.qml)"
|
||||
);
|
||||
|
||||
private:
|
||||
QString str;
|
||||
};
|
||||
auto* pathArg =
|
||||
selection->add_option("-p,--path", path, "Path to a QML file to run. (Env:QS_CONFIG_PATH)");
|
||||
|
||||
class QStringRefOption {
|
||||
public:
|
||||
QStringRefOption(QString* str): str(str) {}
|
||||
QStringRefOption& operator=(const std::string& str) {
|
||||
*this->str = QString::fromStdString(str);
|
||||
return *this;
|
||||
}
|
||||
auto* mfArg = selection->add_option(
|
||||
"-m,--manifest",
|
||||
manifest,
|
||||
"Path to a manifest containing configurations. (Env:QS_MANIFEST)\n"
|
||||
"(Defaults to $XDG_CONFIG_HOME/quickshell/manifest.conf)"
|
||||
);
|
||||
|
||||
private:
|
||||
QString* str;
|
||||
};
|
||||
auto* cfgArg = selection->add_option(
|
||||
"-c,--config",
|
||||
config,
|
||||
"Name of a configuration within a manifest. (Env:QS_CONFIG_NAME)"
|
||||
);
|
||||
|
||||
/// ---
|
||||
QStringOption path;
|
||||
QStringOption manifest;
|
||||
QStringOption config;
|
||||
QStringRefOption workdirRef(&workingDirectory);
|
||||
selection->add_option("-d,--workdir", workdirRef, "Initial working directory.");
|
||||
|
||||
auto* selection = app.add_option_group(
|
||||
"Config Selection",
|
||||
"Select a configuration to run (defaults to $XDG_CONFIG_HOME/quickshell/shell.qml)"
|
||||
);
|
||||
pathArg->excludes(mfArg, cfgArg);
|
||||
|
||||
auto* pathArg =
|
||||
selection->add_option("-p,--path", path, "Path to a QML file to run. (Env:QS_CONFIG_PATH)");
|
||||
/// ---
|
||||
auto* debug = app.add_option_group("Debugging");
|
||||
|
||||
auto* mfArg = selection->add_option(
|
||||
"-m,--manifest",
|
||||
manifest,
|
||||
"Path to a manifest containing configurations. (Env:QS_MANIFEST)\n"
|
||||
"(Defaults to $XDG_CONFIG_HOME/quickshell/manifest.conf)"
|
||||
);
|
||||
auto* debugPortArg = debug
|
||||
->add_option(
|
||||
"--debugport",
|
||||
info.debugPort,
|
||||
"Open the given port for a QML debugger to connect to."
|
||||
)
|
||||
->check(CLI::Range(0, 65535));
|
||||
|
||||
auto* cfgArg = selection->add_option(
|
||||
"-c,--config",
|
||||
config,
|
||||
"Name of a configuration within a manifest. (Env:QS_CONFIG_NAME)"
|
||||
);
|
||||
debug
|
||||
->add_flag(
|
||||
"--waitfordebug",
|
||||
info.waitForDebug,
|
||||
"Wait for a debugger to attach to the given port before launching."
|
||||
)
|
||||
->needs(debugPortArg);
|
||||
|
||||
selection->add_option("-d,--workdir", workdirRef, "Initial working directory.");
|
||||
/// ---
|
||||
app.add_flag("--info", info.printInfo, "Print information about the shell")
|
||||
->excludes(debugPortArg);
|
||||
app.add_flag("--no-color", info.noColor, "Do not color the log output. (Env:NO_COLOR)");
|
||||
auto* printVersion = app.add_flag("-V,--version", "Print quickshell's version, then exit.");
|
||||
|
||||
pathArg->excludes(mfArg, cfgArg);
|
||||
app.add_flag(
|
||||
"--no-detailed-logs",
|
||||
info.sparseLogsOnly,
|
||||
"Do not enable this unless you know what you are doing."
|
||||
);
|
||||
|
||||
/// ---
|
||||
auto* debug = app.add_option_group("Debugging");
|
||||
/// ---
|
||||
QStringOption logPath;
|
||||
QStringOption logFilter;
|
||||
auto logNoTime = false;
|
||||
|
||||
auto* debugPortArg = debug
|
||||
->add_option(
|
||||
"--debugport",
|
||||
debugPort,
|
||||
"Open the given port for a QML debugger to connect to."
|
||||
)
|
||||
->check(CLI::Range(0, 65535));
|
||||
auto* readLog = app.add_subcommand("read-log", "Read a quickshell log file.");
|
||||
readLog->add_option("path", logPath, "Path to the log file to read")->required();
|
||||
|
||||
debug
|
||||
->add_flag(
|
||||
"--waitfordebug",
|
||||
waitForDebug,
|
||||
"Wait for a debugger to attach to the given port before launching."
|
||||
)
|
||||
->needs(debugPortArg);
|
||||
readLog->add_option(
|
||||
"-f,--filter",
|
||||
logFilter,
|
||||
"Logging categories to display. (same syntax as QT_LOGGING_RULES)"
|
||||
);
|
||||
|
||||
/// ---
|
||||
auto sparseLogsOnly = false;
|
||||
app.add_flag("--info", printInfo, "Print information about the shell")->excludes(debugPortArg);
|
||||
app.add_flag("--no-color", noColor, "Do not color the log output. (Env:NO_COLOR)");
|
||||
auto* printVersion = app.add_flag("-V,--version", "Print quickshell's version, then exit.");
|
||||
readLog->add_flag("--no-time", logNoTime, "Do not print timestamps of log messages.");
|
||||
readLog->add_flag("--no-color", info.noColor, "Do not color the log output. (Env:NO_COLOR)");
|
||||
|
||||
app.add_flag(
|
||||
"--no-detailed-logs",
|
||||
sparseLogsOnly,
|
||||
"Do not enable this unless you know what you are doing."
|
||||
);
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch (const CLI::ParseError& e) {
|
||||
exit(app.exit(e)); // NOLINT
|
||||
};
|
||||
|
||||
/// ---
|
||||
QStringOption logPath;
|
||||
QStringOption logFilter;
|
||||
auto logNoTime = false;
|
||||
|
||||
auto* readLog = app.add_subcommand("read-log", "Read a quickshell log file.");
|
||||
readLog->add_option("path", logPath, "Path to the log file to read")->required();
|
||||
|
||||
readLog->add_option(
|
||||
"-f,--filter",
|
||||
logFilter,
|
||||
"Logging categories to display. (same syntax as QT_LOGGING_RULES)"
|
||||
);
|
||||
|
||||
readLog->add_flag("--no-time", logNoTime, "Do not print timestamps of log messages.");
|
||||
readLog->add_flag("--no-color", noColor, "Do not color the log output. (Env:NO_COLOR)");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
const auto qApplication = QCoreApplication(qArgC, qArgV);
|
||||
|
||||
// Start log manager - has to happen with an active event loop or offthread can't be started.
|
||||
LogManager::init(!noColor, sparseLogsOnly);
|
||||
|
||||
if (*printVersion) {
|
||||
std::cout << "quickshell pre-release, revision: " << GIT_REVISION << std::endl;
|
||||
return 0;
|
||||
} if (*readLog) {
|
||||
auto file = QFile(*logPath);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
qCritical() << "Failed to open log for reading:" << *logPath;
|
||||
return -1;
|
||||
} else {
|
||||
qInfo() << "Reading log" << *logPath;
|
||||
}
|
||||
|
||||
return qs::log::readEncodedLogs(&file, !logNoTime, *logFilter) ? 0 : -1;
|
||||
if (*printVersion) {
|
||||
std::cout << "quickshell pre-release, revision: " << GIT_REVISION << std::endl;
|
||||
exit(0); // NOLINT
|
||||
} else if (*readLog) {
|
||||
auto file = QFile(*logPath);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
qCritical() << "Failed to open log for reading:" << *logPath;
|
||||
exit(-1); // NOLINT
|
||||
} else {
|
||||
qInfo() << "Reading log" << *logPath;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN
|
||||
exit( // NOLINT
|
||||
qs::log::readEncodedLogs(&file, !logNoTime, *logFilter) ? 0 : -1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QString commandConfigPath(QString path, QString manifest, QString config, bool printInfo) {
|
||||
// NOLINTBEGIN
|
||||
#define CHECK(rname, name, level, label, expr) \
|
||||
QString name = expr; \
|
||||
if (rname.isEmpty() && !name.isEmpty()) { \
|
||||
|
@ -189,231 +195,341 @@ int qs_main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
#define OPTSTR(name) (name.isEmpty() ? "(unset)" : name.toStdString())
|
||||
// NOLINTEND
|
||||
// NOLINTEND
|
||||
|
||||
QString basePath;
|
||||
int basePathLevel = 0;
|
||||
Q_UNUSED(basePathLevel);
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
// clang-format off
|
||||
CHECK(basePath, envBasePath, 0, foundbase, qEnvironmentVariable("QS_BASE_PATH"));
|
||||
CHECK(basePath, defaultBasePath, 0, foundbase, QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).filePath("quickshell"));
|
||||
// clang-format on
|
||||
// NOLINTEND
|
||||
QString basePath;
|
||||
int basePathLevel = 0;
|
||||
Q_UNUSED(basePathLevel);
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
// clang-format off
|
||||
CHECK(basePath, envBasePath, 0, foundbase, qEnvironmentVariable("QS_BASE_PATH"));
|
||||
CHECK(basePath, defaultBasePath, 0, foundbase, QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).filePath("quickshell"));
|
||||
// clang-format on
|
||||
// NOLINTEND
|
||||
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "Base path: " << OPTSTR(basePath) << "\n";
|
||||
std::cout << " - Environment (QS_BASE_PATH): " << OPTSTR(envBasePath) << "\n";
|
||||
std::cout << " - Default: " << OPTSTR(defaultBasePath) << "\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundbase:;
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "Base path: " << OPTSTR(basePath) << "\n";
|
||||
std::cout << " - Environment (QS_BASE_PATH): " << OPTSTR(envBasePath) << "\n";
|
||||
std::cout << " - Default: " << OPTSTR(defaultBasePath) << "\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundbase:;
|
||||
|
||||
QString configPath;
|
||||
int configPathLevel = 10;
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
CHECK(configPath, optionConfigPath, 0, foundpath, *path);
|
||||
CHECK(configPath, envConfigPath, 1, foundpath, qEnvironmentVariable("QS_CONFIG_PATH"));
|
||||
// NOLINTEND
|
||||
QString configPath;
|
||||
int configPathLevel = 10;
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
CHECK(configPath, optionConfigPath, 0, foundpath, path);
|
||||
CHECK(configPath, envConfigPath, 1, foundpath, qEnvironmentVariable("QS_CONFIG_PATH"));
|
||||
// NOLINTEND
|
||||
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "\nConfig path: " << OPTSTR(configPath) << "\n";
|
||||
std::cout << " - Option: " << OPTSTR(optionConfigPath) << "\n";
|
||||
std::cout << " - Environment (QS_CONFIG_PATH): " << OPTSTR(envConfigPath) << "\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundpath:;
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "\nConfig path: " << OPTSTR(configPath) << "\n";
|
||||
std::cout << " - Option: " << OPTSTR(optionConfigPath) << "\n";
|
||||
std::cout << " - Environment (QS_CONFIG_PATH): " << OPTSTR(envConfigPath) << "\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundpath:;
|
||||
|
||||
QString manifestPath;
|
||||
int manifestPathLevel = 10;
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
// clang-format off
|
||||
CHECK(manifestPath, optionManifestPath, 0, foundmf, *manifest);
|
||||
CHECK(manifestPath, envManifestPath, 1, foundmf, qEnvironmentVariable("QS_MANIFEST"));
|
||||
CHECK(manifestPath, defaultManifestPath, 2, foundmf, QDir(basePath).filePath("manifest.conf"));
|
||||
// clang-format on
|
||||
// NOLINTEND
|
||||
QString manifestPath;
|
||||
int manifestPathLevel = 10;
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
// clang-format off
|
||||
CHECK(manifestPath, optionManifestPath, 0, foundmf, manifest);
|
||||
CHECK(manifestPath, envManifestPath, 1, foundmf, qEnvironmentVariable("QS_MANIFEST"));
|
||||
CHECK(manifestPath, defaultManifestPath, 2, foundmf, QDir(basePath).filePath("manifest.conf"));
|
||||
// clang-format on
|
||||
// NOLINTEND
|
||||
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "\nManifest path: " << OPTSTR(manifestPath) << "\n";
|
||||
std::cout << " - Option: " << OPTSTR(optionManifestPath) << "\n";
|
||||
std::cout << " - Environment (QS_MANIFEST): " << OPTSTR(envManifestPath) << "\n";
|
||||
std::cout << " - Default: " << OPTSTR(defaultManifestPath) << "\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundmf:;
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "\nManifest path: " << OPTSTR(manifestPath) << "\n";
|
||||
std::cout << " - Option: " << OPTSTR(optionManifestPath) << "\n";
|
||||
std::cout << " - Environment (QS_MANIFEST): " << OPTSTR(envManifestPath) << "\n";
|
||||
std::cout << " - Default: " << OPTSTR(defaultManifestPath) << "\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundmf:;
|
||||
|
||||
QString configName;
|
||||
int configNameLevel = 10;
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
CHECK(configName, optionConfigName, 0, foundname, *config);
|
||||
CHECK(configName, envConfigName, 1, foundname, qEnvironmentVariable("QS_CONFIG_NAME"));
|
||||
// NOLINTEND
|
||||
QString configName;
|
||||
int configNameLevel = 10;
|
||||
{
|
||||
// NOLINTBEGIN
|
||||
CHECK(configName, optionConfigName, 0, foundname, config);
|
||||
CHECK(configName, envConfigName, 1, foundname, qEnvironmentVariable("QS_CONFIG_NAME"));
|
||||
// NOLINTEND
|
||||
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "\nConfig name: " << OPTSTR(configName) << "\n";
|
||||
std::cout << " - Option: " << OPTSTR(optionConfigName) << "\n";
|
||||
std::cout << " - Environment (QS_CONFIG_NAME): " << OPTSTR(envConfigName) << "\n\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundname:;
|
||||
if (printInfo) {
|
||||
// clang-format off
|
||||
std::cout << "\nConfig name: " << OPTSTR(configName) << "\n";
|
||||
std::cout << " - Option: " << OPTSTR(optionConfigName) << "\n";
|
||||
std::cout << " - Environment (QS_CONFIG_NAME): " << OPTSTR(envConfigName) << "\n\n";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
foundname:;
|
||||
|
||||
if (!configPath.isEmpty() && configPathLevel <= configNameLevel) {
|
||||
configFilePath = configPath;
|
||||
} else if (!configName.isEmpty()) {
|
||||
if (!manifestPath.isEmpty()) {
|
||||
auto file = QFile(manifestPath);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
auto stream = QTextStream(&file);
|
||||
while (!stream.atEnd()) {
|
||||
auto line = stream.readLine();
|
||||
if (line.trimmed().startsWith("#")) continue;
|
||||
if (line.trimmed().isEmpty()) continue;
|
||||
QString configFilePath;
|
||||
|
||||
auto split = line.split('=');
|
||||
if (split.length() != 2) {
|
||||
qCritical() << "manifest line not in expected format 'name = relativepath':"
|
||||
<< line;
|
||||
return -1;
|
||||
}
|
||||
if (!configPath.isEmpty() && configPathLevel <= configNameLevel) {
|
||||
configFilePath = configPath;
|
||||
} else if (!configName.isEmpty()) {
|
||||
if (!manifestPath.isEmpty()) {
|
||||
auto file = QFile(manifestPath);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
auto stream = QTextStream(&file);
|
||||
while (!stream.atEnd()) {
|
||||
auto line = stream.readLine();
|
||||
if (line.trimmed().startsWith("#")) continue;
|
||||
if (line.trimmed().isEmpty()) continue;
|
||||
|
||||
if (split[0].trimmed() == configName) {
|
||||
configFilePath = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed());
|
||||
goto haspath; // NOLINT
|
||||
}
|
||||
}
|
||||
auto split = line.split('=');
|
||||
if (split.length() != 2) {
|
||||
qCritical() << "manifest line not in expected format 'name = relativepath':" << line;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
|
||||
qCritical() << "configuration" << configName << "not found in manifest" << manifestPath;
|
||||
return -1;
|
||||
} else if (manifestPathLevel < 2) {
|
||||
qCritical() << "cannot open config manifest at" << manifestPath;
|
||||
return -1;
|
||||
if (split[0].trimmed() == configName) {
|
||||
configFilePath = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed());
|
||||
goto foundp;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto basePathInfo = QFileInfo(basePath);
|
||||
if (!basePathInfo.exists()) {
|
||||
qCritical() << "base path does not exist:" << basePath;
|
||||
return -1;
|
||||
} else if (!QFileInfo(basePathInfo.canonicalFilePath()).isDir()) {
|
||||
qCritical() << "base path is not a directory" << basePath;
|
||||
return -1;
|
||||
}
|
||||
qCritical() << "configuration" << configName << "not found in manifest" << manifestPath;
|
||||
exit(-1); // NOLINT
|
||||
} else if (manifestPathLevel < 2) {
|
||||
qCritical() << "cannot open config manifest at" << manifestPath;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
auto dir = QDir(basePath);
|
||||
for (auto& entry: dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
|
||||
if (entry == configName) {
|
||||
configFilePath = dir.filePath(entry);
|
||||
goto haspath; // NOLINT
|
||||
}
|
||||
}
|
||||
{
|
||||
auto basePathInfo = QFileInfo(basePath);
|
||||
if (!basePathInfo.exists()) {
|
||||
qCritical() << "base path does not exist:" << basePath;
|
||||
exit(-1); // NOLINT
|
||||
} else if (!QFileInfo(basePathInfo.canonicalFilePath()).isDir()) {
|
||||
qCritical() << "base path is not a directory" << basePath;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
|
||||
qCritical() << "no directory named " << configName << "found in base path" << basePath;
|
||||
return -1;
|
||||
auto dir = QDir(basePath);
|
||||
for (auto& entry: dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
|
||||
if (entry == configName) {
|
||||
configFilePath = dir.filePath(entry);
|
||||
goto foundp;
|
||||
}
|
||||
haspath:;
|
||||
} else {
|
||||
configFilePath = basePath;
|
||||
}
|
||||
|
||||
auto configFile = QFileInfo(configFilePath);
|
||||
if (!configFile.exists()) {
|
||||
qCritical() << "config path does not exist:" << configFilePath;
|
||||
return -1;
|
||||
}
|
||||
qCritical() << "no directory named " << configName << "found in base path" << basePath;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
} else {
|
||||
configFilePath = basePath;
|
||||
}
|
||||
|
||||
if (configFile.isDir()) {
|
||||
configFilePath = QDir(configFilePath).filePath("shell.qml");
|
||||
}
|
||||
foundp:;
|
||||
auto configFile = QFileInfo(configFilePath);
|
||||
if (!configFile.exists()) {
|
||||
qCritical() << "config path does not exist:" << configFilePath;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
|
||||
configFile = QFileInfo(configFilePath);
|
||||
if (!configFile.exists()) {
|
||||
qCritical() << "no shell.qml found in config path:" << configFilePath;
|
||||
return -1;
|
||||
} else if (configFile.isDir()) {
|
||||
qCritical() << "shell.qml is a directory:" << configFilePath;
|
||||
return -1;
|
||||
}
|
||||
if (configFile.isDir()) {
|
||||
configFilePath = QDir(configFilePath).filePath("shell.qml");
|
||||
}
|
||||
|
||||
configFilePath = QFileInfo(configFilePath).canonicalFilePath();
|
||||
configFile = QFileInfo(configFilePath);
|
||||
if (!configFile.exists()) {
|
||||
qCritical() << "config file does not exist:" << configFilePath;
|
||||
return -1;
|
||||
} else if (configFile.isDir()) {
|
||||
qCritical() << "config file is a directory:" << configFilePath;
|
||||
return -1;
|
||||
}
|
||||
configFile = QFileInfo(configFilePath);
|
||||
if (!configFile.exists()) {
|
||||
qCritical() << "no shell.qml found in config path:" << configFilePath;
|
||||
exit(-1); // NOLINT
|
||||
} else if (configFile.isDir()) {
|
||||
qCritical() << "shell.qml is a directory:" << configFilePath;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
|
||||
configFilePath = QFileInfo(configFilePath).canonicalFilePath();
|
||||
configFile = QFileInfo(configFilePath);
|
||||
if (!configFile.exists()) {
|
||||
qCritical() << "config file does not exist:" << configFilePath;
|
||||
exit(-1); // NOLINT
|
||||
} else if (configFile.isDir()) {
|
||||
qCritical() << "config file is a directory:" << configFilePath;
|
||||
exit(-1); // NOLINT
|
||||
}
|
||||
|
||||
#undef CHECK
|
||||
#undef OPTSTR
|
||||
|
||||
shellId = QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||
return configFilePath;
|
||||
}
|
||||
|
||||
qInfo() << "Config file path:" << configFilePath;
|
||||
int qs_main(int argc, char** argv) {
|
||||
#if CRASH_REPORTER
|
||||
qsCheckCrash(argc, argv);
|
||||
auto crashHandler = qs::crash::CrashHandler();
|
||||
#endif
|
||||
|
||||
if (!QFile(configFilePath).exists()) {
|
||||
qCritical() << "config file does not exist";
|
||||
return -1;
|
||||
auto qArgC = 1;
|
||||
auto* qArgV = argv;
|
||||
|
||||
QString configFilePath;
|
||||
QString initialWorkdir;
|
||||
QString shellId;
|
||||
|
||||
int debugPort = -1;
|
||||
bool waitForDebug = false;
|
||||
bool printInfo = false;
|
||||
bool noColor = !qEnvironmentVariableIsEmpty("NO_COLOR");
|
||||
bool sparseLogsOnly = false;
|
||||
|
||||
auto useQApplication = false;
|
||||
auto nativeTextRendering = false;
|
||||
auto desktopSettingsAware = true;
|
||||
QHash<QString, QString> envOverrides;
|
||||
|
||||
{
|
||||
const auto qApplication = QCoreApplication(qArgC, qArgV);
|
||||
|
||||
#if CRASH_REPORTER
|
||||
auto lastInfoFdStr = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD");
|
||||
|
||||
if (!lastInfoFdStr.isEmpty()) {
|
||||
auto lastInfoFd = lastInfoFdStr.toInt();
|
||||
|
||||
QFile file;
|
||||
file.open(lastInfoFd, QFile::ReadOnly, QFile::AutoCloseHandle);
|
||||
file.seek(0);
|
||||
|
||||
auto ds = QDataStream(&file);
|
||||
InstanceInfo info;
|
||||
ds >> info;
|
||||
|
||||
configFilePath = info.configPath;
|
||||
initialWorkdir = info.initialWorkdir;
|
||||
noColor = info.noColor;
|
||||
sparseLogsOnly = info.sparseLogsOnly;
|
||||
|
||||
LogManager::init(!noColor, sparseLogsOnly);
|
||||
|
||||
qCritical().nospace() << "Quickshell has crashed under pid "
|
||||
<< qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt()
|
||||
<< " (Coredumps will be available under that pid.)";
|
||||
|
||||
qCritical() << "Further crash information is stored under"
|
||||
<< QsPaths::crashDir(info.shellId, info.launchTime).path();
|
||||
|
||||
if (info.launchTime.msecsTo(QDateTime::currentDateTime()) < 10000) {
|
||||
qCritical() << "Quickshell crashed within 10 seconds of launching. Not restarting to avoid "
|
||||
"a crash loop.";
|
||||
return 0;
|
||||
} else {
|
||||
qCritical() << "Quickshell has been restarted.";
|
||||
}
|
||||
|
||||
auto file = QFile(configFilePath);
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
qCritical() << "could not open config file";
|
||||
return -1;
|
||||
}
|
||||
crashHandler.init();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
||||
auto stream = QTextStream(&file);
|
||||
while (!stream.atEnd()) {
|
||||
auto line = stream.readLine().trimmed();
|
||||
if (line.startsWith("//@ pragma ")) {
|
||||
auto pragma = line.sliced(11).trimmed();
|
||||
auto command = CommandInfo {
|
||||
.initialWorkdir = initialWorkdir,
|
||||
.debugPort = debugPort,
|
||||
.waitForDebug = waitForDebug,
|
||||
.printInfo = printInfo,
|
||||
.noColor = noColor,
|
||||
.sparseLogsOnly = sparseLogsOnly,
|
||||
};
|
||||
|
||||
if (pragma == "UseQApplication") useQApplication = true;
|
||||
else if (pragma == "NativeTextRendering") nativeTextRendering = true;
|
||||
else if (pragma == "IgnoreSystemSettings") desktopSettingsAware = false;
|
||||
else if (pragma.startsWith("Env ")) {
|
||||
auto envPragma = pragma.sliced(4);
|
||||
auto splitIdx = envPragma.indexOf('=');
|
||||
processCommand(argc, argv, command);
|
||||
|
||||
if (splitIdx == -1) {
|
||||
qCritical() << "Env pragma" << pragma << "not in the form 'VAR = VALUE'";
|
||||
return -1;
|
||||
}
|
||||
// Start log manager - has to happen with an active event loop or offthread can't be started.
|
||||
LogManager::init(!noColor, sparseLogsOnly);
|
||||
|
||||
auto var = envPragma.sliced(0, splitIdx).trimmed();
|
||||
auto val = envPragma.sliced(splitIdx + 1).trimmed();
|
||||
envOverrides.insert(var, val);
|
||||
} else if (pragma.startsWith("ShellId ")) {
|
||||
shellId = pragma.sliced(8).trimmed();
|
||||
} else {
|
||||
qCritical() << "Unrecognized pragma" << pragma;
|
||||
#if CRASH_REPORTER
|
||||
// Started after log manager for pretty debug logs. Unlikely anything will crash before this point, but
|
||||
// this can be moved if it happens.
|
||||
crashHandler.init();
|
||||
#endif
|
||||
|
||||
configFilePath = commandConfigPath(
|
||||
command.configPath,
|
||||
command.manifestPath,
|
||||
command.configName,
|
||||
command.printInfo
|
||||
);
|
||||
}
|
||||
|
||||
shellId = QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||
|
||||
qInfo() << "Config file path:" << configFilePath;
|
||||
|
||||
if (!QFile(configFilePath).exists()) {
|
||||
qCritical() << "config file does not exist";
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto file = QFile(configFilePath);
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
qCritical() << "could not open config file";
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto stream = QTextStream(&file);
|
||||
while (!stream.atEnd()) {
|
||||
auto line = stream.readLine().trimmed();
|
||||
if (line.startsWith("//@ pragma ")) {
|
||||
auto pragma = line.sliced(11).trimmed();
|
||||
|
||||
if (pragma == "UseQApplication") useQApplication = true;
|
||||
else if (pragma == "NativeTextRendering") nativeTextRendering = true;
|
||||
else if (pragma == "IgnoreSystemSettings") desktopSettingsAware = false;
|
||||
else if (pragma.startsWith("Env ")) {
|
||||
auto envPragma = pragma.sliced(4);
|
||||
auto splitIdx = envPragma.indexOf('=');
|
||||
|
||||
if (splitIdx == -1) {
|
||||
qCritical() << "Env pragma" << pragma << "not in the form 'VAR = VALUE'";
|
||||
return -1;
|
||||
}
|
||||
} else if (line.startsWith("import")) break;
|
||||
}
|
||||
|
||||
file.close();
|
||||
auto var = envPragma.sliced(0, splitIdx).trimmed();
|
||||
auto val = envPragma.sliced(splitIdx + 1).trimmed();
|
||||
envOverrides.insert(var, val);
|
||||
} else if (pragma.startsWith("ShellId ")) {
|
||||
shellId = pragma.sliced(8).trimmed();
|
||||
} else {
|
||||
qCritical() << "Unrecognized pragma" << pragma;
|
||||
return -1;
|
||||
}
|
||||
} else if (line.startsWith("import")) break;
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
qInfo() << "Shell ID:" << shellId;
|
||||
|
||||
if (printInfo) return 0;
|
||||
|
||||
#if CRASH_REPORTER
|
||||
crashHandler.setInstanceInfo(InstanceInfo {
|
||||
.configPath = configFilePath,
|
||||
.shellId = shellId,
|
||||
.initialWorkdir = initialWorkdir,
|
||||
.launchTime = qs::Common::LAUNCH_TIME,
|
||||
.noColor = noColor,
|
||||
.sparseLogsOnly = sparseLogsOnly,
|
||||
});
|
||||
#endif
|
||||
|
||||
for (auto [var, val]: envOverrides.asKeyValueRange()) {
|
||||
qputenv(var.toUtf8(), val.toUtf8());
|
||||
}
|
||||
|
@ -484,8 +600,8 @@ int qs_main(int argc, char** argv) {
|
|||
QQmlDebuggingEnabler::startTcpDebugServer(debugPort, wait);
|
||||
}
|
||||
|
||||
if (!workingDirectory.isEmpty()) {
|
||||
QDir::setCurrent(workingDirectory);
|
||||
if (!initialWorkdir.isEmpty()) {
|
||||
QDir::setCurrent(initialWorkdir);
|
||||
}
|
||||
|
||||
QuickshellPlugin::initPlugins();
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <qtenvironmentvariables.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg);
|
||||
|
||||
QsPaths* QsPaths::instance() {
|
||||
|
@ -18,6 +20,15 @@ QsPaths* QsPaths::instance() {
|
|||
|
||||
void QsPaths::init(QString shellId) { QsPaths::instance()->shellId = std::move(shellId); }
|
||||
|
||||
QDir QsPaths::crashDir(const QString& shellId, const QDateTime& launchTime) {
|
||||
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
dir = QDir(dir.filePath("crashes"));
|
||||
dir = QDir(dir.filePath(shellId));
|
||||
dir = QDir(dir.filePath(QString("run-%1").arg(launchTime.toMSecsSinceEpoch())));
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
QDir* QsPaths::cacheDir() {
|
||||
if (this->cacheState == DirState::Unknown) {
|
||||
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
|
@ -73,7 +84,7 @@ QDir* QsPaths::instanceRunDir() {
|
|||
this->instanceRunState = DirState::Failed;
|
||||
} else {
|
||||
this->mInstanceRunDir =
|
||||
runtimeDir->filePath(QString("run-%1").arg(QDateTime::currentMSecsSinceEpoch()));
|
||||
runtimeDir->filePath(QString("run-%1").arg(qs::Common::LAUNCH_TIME.toMSecsSinceEpoch()));
|
||||
|
||||
qCDebug(logPaths) << "Initialized instance runtime path:" << this->mInstanceRunDir.path();
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
#include <qdatetime.h>
|
||||
#include <qdir.h>
|
||||
|
||||
class QsPaths {
|
||||
public:
|
||||
static QsPaths* instance();
|
||||
static void init(QString shellId);
|
||||
static QDir crashDir(const QString& shellId, const QDateTime& launchTime);
|
||||
|
||||
QDir* cacheDir();
|
||||
QDir* runDir();
|
||||
|
|
16
src/crash/CMakeLists.txt
Normal file
16
src/crash/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
qt_add_library(quickshell-crash STATIC
|
||||
main.cpp
|
||||
interface.cpp
|
||||
handler.cpp
|
||||
)
|
||||
|
||||
qs_pch(quickshell-crash)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(breakpad REQUIRED IMPORTED_TARGET breakpad)
|
||||
# only need client?? take only includes from pkg config todo
|
||||
target_link_libraries(quickshell-crash PRIVATE PkgConfig::breakpad -lbreakpad_client)
|
||||
|
||||
target_link_libraries(quickshell-crash PRIVATE quickshell-build Qt6::Widgets)
|
||||
|
||||
target_link_libraries(quickshell-core PRIVATE quickshell-crash)
|
180
src/crash/handler.cpp
Normal file
180
src/crash/handler.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
#include "handler.hpp"
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <bits/types/sigset_t.h>
|
||||
#include <breakpad/client/linux/handler/exception_handler.h>
|
||||
#include <breakpad/client/linux/handler/minidump_descriptor.h>
|
||||
#include <breakpad/common/linux/linux_libc_support.h>
|
||||
#include <qdatastream.h>
|
||||
#include <qfile.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../core/crashinfo.hpp"
|
||||
|
||||
extern char** environ; // NOLINT
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace qs::crash {
|
||||
|
||||
Q_LOGGING_CATEGORY(logCrashHandler, "quickshell.crashhandler", QtWarningMsg);
|
||||
|
||||
struct CrashHandlerPrivate {
|
||||
ExceptionHandler* exceptionHandler = nullptr;
|
||||
int minidumpFd = -1;
|
||||
int infoFd = -1;
|
||||
|
||||
static bool minidumpCallback(const MinidumpDescriptor& descriptor, void* context, bool succeeded);
|
||||
};
|
||||
|
||||
CrashHandler::CrashHandler(): d(new CrashHandlerPrivate()) {}
|
||||
|
||||
void CrashHandler::init() {
|
||||
// MinidumpDescriptor has no move constructor and the copy constructor breaks fds.
|
||||
auto createHandler = [this](const MinidumpDescriptor& desc) {
|
||||
this->d->exceptionHandler = new ExceptionHandler(
|
||||
desc,
|
||||
nullptr,
|
||||
&CrashHandlerPrivate::minidumpCallback,
|
||||
this->d,
|
||||
true,
|
||||
-1
|
||||
);
|
||||
};
|
||||
|
||||
qCDebug(logCrashHandler) << "Starting crash handler...";
|
||||
|
||||
this->d->minidumpFd = memfd_create("quickshell:minidump", MFD_CLOEXEC);
|
||||
|
||||
if (this->d->minidumpFd == -1) {
|
||||
qCCritical(logCrashHandler
|
||||
) << "Failed to allocate minidump memfd, minidumps will be saved in the working directory.";
|
||||
createHandler(MinidumpDescriptor("."));
|
||||
} else {
|
||||
qCDebug(logCrashHandler) << "Created memfd" << this->d->minidumpFd
|
||||
<< "for holding possible minidumps.";
|
||||
createHandler(MinidumpDescriptor(this->d->minidumpFd));
|
||||
}
|
||||
|
||||
qCInfo(logCrashHandler) << "Crash handler initialized.";
|
||||
}
|
||||
|
||||
void CrashHandler::setInstanceInfo(const InstanceInfo& info) {
|
||||
this->d->infoFd = memfd_create("quickshell:instance_info", MFD_CLOEXEC);
|
||||
|
||||
if (this->d->infoFd == -1) {
|
||||
qCCritical(logCrashHandler
|
||||
) << "Failed to allocate instance info memfd, crash recovery will not work.";
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file;
|
||||
file.open(this->d->infoFd, QFile::ReadWrite);
|
||||
|
||||
QDataStream ds(&file);
|
||||
ds << info;
|
||||
file.flush();
|
||||
|
||||
qCDebug(logCrashHandler) << "Stored instance info in memfd" << this->d->infoFd;
|
||||
}
|
||||
|
||||
CrashHandler::~CrashHandler() {
|
||||
delete this->d->exceptionHandler;
|
||||
delete this->d;
|
||||
}
|
||||
|
||||
bool CrashHandlerPrivate::minidumpCallback(
|
||||
const MinidumpDescriptor& /*descriptor*/,
|
||||
void* context,
|
||||
bool /*success*/
|
||||
) {
|
||||
// A fork that just dies to ensure the coredump is caught by the system.
|
||||
auto coredumpPid = fork();
|
||||
|
||||
if (coredumpPid == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* self = static_cast<CrashHandlerPrivate*>(context);
|
||||
|
||||
auto exe = std::array<char, 4096>();
|
||||
if (readlink("/proc/self/exe", exe.data(), exe.size() - 1) == -1) {
|
||||
perror("Failed to find crash reporter executable.\n");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
auto arg = std::array<char*, 2> {exe.data(), nullptr};
|
||||
|
||||
auto env = std::array<char*, 4096>();
|
||||
auto envi = 0;
|
||||
|
||||
auto infoFd = dup(self->infoFd);
|
||||
auto infoFdStr = std::array<char, 38>();
|
||||
memcpy(infoFdStr.data(), "__QUICKSHELL_CRASH_INFO_FD=-1" /*\0*/, 30);
|
||||
if (infoFd != -1) my_uitos(&infoFdStr[27], infoFd, 10);
|
||||
env[envi++] = infoFdStr.data();
|
||||
|
||||
auto corePidStr = std::array<char, 39>();
|
||||
memcpy(corePidStr.data(), "__QUICKSHELL_CRASH_DUMP_PID=-1" /*\0*/, 31);
|
||||
if (coredumpPid != -1) my_uitos(&corePidStr[28], coredumpPid, 10);
|
||||
env[envi++] = corePidStr.data();
|
||||
|
||||
auto populateEnv = [&]() {
|
||||
auto senvi = 0;
|
||||
while (envi < 4095) {
|
||||
env[envi++] = environ[senvi++]; // NOLINT
|
||||
}
|
||||
|
||||
env[envi] = nullptr;
|
||||
};
|
||||
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset); // NOLINT (include)
|
||||
sigprocmask(SIG_SETMASK, &sigset, nullptr); // NOLINT
|
||||
|
||||
auto pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
perror("Failed to fork and launch crash reporter.\n");
|
||||
return false;
|
||||
} else if (pid == 0) {
|
||||
// dup to remove CLOEXEC
|
||||
// if already -1 will return -1
|
||||
auto dumpFd = dup(self->minidumpFd);
|
||||
auto logFd = dup(CrashInfo::INSTANCE.logFd);
|
||||
|
||||
// allow up to 10 digits, which should never happen
|
||||
auto dumpFdStr = std::array<char, 38>();
|
||||
auto logFdStr = std::array<char, 37>();
|
||||
|
||||
memcpy(dumpFdStr.data(), "__QUICKSHELL_CRASH_DUMP_FD=-1" /*\0*/, 30);
|
||||
memcpy(logFdStr.data(), "__QUICKSHELL_CRASH_LOG_FD=-1" /*\0*/, 29);
|
||||
|
||||
if (dumpFd != -1) my_uitos(&dumpFdStr[27], dumpFd, 10);
|
||||
if (logFd != -1) my_uitos(&logFdStr[26], logFd, 10);
|
||||
|
||||
env[envi++] = dumpFdStr.data();
|
||||
env[envi++] = logFdStr.data();
|
||||
|
||||
populateEnv();
|
||||
execve(exe.data(), arg.data(), env.data());
|
||||
|
||||
perror("Failed to launch crash reporter.\n");
|
||||
_exit(-1);
|
||||
} else {
|
||||
populateEnv();
|
||||
execve(exe.data(), arg.data(), env.data());
|
||||
|
||||
perror("Failed to relaunch quickshell.\n");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
return false; // should make sure it hits the system coredump handler
|
||||
}
|
||||
|
||||
} // namespace qs::crash
|
23
src/crash/handler.hpp
Normal file
23
src/crash/handler.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <qtclasshelpermacros.h>
|
||||
|
||||
#include "../core/crashinfo.hpp"
|
||||
namespace qs::crash {
|
||||
|
||||
struct CrashHandlerPrivate;
|
||||
|
||||
class CrashHandler {
|
||||
public:
|
||||
explicit CrashHandler();
|
||||
~CrashHandler();
|
||||
Q_DISABLE_COPY_MOVE(CrashHandler);
|
||||
|
||||
void init();
|
||||
void setInstanceInfo(const InstanceInfo& info);
|
||||
|
||||
private:
|
||||
CrashHandlerPrivate* d;
|
||||
};
|
||||
|
||||
} // namespace qs::crash
|
97
src/crash/interface.cpp
Normal file
97
src/crash/interface.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include "interface.hpp"
|
||||
#include <utility>
|
||||
|
||||
#include <qapplication.h>
|
||||
#include <qboxlayout.h>
|
||||
#include <qdesktopservices.h>
|
||||
#include <qfont.h>
|
||||
#include <qfontinfo.h>
|
||||
#include <qlabel.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobject.h>
|
||||
#include <qpushbutton.h>
|
||||
#include <qwidget.h>
|
||||
|
||||
#include "build.hpp"
|
||||
|
||||
class ReportLabel: public QWidget {
|
||||
public:
|
||||
ReportLabel(const QString& label, const QString& content, QWidget* parent): QWidget(parent) {
|
||||
auto* layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(new QLabel(label, this));
|
||||
|
||||
auto* cl = new QLabel(content, this);
|
||||
cl->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
layout->addWidget(cl);
|
||||
|
||||
layout->addStretch();
|
||||
}
|
||||
};
|
||||
|
||||
CrashReporterGui::CrashReporterGui(QString reportFolder, int pid)
|
||||
: reportFolder(std::move(reportFolder)) {
|
||||
this->setWindowFlags(Qt::Window);
|
||||
|
||||
auto textHeight = QFontInfo(QFont()).pixelSize();
|
||||
|
||||
auto* mainLayout = new QVBoxLayout(this);
|
||||
|
||||
mainLayout->addWidget(new QLabel(
|
||||
"<u>Quickshell has crashed. Please submit a bug report to help us fix it.</u>",
|
||||
this
|
||||
));
|
||||
|
||||
mainLayout->addSpacing(textHeight);
|
||||
|
||||
mainLayout->addWidget(new QLabel("General information", this));
|
||||
mainLayout->addWidget(new ReportLabel("Git Revision:", GIT_REVISION, this));
|
||||
mainLayout->addWidget(new ReportLabel("Crashed process ID:", QString::number(pid), this));
|
||||
mainLayout->addWidget(new ReportLabel("Crash report folder:", this->reportFolder, this));
|
||||
mainLayout->addSpacing(textHeight);
|
||||
|
||||
mainLayout->addWidget(new QLabel("Please open a bug report for this issue via github or email."));
|
||||
|
||||
mainLayout->addWidget(new ReportLabel(
|
||||
"Github:",
|
||||
"https://github.com/outfoxxed/quickshell/issues/new?template=crash.yml",
|
||||
this
|
||||
));
|
||||
|
||||
mainLayout->addWidget(new ReportLabel("Email:", "quickshell-bugs@outfoxxed.me", this));
|
||||
|
||||
auto* buttons = new QWidget(this);
|
||||
buttons->setMinimumWidth(900);
|
||||
auto* buttonLayout = new QHBoxLayout(buttons);
|
||||
buttonLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto* reportButton = new QPushButton("Open report page", buttons);
|
||||
reportButton->setDefault(true);
|
||||
QObject::connect(reportButton, &QPushButton::clicked, this, &CrashReporterGui::openReportUrl);
|
||||
buttonLayout->addWidget(reportButton);
|
||||
|
||||
auto* openFolderButton = new QPushButton("Open crash folder", buttons);
|
||||
QObject::connect(openFolderButton, &QPushButton::clicked, this, &CrashReporterGui::openFolder);
|
||||
buttonLayout->addWidget(openFolderButton);
|
||||
|
||||
auto* cancelButton = new QPushButton("Exit", buttons);
|
||||
QObject::connect(cancelButton, &QPushButton::clicked, this, &CrashReporterGui::cancel);
|
||||
buttonLayout->addWidget(cancelButton);
|
||||
|
||||
mainLayout->addWidget(buttons);
|
||||
|
||||
mainLayout->addStretch();
|
||||
this->setFixedSize(this->sizeHint());
|
||||
}
|
||||
|
||||
void CrashReporterGui::openFolder() {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(this->reportFolder));
|
||||
}
|
||||
|
||||
void CrashReporterGui::openReportUrl() {
|
||||
QDesktopServices::openUrl(
|
||||
QUrl("https://github.com/outfoxxed/quickshell/issues/new?template=crash.yml")
|
||||
);
|
||||
}
|
||||
|
||||
void CrashReporterGui::cancel() { QApplication::quit(); }
|
17
src/crash/interface.hpp
Normal file
17
src/crash/interface.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <qwidget.h>
|
||||
|
||||
class CrashReporterGui: public QWidget {
|
||||
public:
|
||||
CrashReporterGui(QString reportFolder, int pid);
|
||||
|
||||
private slots:
|
||||
void openFolder();
|
||||
|
||||
static void openReportUrl();
|
||||
static void cancel();
|
||||
|
||||
private:
|
||||
QString reportFolder;
|
||||
};
|
165
src/crash/main.cpp
Normal file
165
src/crash/main.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
#include "main.hpp"
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <qapplication.h>
|
||||
#include <qconfig.h>
|
||||
#include <qdatastream.h>
|
||||
#include <qdatetime.h>
|
||||
#include <qdir.h>
|
||||
#include <qfile.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qtenvironmentvariables.h>
|
||||
#include <qtextstream.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "../core/crashinfo.hpp"
|
||||
#include "../core/logging.hpp"
|
||||
#include "../core/paths.hpp"
|
||||
#include "build.hpp"
|
||||
#include "interface.hpp"
|
||||
|
||||
Q_LOGGING_CATEGORY(logCrashReporter, "quickshell.crashreporter", QtWarningMsg);
|
||||
|
||||
void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instanceInfo);
|
||||
|
||||
void qsCheckCrash(int argc, char** argv) {
|
||||
auto fd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD");
|
||||
if (fd.isEmpty()) return;
|
||||
auto app = QApplication(argc, argv);
|
||||
|
||||
InstanceInfo instance;
|
||||
|
||||
auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
|
||||
|
||||
{
|
||||
auto infoFd = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD").toInt();
|
||||
|
||||
QFile file;
|
||||
file.open(infoFd, QFile::ReadOnly, QFile::AutoCloseHandle);
|
||||
file.seek(0);
|
||||
|
||||
auto ds = QDataStream(&file);
|
||||
ds >> instance;
|
||||
}
|
||||
|
||||
LogManager::init(!instance.noColor, false);
|
||||
auto crashDir = QsPaths::crashDir(instance.shellId, instance.launchTime);
|
||||
|
||||
qCInfo(logCrashReporter) << "Starting crash reporter...";
|
||||
|
||||
recordCrashInfo(crashDir, instance);
|
||||
|
||||
auto gui = CrashReporterGui(crashDir.path(), crashProc);
|
||||
gui.show();
|
||||
exit(QApplication::exec()); // NOLINT
|
||||
}
|
||||
|
||||
int tryDup(int fd, const QString& path) {
|
||||
QFile sourceFile;
|
||||
if (!sourceFile.open(fd, QFile::ReadOnly, QFile::AutoCloseHandle)) {
|
||||
qCCritical(logCrashReporter) << "Failed to open source fd for duplication.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto destFile = QFile(path);
|
||||
if (!destFile.open(QFile::WriteOnly)) {
|
||||
qCCritical(logCrashReporter) << "Failed to open dest file for duplication.";
|
||||
return 2;
|
||||
}
|
||||
|
||||
auto size = sourceFile.size();
|
||||
off_t offset = 0;
|
||||
ssize_t count = 0;
|
||||
|
||||
sourceFile.seek(0);
|
||||
|
||||
while (count != size) {
|
||||
auto r = sendfile(destFile.handle(), sourceFile.handle(), &offset, sourceFile.size());
|
||||
if (r == -1) {
|
||||
qCCritical(logCrashReporter).nospace()
|
||||
<< "Failed to duplicate fd " << fd << " with error code " << errno
|
||||
<< ". Error: " << qt_error_string();
|
||||
return 3;
|
||||
} else {
|
||||
count += r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance) {
|
||||
qCDebug(logCrashReporter) << "Recording crash information at" << crashDir.path();
|
||||
|
||||
if (!crashDir.mkpath(".")) {
|
||||
qCCritical(logCrashReporter) << "Failed to create folder" << crashDir
|
||||
<< "to save crash information.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
|
||||
auto dumpFd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD").toInt();
|
||||
auto logFd = qEnvironmentVariable("__QUICKSHELL_CRASH_LOG_FD").toInt();
|
||||
|
||||
qCDebug(logCrashReporter) << "Saving minidump from fd" << dumpFd;
|
||||
auto dumpDupStatus = tryDup(dumpFd, crashDir.filePath("minidump.dmp"));
|
||||
if (dumpDupStatus != 0) {
|
||||
qCCritical(logCrashReporter) << "Failed to write minidump:" << dumpDupStatus;
|
||||
}
|
||||
|
||||
qCDebug(logCrashReporter) << "Saving log from fd" << logFd;
|
||||
auto logDupStatus = tryDup(logFd, crashDir.filePath("log.qslog"));
|
||||
if (logDupStatus != 0) {
|
||||
qCCritical(logCrashReporter) << "Failed to save log:" << logDupStatus;
|
||||
}
|
||||
|
||||
{
|
||||
auto extraInfoFile = QFile(crashDir.filePath("info.txt"));
|
||||
if (!extraInfoFile.open(QFile::WriteOnly)) {
|
||||
qCCritical(logCrashReporter) << "Failed to open crash info file for writing.";
|
||||
} else {
|
||||
auto stream = QTextStream(&extraInfoFile);
|
||||
stream << "===== Quickshell Crash =====\n";
|
||||
stream << "Git Revision: " << GIT_REVISION << '\n';
|
||||
stream << "Crashed process ID: " << crashProc << '\n';
|
||||
stream << "Run ID: " << QString("run-%1").arg(instance.launchTime.toMSecsSinceEpoch())
|
||||
<< '\n';
|
||||
|
||||
stream << "\n===== Shell Information =====\n";
|
||||
stream << "Shell ID: " << instance.shellId << '\n';
|
||||
stream << "Config Path: " << instance.configPath << '\n';
|
||||
|
||||
stream << "\n===== Report Integrity =====\n";
|
||||
stream << "Minidump save status: " << dumpDupStatus << '\n';
|
||||
stream << "Log save status: " << logDupStatus << '\n';
|
||||
|
||||
stream << "\n===== System Information =====\n";
|
||||
stream << "Qt Version: " << QT_VERSION_STR << "\n\n";
|
||||
|
||||
stream << "/etc/os-release:";
|
||||
auto osReleaseFile = QFile("/etc/os-release");
|
||||
if (osReleaseFile.open(QFile::ReadOnly)) {
|
||||
stream << '\n' << osReleaseFile.readAll() << '\n';
|
||||
osReleaseFile.close();
|
||||
} else {
|
||||
stream << "FAILED TO OPEN\n";
|
||||
}
|
||||
|
||||
stream << "/etc/lsb-release:";
|
||||
auto lsbReleaseFile = QFile("/etc/lsb-release");
|
||||
if (lsbReleaseFile.open(QFile::ReadOnly)) {
|
||||
stream << '\n' << lsbReleaseFile.readAll() << '\n';
|
||||
lsbReleaseFile.close();
|
||||
} else {
|
||||
stream << "FAILED TO OPEN\n";
|
||||
}
|
||||
|
||||
extraInfoFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(logCrashReporter) << "Recorded crash information.";
|
||||
}
|
3
src/crash/main.hpp
Normal file
3
src/crash/main.hpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
void qsCheckCrash(int argc, char** argv);
|
|
@ -11,6 +11,7 @@
|
|||
#include <qtmetamacros.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../../core/common.hpp"
|
||||
#include "../../dbus/properties.hpp"
|
||||
#include "dbus_watcher_interface.h"
|
||||
#include "item.hpp"
|
||||
|
@ -31,7 +32,10 @@ StatusNotifierHost::StatusNotifierHost(QObject* parent): QObject(parent) {
|
|||
return;
|
||||
}
|
||||
|
||||
this->hostId = QString("org.kde.StatusNotifierHost-") + QString::number(getpid());
|
||||
this->hostId = QString("org.kde.StatusNotifierHost-%1-%2")
|
||||
.arg(QString::number(getpid()))
|
||||
.arg(QString::number(qs::Common::LAUNCH_TIME.toMSecsSinceEpoch()));
|
||||
|
||||
auto success = bus.registerService(this->hostId);
|
||||
|
||||
if (!success) {
|
||||
|
@ -98,7 +102,7 @@ void StatusNotifierHost::connectToWatcher() {
|
|||
[this](QStringList value, QDBusError error) { // NOLINT
|
||||
if (error.isValid()) {
|
||||
qCWarning(logStatusNotifierHost).noquote()
|
||||
<< "Error reading \"RegisteredStatusNotifierITems\" property of watcher"
|
||||
<< "Error reading \"RegisteredStatusNotifierItems\" property of watcher"
|
||||
<< this->watcher->service();
|
||||
|
||||
qCWarning(logStatusNotifierHost) << error;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue