Qt

Introduction

Qt is a powerful cross-platform C++ app development platform. With a few easy steps, you can integrate BugSplat crash reporting in your macOS, Windows, and Linux Qt applications and leverage the most in-depth crash data on the market. Before integrating your application with BugSplat, make sure to review the Getting Started resources and complete the simple startup tasks listed below.
Need any further help? Check out the full BugSplat documentation here, or email the team at [email protected].

Building Crashpad

BugSplat leverages Crashpad to provide crash reporting for macOS, Windows, and Linux Qt applications. For an in-depth guide that discusses how to build Crashpad, please see this article.
For Windows, you'll need to build shared libraries. After running gn gen out/Default run gn args out/Default and add extra_cflags="/MD" so that your builds produce shared libraries. Additionally, make sure the version of MSVC and the Windows SDK used to compile Crashpad is the same version used by your Qt build otherwise your project will not build. Setting the version of MSVC that builds Crashpad can be done by instead generating your configuration using the command gn gen out/Default --winsdk="10.0.19041.0" --ide="vs2017".
For more info on how to build shared libraries for Windows, see this post.

Integrating Crashpad

Once Crashpad has been built you'll need to add the relevant include directories to your project. Copy all of the Crashpad .h files to the directory $PWD/Crashpad/Include/crashpad where $PWD is your project's working directory. Add the include directories to your project by pasting the following snippet at the top of your project file:
1
# Include directories for Crashpad libraries
2
INCLUDEPATH += $PWD/Crashpad/Include/crashpad
3
INCLUDEPATH += $PWD/Crashpad/Include/crashpad/third_party/mini_chromium/mini_chromium
Copied!
Next, link your app with the Crashpad libraries. Linking with the Crashpad libraries is platform-dependent.

macOS

Copy libbase.a, libutil.a and libclient.a into $PWD/Crashpad/Libraries/MacOS. Additionally, you'll need to copy all of the .o files from the Crashpad build folder /out/Default/gen/util/mach to the directory $PWD/Crashpad/Libraries/MacOS/util/mach. Finally, you'll need to link with the system libraries libbsm, AppKit.Framework, and Security.Framework. Add the following snippet to your project file to link with the aforementioned libraries:
1
# Crashpad rules for MacOS
2
macx {
3
# Crashpad libraries
4
LIBS += -L$PWD/Crashpad/Libraries/MacOS/ -lbase
5
LIBS += -L$PWD/Crashpad/Libraries/MacOS/ -lutil
6
LIBS += -L$PWD/Crashpad/Libraries/MacOS/ -lclient
7
LIBS += "$PWD/Crashpad/Libraries/MacOS/util/mach/*.o"
8
โ€‹
9
# System libraries
10
LIBS += -L/usr/lib/ -lbsm
11
LIBS += -framework AppKit
12
LIBS += -framework Security
13
}
Copied!
You'll need to ship a copy of the crashpad_handler executable with your application. Copy crashpad_handler to the $PWD/Crashpad/Bin/MacOS directory. Add the following snippet to the macx section of your project file that copies the macOS crashpad_handler to your project's build directory.
1
# Crashpad rules for MacOS
2
macx {
3
...
4
# Copy crashpad_handler to build directory
5
QMAKE_POST_LINK += "mkdir -p $OUT_PWD/crashpad"
6
QMAKE_POST_LINK += "&& cp $PWD/Crashpad/Bin/MacOS/crashpad_handler $OUT_PWD/crashpad"
7
}
Copied!

Windows

