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