File indexing completed on 2024-05-19 04:25:05

0001 /*
0002  * SPDX-FileCopyrightText: 2022 Sharaf Zaman <shzam@sdf.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KisAndroidCrashHandler.h"
0008 
0009 #include <KritaVersionWrapper.h>
0010 
0011 #include <QDateTime>
0012 #include <QMap>
0013 #include <QScopedPointer>
0014 #include <QStandardPaths>
0015 #include <QThread>
0016 
0017 #include <android/log.h>
0018 #include <array>
0019 #include <fcntl.h>
0020 #include <signal.h>
0021 #include <sstream>
0022 #include <unistd.h>
0023 #include <unwindstack/Regs.h>
0024 #include <unwindstack/Unwinder.h>
0025 
0026 #define CRASH_LOGGER(...) __android_log_print(ANDROID_LOG_WARN, "KisAndroidCrashHandler", __VA_ARGS__)
0027 
0028 namespace KisAndroidCrashHandler {
0029 
0030 static const std::array<int, 6> signals = {SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGSYS, SIGTERM};
0031 static QMap<int, struct sigaction> g_old_actions;
0032 
0033 // we need to have keep this object alive
0034 static const std::string path =
0035     QString(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/kritacrashlog.txt").toStdString();
0036 static const char *crashlog_path = path.c_str();
0037 
0038 static bool g_handling_crash = false;
0039 
0040 const char *get_signal_name(const int signo)
0041 {
0042     switch (signo) {
0043     case SIGABRT:
0044         return "SIGABRT";
0045     case SIGBUS:
0046         return "SIGBUS";
0047     case SIGFPE:
0048         return "SIGFPE";
0049     case SIGSEGV:
0050         return "SIGSEGV";
0051     case SIGSYS:
0052         return "SIGSYS";
0053     case SIGTERM:
0054         return "SIGTERM";
0055     default:
0056         return "?";
0057     }
0058 }
0059 
0060 void dump_backtrace(siginfo_t *info, void *ucontext)
0061 {
0062     QScopedPointer<unwindstack::Regs> regs;
0063     if (ucontext) {
0064         regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
0065     } else {
0066         regs.reset(unwindstack::Regs::CreateFromLocal());
0067     }
0068 
0069     unwindstack::UnwinderFromPid unwinder(256, getpid(), unwindstack::Regs::CurrentArch());
0070     if (!unwinder.Init()) {
0071         CRASH_LOGGER("Couldn't initialize the unwinder: %s\n", unwinder.LastErrorCodeString());
0072         return;
0073     }
0074 
0075     unwinder.SetRegs(regs.data());
0076     unwinder.Unwind();
0077 
0078     std::vector<unwindstack::FrameData> frames = unwinder.frames();
0079     if (frames.size() == 0) {
0080         CRASH_LOGGER("Couldn't unwind: %s\t code = %d\n", unwinder.LastErrorCodeString(), unwinder.LastErrorCode());
0081         return;
0082     }
0083 
0084     const int fd = open(crashlog_path, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
0085 
0086     std::stringstream header;
0087     header << "********************** Dumping backtrace **********************\n"
0088            << "Signal: " << info->si_signo << " (" << get_signal_name(info->si_signo) << ")"
0089            << " (Code: " << info->si_code << ")"
0090            << " Time: " << QDateTime::currentDateTimeUtc().toString().toStdString().c_str()
0091            << " Version: " << KritaVersionWrapper::versionString(true).toStdString().c_str() << "\n";
0092     write(fd, header.str().c_str(), header.str().size());
0093 
0094     for (size_t i = 0; i < frames.size(); ++i) {
0095         std::string frame = unwinder.FormatFrame(frames[i]) + "\n";
0096         write(fd, frame.c_str(), frame.size());
0097     }
0098     write(fd, "\n", 1);
0099     close(fd);
0100 }
0101 
0102 void crash_callback(int sig, siginfo_t *info, void *ucontext)
0103 {
0104     // to prevent second invocation of our signal handler
0105     if (g_handling_crash) {
0106         // uninstall the handler for the signal
0107         sigaction(sig, &g_old_actions[sig], nullptr);
0108         raise(sig);
0109         return;
0110     }
0111 
0112     g_handling_crash = true;
0113     dump_backtrace(info, ucontext);
0114 
0115     // uninstall the handler for the signal
0116     sigaction(sig, &g_old_actions[sig], nullptr);
0117 
0118     // invoke the previous handler
0119     // Some other implementations tend to make call to handler functions
0120     // directly, seemingly to not make another _slow_ syscall.
0121     raise(sig);
0122 }
0123 
0124 void handler_init()
0125 {
0126     // create an alternate stack to make sure we can handle overflows
0127     stack_t alternate_stack;
0128     alternate_stack.ss_flags = 0;
0129     alternate_stack.ss_size = SIGSTKSZ;
0130     if ((alternate_stack.ss_sp = malloc(SIGSTKSZ)) == nullptr) {
0131         CRASH_LOGGER("Couldn't allocate memory for alternate stack");
0132         return;
0133     }
0134 
0135     struct sigaction act = {};
0136     act.sa_sigaction = crash_callback;
0137     act.sa_flags = SA_SIGINFO | SA_ONSTACK;
0138     sigaltstack(&alternate_stack, nullptr);
0139 
0140     for (size_t i = 0; i < signals.size(); ++i) {
0141         sigaction(signals[i], &act, &g_old_actions[signals[i]]);
0142     }
0143 }
0144 
0145 } // namespace KisAndroidCrashHandler