Copy base.lib, client.lib and util.lib into $PWD/Crashpad/Libraries/Windows. You'll need to link with the system library Advapi32. Add the following snippet to your project file to link with the aforementioned libraries:
1
# Crashpad rules for Windows
2
win32 {
3
# Crashpad libraries
4
LIBS += -L$PWD/Crashpad/Libraries/Windows/ -lbase
5
LIBS += -L$PWD/Crashpad/Libraries/Windows/ -lclient
6
LIBS += -L$PWD/Crashpad/Libraries/Windows/ -lutil
7
โ€‹
8
# System libraries
9
LIBS += -lAdvapi32
10
}
Copied!
Additionally, you'll need to ship a copy of the crashpad_handler.exe executable with your application. Copy crashpad_handler.exe to the $PWD/Crashpad/Bin/Windows directory. Add the following snippet to the win32 section of your project file that copies the Windows crashpad_handler.exe to your project's build directory.
1
# Crashpad rules for Windows
2
win32 {
3
...
4
# Build variables
5
CONFIG(debug, debug|release) {
6
EXEDIR = $OUT_PWD\debug
7
}
8
CONFIG(release, debug|release) {
9
EXEDIR = $OUT_PWD\release
10
}
11
โ€‹
12
# Copy crashpad_handler.exe to output directory
13
QMAKE_POST_LINK += "copy /y $shell_path($PWD)\Crashpad\Bin\Windows\crashpad_handler.exe $shell_path($OUT_PWD)\crashpad"
14
}
Copied!

Linux

Copy libbase.a, libutil.a and libclient.a into $PWD/Crashpad/Libraries/Linux. The order in which you specify the Crashpad libraries to link is important! libclient.a must be specified first, then libutil.a and finally libbase.a. Add the following snippet to your project file to link with the aforementioned libraries:
1
# Crashpad rules for Linux
2
linux {
3
# Crashpad libraries
4
LIBS += -L$PWD/Crashpad/Libraries/Linux/ -lclient
5
LIBS += -L$PWD/Crashpad/Libraries/Linux/ -lutil
6
LIBS += -L$PWD/Crashpad/Libraries/Linux/ -lbase
7
}
Copied!
Additionally, you'll need to ship a copy of the crashpad_handler executable with your application. Copy crashpad_handler to the $PWD/Crashpad/Bin/MacOS directory. Add the following snippet to the linux section of your project file that copies the Linux crashpad_handler to your project's build directory.
1
# Crashpad rules for Linux
2
linux {
3
...
4
# Copy crashpad_handler to build directory
5
QMAKE_POST_LINK += "cp $PWD/Crashpad/Bin/Linux/crashpad_handler $OUT_PWD/crashpad"
6
}
Copied!

Configuring Crashpad

