swift
crashhandler.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "misc/crashhandler.h"
5 
6 #include <QCoreApplication>
7 #include <QFileInfo>
8 #include <QStringBuilder>
9 
10 #include "config/buildconfig.h"
11 #include "misc/directoryutils.h"
12 #include "misc/filelogger.h"
13 #include "misc/logmessage.h"
14 #include "misc/swiftdirectories.h"
15 
16 #ifdef SWIFT_USE_CRASHPAD
17 # if defined(Q_OS_WIN) && !defined(NOMINMAX)
18 # define NOMINMAX
19 # endif
20 # include "crashpad/client/crash_report_database.h"
21 # include "crashpad/client/crashpad_client.h"
22 # include "crashpad/client/settings.h"
23 # include "crashpad/client/simulate_crash.h"
24 #endif
25 
26 using namespace swift::config;
27 using namespace swift::misc;
28 using namespace crashpad;
29 
30 namespace swift::misc
31 {
32  CCrashHandler *CCrashHandler::instance()
33  {
34  static CCrashHandler crashHandler;
35  return &crashHandler;
36  }
37 
38  CCrashHandler::~CCrashHandler() {}
39 
40 #ifdef SWIFT_USE_CRASHPAD
42  base::FilePath qstringToFilePath(const QString &str)
43  {
44 # ifdef Q_OS_WIN
45  return base::FilePath(str.toStdWString());
46 # else
47  return base::FilePath(str.toStdString());
48 # endif
49  }
50 #endif
51 
52  void CCrashHandler::init()
53  {
54 #ifdef SWIFT_USE_CRASHPAD
55  static const QString crashpadHandler(
56  CBuildConfig::isRunningOnWindowsNtPlatform() ? "swift_crashpad_handler.exe" : "swift_crashpad_handler");
57  static const QString handler = CFileUtils::appendFilePaths(CSwiftDirectories::binDirectory(), crashpadHandler);
58  const QString database = CSwiftDirectories::crashpadDatabaseDirectory();
59  const QString metrics = CSwiftDirectories::crashpadMetricsDirectory();
60 
61  if (!QFileInfo::exists(handler)) { return; }
62 
63  const std::string serverUrl("http://swift-project.sp.backtrace.io:6097/");
64  std::map<std::string, std::string> annotations;
65 
66  // Backtrace annotations
67  annotations["token"] = CBuildConfig::backtraceToken().toStdString();
68  annotations["format"] = "minidump";
69  annotations["commit"] = CBuildConfig::gitHeadSha1().toStdString();
70  annotations["version"] = CBuildConfig::getVersionString().toStdString();
71  annotations["short_version"] = CBuildConfig::getShortVersionString().toStdString();
72  annotations["platform"] = CBuildConfig::getPlatformString().toStdString();
73  annotations["qtversion"] = QT_VERSION_STR;
74 
75  // add our logfile
76  const QString logAttachment = QStringLiteral("--attachment=attachment_%1=%2")
77  .arg(CFileLogger::getLogFileName(), CFileLogger::getLogFilePath());
78 
79  std::vector<std::string> arguments;
80  arguments.push_back(logAttachment.toStdString());
81 
82  // and the simplified crash info if any
83  const QString crashInfoFileName("swiftcrashinfo.txt");
84  const QString crashInfoFilePath(CFileUtils::appendFilePaths(
85  CFileUtils::stripFileFromPath(CFileLogger::getLogFilePath()), crashInfoFileName));
86  m_crashAndLogInfo.setLogPathAndFileName(crashInfoFilePath);
87  const QString crashAttachment =
88  QStringLiteral("--attachment=attachment_%1=%2").arg(crashInfoFileName, crashInfoFilePath);
89  arguments.push_back(crashAttachment.toStdString());
90 
91  // for testing purposes
92  if (CBuildConfig::isLocalDeveloperDebugBuild()) { arguments.push_back("--no-rate-limit"); }
93 
94  QDir().mkpath(database);
95 
96  m_crashReportDatabase = CrashReportDatabase::Initialize(qstringToFilePath(database));
97  m_crashpadClient = std::make_unique<CrashpadClient>();
98  m_crashpadClient->StartHandler(qstringToFilePath(handler), qstringToFilePath(database),
99  qstringToFilePath(metrics), serverUrl, annotations, arguments, false, true);
100 
101  this->crashAndLogAppendInfo(u"Init crash info at " % QDateTime::currentDateTimeUtc().toString());
102 #endif
103  }
104 
105  void CCrashHandler::setUploadsEnabled(bool enable)
106  {
107 #ifdef SWIFT_USE_CRASHPAD
108  if (!m_crashReportDatabase) { return; }
109  crashpad::Settings *settings = m_crashReportDatabase->GetSettings();
110  settings->SetUploadsEnabled(enable);
111 #else
112  Q_UNUSED(enable);
113 #endif
114  }
115 
116  bool CCrashHandler::isCrashDumpUploadEnabled() const
117  {
118 #ifdef SWIFT_USE_CRASHPAD
119  if (!m_crashReportDatabase) { return false; }
120  crashpad::Settings *settings = m_crashReportDatabase->GetSettings();
121  bool enabled = false;
122  bool ok = settings->GetUploadsEnabled(&enabled);
123  return ok && enabled;
124 #else
125  return false;
126 #endif
127  }
128 
129  void CCrashHandler::triggerCrashInfoWrite() { m_crashAndLogInfo.triggerWritingFile(); }
130 
131  void CCrashHandler::setCrashInfo(const CCrashInfo &info)
132  {
133  m_crashAndLogInfo = info;
134  m_dsCrashAndLogInfo.inputSignal();
135  }
136 
137  void CCrashHandler::crashAndLogInfoUserName(const QString &name)
138  {
139  m_crashAndLogInfo.setUserName(name);
140  m_dsCrashAndLogInfo.inputSignal();
141  }
142 
143  void CCrashHandler::crashAndLogInfoSimulator(const QString &simulator)
144  {
145  m_crashAndLogInfo.setSimulatorString(simulator);
146  m_dsCrashAndLogInfo.inputSignal();
147  }
148 
149  void CCrashHandler::crashAndLogInfoFlightNetwork(const QString &flightNetwork)
150  {
151  m_crashAndLogInfo.setFlightNetworkString(flightNetwork);
152  m_dsCrashAndLogInfo.inputSignal();
153  }
154 
155  void CCrashHandler::crashAndLogAppendInfo(const QString &info)
156  {
157  m_crashAndLogInfo.appendInfo(info);
158  m_dsCrashAndLogInfo.inputSignal();
159  }
160 
161  void CCrashHandler::simulateCrash()
162  {
163 #ifdef SWIFT_USE_CRASHPAD
164  CLogMessage(this).info(u"Simulated crash dump!");
165  m_crashAndLogInfo.appendInfo("Simulated crash dump!");
166  m_crashAndLogInfo.writeToFile();
167  CRASHPAD_SIMULATE_CRASH();
168  // real crash
169  // raise(SIGSEGV); #include <signal.h>
170 #else
171  CLogMessage(this).warning(u"This compiler or platform does not support crashpad. Cannot simulate crash dump!");
172 #endif
173  }
174 
175  void CCrashHandler::simulateAssert()
176  {
177 #ifdef SWIFT_USE_CRASHPAD
178  CLogMessage(this).info(u"Simulated ASSERT!");
179  m_crashAndLogInfo.appendInfo("Simulated ASSERT!");
180  m_crashAndLogInfo.writeToFile();
181  Q_ASSERT_X(false, Q_FUNC_INFO, "Test server to test Crash handler");
182 #else
183  CLogMessage(this).warning(u"This compiler or platform does not support crashpad. Cannot simulate crash dump!");
184 #endif
185  }
186 
187  CCrashHandler::CCrashHandler(QObject *parent) : QObject(parent) {}
188 } // namespace swift::misc
static const QString & getPlatformString()
Info such as Win32, Win64, macOS, Linux.
static const QString & getShortVersionString()
Version as QVersionNumber.
static const QString & getVersionString()
Version as QVersionNumber.
static bool isLocalDeveloperDebugBuild()
Local build for developers.
static constexpr bool isRunningOnWindowsNtPlatform()
Running on Windows NT platform?
static const QString & gitHeadSha1()
Returns SHA-1 of git HEAD at build time.
static const QString & backtraceToken()
Backtrace token for minidump uploads.
void setUserName(const QString &userName)
User name.
Definition: crashinfo.h:39
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Free functions in swift::misc.