Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAndroidCrashHandler.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2022 Sharaf Zaman <shzam@sdf.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
10
11#include <QDateTime>
12#include <QMap>
13#include <QScopedPointer>
14#include <QStandardPaths>
15#include <QThread>
16
17#include <android/log.h>
18#include <array>
19#include <fcntl.h>
20#include <signal.h>
21#include <sstream>
22#include <unistd.h>
23#include <unwindstack/Regs.h>
24#include <unwindstack/Unwinder.h>
25
26#define CRASH_LOGGER(...) __android_log_print(ANDROID_LOG_WARN, "KisAndroidCrashHandler", __VA_ARGS__)
27
29
30static const std::array<int, 6> signals = {SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGSYS, SIGTERM};
31static QMap<int, struct sigaction> g_old_actions;
32
33// we need to have keep this object alive
34static const std::string path =
35 QString(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/kritacrashlog.txt").toStdString();
36static const char *crashlog_path = path.c_str();
37
38static bool g_handling_crash = false;
39
40const char *get_signal_name(const int signo)
41{
42 switch (signo) {
43 case SIGABRT:
44 return "SIGABRT";
45 case SIGBUS:
46 return "SIGBUS";
47 case SIGFPE:
48 return "SIGFPE";
49 case SIGSEGV:
50 return "SIGSEGV";
51 case SIGSYS:
52 return "SIGSYS";
53 case SIGTERM:
54 return "SIGTERM";
55 default:
56 return "?";
57 }
58}
59
60void dump_backtrace(siginfo_t *info, void *ucontext)
61{
62 QScopedPointer<unwindstack::Regs> regs;
63 if (ucontext) {
64 regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
65 } else {
66 regs.reset(unwindstack::Regs::CreateFromLocal());
67 }
68
69 unwindstack::UnwinderFromPid unwinder(256, getpid(), unwindstack::Regs::CurrentArch());
70 if (!unwinder.Init()) {
71 CRASH_LOGGER("Couldn't initialize the unwinder: %s\n", unwinder.LastErrorCodeString());
72 return;
73 }
74
75 unwinder.SetRegs(regs.data());
76 unwinder.Unwind();
77
78 std::vector<unwindstack::FrameData> frames = unwinder.frames();
79 if (frames.size() == 0) {
80 CRASH_LOGGER("Couldn't unwind: %s\t code = %d\n", unwinder.LastErrorCodeString(), unwinder.LastErrorCode());
81 return;
82 }
83
84 const int fd = open(crashlog_path, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
85
86 std::stringstream header;
87 header << "********************** Dumping backtrace **********************\n"
88 << "Signal: " << info->si_signo << " (" << get_signal_name(info->si_signo) << ")"
89 << " (Code: " << info->si_code << ")"
90 << " Time: " << QDateTime::currentDateTimeUtc().toString().toStdString().c_str()
91 << " Version: " << KritaVersionWrapper::versionString(true).toStdString().c_str() << "\n";
92 write(fd, header.str().c_str(), header.str().size());
93
94 for (size_t i = 0; i < frames.size(); ++i) {
95 std::string frame = unwinder.FormatFrame(frames[i]) + "\n";
96 write(fd, frame.c_str(), frame.size());
97 }
98 write(fd, "\n", 1);
99 close(fd);
100}
101
102void crash_callback(int sig, siginfo_t *info, void *ucontext)
103{
104 // to prevent second invocation of our signal handler
105 if (g_handling_crash) {
106 // uninstall the handler for the signal
107 sigaction(sig, &g_old_actions[sig], nullptr);
108 raise(sig);
109 return;
110 }
111
112 g_handling_crash = true;
113 dump_backtrace(info, ucontext);
114
115 // uninstall the handler for the signal
116 sigaction(sig, &g_old_actions[sig], nullptr);
117
118 // invoke the previous handler
119 // Some other implementations tend to make call to handler functions
120 // directly, seemingly to not make another _slow_ syscall.
121 raise(sig);
122}
123
125{
126 // create an alternate stack to make sure we can handle overflows
127 stack_t alternate_stack;
128 alternate_stack.ss_flags = 0;
129 alternate_stack.ss_size = SIGSTKSZ;
130 if ((alternate_stack.ss_sp = malloc(SIGSTKSZ)) == nullptr) {
131 CRASH_LOGGER("Couldn't allocate memory for alternate stack");
132 return;
133 }
134
135 struct sigaction act = {};
136 act.sa_sigaction = crash_callback;
137 act.sa_flags = SA_SIGINFO | SA_ONSTACK;
138 sigaltstack(&alternate_stack, nullptr);
139
140 for (size_t i = 0; i < signals.size(); ++i) {
141 sigaction(signals[i], &act, &g_old_actions[signals[i]]);
142 }
143}
144
145} // namespace KisAndroidCrashHandler
#define CRASH_LOGGER(...)
void dump_backtrace(siginfo_t *info, void *ucontext)
const char * get_signal_name(const int signo)
static const std::array< int, 6 > signals
static const char * crashlog_path
static QMap< int, struct sigaction > g_old_actions
void crash_callback(int sig, siginfo_t *info, void *ucontext)
KRITAVERSION_EXPORT QString versionString(bool checkGit=false)