To enable Crashpad in your application you'll need to configure the Crashpad handler with your BugSplat database, application name, and application version. The following is a macOS, Windows, and Linux compatible snippet that will configure the Crashpad handler:
1
#include <QApplication>
2
#include <vector>
3
#include "paths.h"
4
#include "client/crash_report_database.h"
5
#include "client/crashpad_client.h"
6
#include "client/settings.h"
7
โ€‹
8
using namespace base;
9
using namespace crashpad;
10
โ€‹
11
bool initializeCrashpad(QString dbName, QString appName, QString appVersion);
12
QString getExecutableDir(void);
13
โ€‹
14
bool initializeCrashpad(QString dbName, QString appName, QString appVersion)
15
{
16
// Get directory where the exe lives so we can pass a full path to handler, reportsDir and metricsDir
17
QString exeDir = getExecutableDir();
18
โ€‹
19
// Helper class for cross-platform file systems
20
Paths crashpadPaths(exeDir);
21
โ€‹
22
// Ensure that crashpad_handler is shipped with your application
23
FilePath handler(Paths::getPlatformString(crashpadPaths.getHandlerPath()));
24
โ€‹
25
// Directory where reports will be saved. Important! Must be writable or crashpad_handler will crash.
26
FilePath reportsDir(Paths::getPlatformString(crashpadPaths.getReportsPath()));
27
โ€‹
28
// Directory where metrics will be saved. Important! Must be writable or crashpad_handler will crash.
29
FilePath metricsDir(Paths::getPlatformString(crashpadPaths.getMetricsPath()));
30
โ€‹
31
// Configure url with your BugSplat database
32
QString url = "https://" + dbName + ".bugsplat.com/post/bp/crash/crashpad.php";
33
โ€‹
34
// Metadata that will be posted to BugSplat
35
QMap<string, string> annotations;
36
annotations["format"] = "minidump"; // Required: Crashpad setting to save crash as a minidump
37
annotations["database"] = dbName.toStdString(); // Required: BugSplat database
38
annotations["product"] = appName.toStdString(); // Required: BugSplat appName
39
annotations["version"] = appVersion.toStdString(); // Required: BugSplat appVersion
40
annotations["key"] = "Sample key"; // Optional: BugSplat key field
41
annotations["user"] = "[email protected]"; // Optional: BugSplat user email
42
annotations["list_annotations"] = "Sample comment"; // Optional: BugSplat crash description
43
โ€‹
44
// Disable crashpad rate limiting so that all crashes have dmp files
45
vector<string> arguments;
46
arguments.push_back("--no-rate-limit");
47
โ€‹
48
// Initialize crashpad database
49
unique_ptr<CrashReportDatabase> database = CrashReportDatabase::Initialize(reportsDir);
50
if (database == NULL) return false;
51
โ€‹
52
// Enable automated crash uploads
53
Settings *settings = database->GetSettings();
54
if (settings == NULL) return false;
55
settings->SetUploadsEnabled(true);
56
โ€‹
57
// Attachments to be uploaded alongside the crash - default bundle size limit is 2MB
58
vector<FilePath> attachments;
59
FilePath attachment(Paths::getPlatformString(crashpadPaths.getAttachmentPath()));
60
#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX)
61
// Crashpad hasn't implemented attachments on macOS yet
62
attachments.push_back(attachment);
63
#endif
64
โ€‹
65
// Start crash handler
66
CrashpadClient *client = new CrashpadClient();
67
bool status = client->StartHandler(handler, reportsDir, metricsDir, url.toStdString(), annotations.toStdMap(), arguments, true, true, attachments);
68
return status;
69
}
Copied!
Be sure to update the values for dbName, appName and appVersion to values specific to your application. The Paths class allows you to get platform-specific paths for Crashpad and its source can be found here. To configure the paths to crashpad_handler, metricsDir, reportsDir and attachment you'll first want to find the location of your executable using the sample code below:
1
#if defined(Q_OS_MACOS)
2
#include <mach-o/dyld.h>
3
#endif
4
โ€‹
5
#if defined(Q_OS_LINUX)
6
#include <unistd.h>
7
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
8
#endif
9
โ€‹
10
QString getExecutableDir() {
11
#if defined(Q_OS_MACOS)
12
unsigned int bufferSize = 512;
13
vector<char> buffer(bufferSize + 1);
14
โ€‹
15
if(_NSGetExecutablePath(&buffer[0], &bufferSize))
16
{
17
buffer.resize(bufferSize);
18
_NSGetExecutablePath(&buffer[0], &bufferSize);
19
}
20
โ€‹
21
char* lastForwardSlash = strrchr(&buffer[0], '/');
22
if (lastForwardSlash == NULL) return NULL;
23
*lastForwardSlash = 0;
24
โ€‹
25
return &buffer[0];
26
#elif defined(Q_OS_WINDOWS)
27
HMODULE hModule = GetModuleHandleW(NULL);
28
WCHAR path[MAX_PATH];
29
DWORD retVal = GetModuleFileNameW(hModule, path, MAX_PATH);
30
if (retVal == 0) return NULL;
31
โ€‹
32
wchar_t *lastBackslash = wcsrchr(path, '\\');
33
if (lastBackslash == NULL) return NULL;
34
*lastBackslash = 0;
35
โ€‹
36
return QString::fromWCharArray(path);
37
#elif defined(Q_OS_LINUX)
38
char pBuf[FILENAME_MAX];
39
int len = sizeof(pBuf);
40
int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
41
if (bytes >= 0) {
42
pBuf[bytes] = '\0';
43
}
44
โ€‹
45
char* lastForwardSlash = strrchr(&pBuf[0], '/');
46
if (lastForwardSlash == NULL) return NULL;
47
*lastForwardSlash = '\0';
48
โ€‹
49
return QString::fromStdString(pBuf);
50
#else
51
#error getExecutableDir not implemented on this platform
52
#endif
53
}
Copied!
Add a call to initializeCrashpad at your application's entry point.
1
int main(int argc, char *argv[]) {
2
QString dbName = "Fred";
3
QString appName = "myQtCrasher";
4
QString appVersion = "1.0";
5
initializeCrashpad(dbName, appName, appVersion);
6
}
Copied!

