File indexing completed on 2024-05-12 15:56:59

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