Symbols

In order to get function names and line numbers in your crash reports, you will need to generate and upload .sym files to BugSplat. Crashpad .sym files can be generated from a macOS .dSYM file, a Windows .pdb file or a Linux .debug file.
To generate .dSYM, .pdb and .debug files add the following to the project file:
1
# Create symbols for dump_syms and symupload
2
CONFIG += force_debug_info
3
CONFIG += separate_debug_info
Copied!

macOS

To generate .sym files you will need to build or locate a copy of dump_syms. Additionally, to upload symbol files to BugSplat you will need to build or locate a copy of symupload. Prebuilt copies of dump_syms and symupload can be found here.
Build your project and run dump_syms to generate .sym files:
1
./dump_syms -g path/to/myApp.dSYM path/to/myApp > myApp.sym
Copied!
Upload the generated .sym file by running symupload. Be sure to replace the {{database}}, {{application}} and {{version}} with the values you used in the Configuring Crashpad section:
1
./symupload "path/to/myApp.sym" "https://{{database}}.bugsplat.com/post/bp/symbol/breakpadsymbols.php?appName={{application}}&appVer={{version}}"
Copied!
After each release build you'll need to generate and upload .sym files making sure to increment the version number each time. The version number from the Configuring Crashpad section must match the version number in your upload URL. An example of how to run dump_syms and symupload as a build step can be found here.

Windows

The functionality of dump_syms.exe is built into symupload.exe on Windows. In order to generate .sym files, you will need to build or locate a copy of symupload.exe. A prebuilt copy of symupload.exe can be found here. Additionally, in order to run symupload.exe a copy of msdia140.dll must be present in the same directory as symupload.exe.
Run symupload.exe to upload .sym files to BugSplat. Be sure to replace the {{database}}, {{application}} and {{version}} with the values you used in the Configuring Crashpad section:
1
symupload.exe --product {{application}} "path\to\{{application}}.exe" "https://{{database}}.bugsplat.com/post/bp/symbol/breakpadsymbols.php?appName={{application}}&appVer={{version}}"
Copied!
You'll need to generate and upload .sym files making sure to increment the version number for each release build. The version number from the Configuring Crashpad section must match the version number in your upload URL. An example of how to run dump_syms and symupload as a build step can be found here.

Linux

To generate .sym files you will need to build or locate a copy of dump_syms. Additionally, to upload symbol files to BugSplat you will need to build or locate a copy of symupload. Prebuilt copies of dump_syms and symupload can be found here. Build your project and run dump_syms to generate .sym files:
1
./dump_syms path/to/myApp.debug > myApp.sym
Copied!
Upload the generated .sym file by running symupload. Be sure to replace the {{database}}, {{application}} and {{version}} with the values you used in the Configuring Crashpad section:
1
./symupload "path/to/myApp.sym" "https://{{database}}.bugsplat.com/post/bp/symbol/breakpadsymbols.php?appName={{application}}&appVer={{version}}"
Copied!
You'll need to generate and upload .sym files making sure to increment the version number for each release build. The version number from the Configuring Crashpad section must match the version number in your upload URL. An example of how to run dump_syms and symupload as a build step can be found here.

Generating a Crash Report

Force a crash in your application after Crashpad has been initialized:
1
*(volatile int *)0 = 0;
Copied!
After you've submitted a crash report, navigate to the Dashboard page. Click the link in the ID column to see the details of your crash report. The following image is from our sample myQtCrasher application:
BugSplat Qt Crash
Last modified 24d ago