From 52c29bac5d67e299b181c34e6a9407a838ae182f Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 30 Jan 2016 21:24:42 +0300 Subject: [PATCH] started crash reporting for linux --- Telegram/SourceFiles/_other/updater_linux.cpp | 31 +++-- Telegram/SourceFiles/pspecific_linux.cpp | 124 +++++++++++++++++- Telegram/SourceFiles/pspecific_linux.h | 6 +- Telegram/SourceFiles/pspecific_wnd.cpp | 21 +-- Telegram/SourceFiles/pspecific_wnd.h | 2 +- 5 files changed, 154 insertions(+), 30 deletions(-) diff --git a/Telegram/SourceFiles/_other/updater_linux.cpp b/Telegram/SourceFiles/_other/updater_linux.cpp index 35d67072c9..f65ad09448 100644 --- a/Telegram/SourceFiles/_other/updater_linux.cpp +++ b/Telegram/SourceFiles/_other/updater_linux.cpp @@ -324,7 +324,7 @@ bool update() { int main(int argc, char *argv[]) { bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false, testmode = false; - char *key = 0; + char *key = 0, *crashreport = 0; for (int i = 1; i < argc; ++i) { if (equal(argv[i], "-noupdate")) { needupdate = false; @@ -342,7 +342,9 @@ int main(int argc, char *argv[]) { key = argv[i]; } else if (equal(argv[i], "-workpath") && ++i < argc) { workDir = argv[i]; - } + } else if (equal(argv[i], "-crashreport") && ++i < argc) { + crashreport = argv[i]; + } } openLog(); @@ -408,17 +410,20 @@ int main(int argc, char *argv[]) { char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_startintray[] = "-startintray", p_testmode[] = "-testmode"; int argIndex = 0; args[argIndex++] = path; - args[argIndex++] = p_noupdate; - if (autostart) args[argIndex++] = p_autostart; - if (debug) args[argIndex++] = p_debug; - if (startintray) args[argIndex++] = p_startintray; - if (testmode) args[argIndex++] = p_testmode; - if (tosettings) args[argIndex++] = p_tosettings; - if (key) { - args[argIndex++] = p_key; - args[argIndex++] = key; - } - + if (crashreport) { + args[argIndex++] = crashreport; + } else { + args[argIndex++] = p_noupdate; + if (autostart) args[argIndex++] = p_autostart; + if (debug) args[argIndex++] = p_debug; + if (startintray) args[argIndex++] = p_startintray; + if (testmode) args[argIndex++] = p_testmode; + if (tosettings) args[argIndex++] = p_tosettings; + if (key) { + args[argIndex++] = p_key; + args[argIndex++] = key; + } + } pid_t pid = fork(); switch (pid) { case -1: writeLog("fork() failed!"); return 1; diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 5696398934..675091fe9b 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -973,6 +973,112 @@ QAbstractNativeEventFilter *psNativeEventFilter() { return _psEventFilter; } +void psWriteDump() { +} + +void psWriteStackTrace(int file) { + void *addresses[1024] = { 0 }; + + size_t size = backtrace(addresses, 1024); + + backtrace_symbols_fd(addresses, size, file); +} + +QString demanglestr(const QString &mangled) { + QByteArray cmd = ("c++filt -n " + mangled).toUtf8(); + FILE *f = popen(cmd.constData(), "r"); + if (!f) return "BAD_SYMBOL_" + mangled; + + QString result; + char buffer[4096] = { 0 }; + while (!feof(f)) { + if (fgets(buffer, 4096, f) != NULL) { + result += buffer; + } + } + pclose(f); + return result.trimmed(); +} + +QString _showCrashDump(const QByteArray &crashdump, QString dumpfile) { + QString initial = QString::fromUtf8(crashdump), result; + QStringList lines = initial.split('\n'); + result.reserve(initial.size()); + int32 i = 0, l = lines.size(); + + while (i < l) { + for (; i < l; ++i) { + result.append(lines.at(i)).append('\n'); + QString line = lines.at(i).trimmed(); + if (line == qstr("Backtrace:")) { + ++i; + break; + } + } + + for (int32 start = i; i < l; ++i) { + QString line = lines.at(i).trimmed(); + if (line.isEmpty()) break; + + if (!QRegularExpression(qsl("^\\d+")).match(line).hasMatch()) { + if (!lines.at(i).startsWith(qstr("ERROR: "))) { + result.append(qstr("BAD LINE: ")); + } + result.append(line).append('\n'); + continue; + } + QStringList lst = line.split(' ', QString::SkipEmptyParts); + result.append(lst.at(0)).append(' '); + for (int j = 1, s = lst.size();;) { + if (lst.at(j).startsWith('_')) { + result.append(demanglestr(lst.at(j))); + if (++j < s) { + result.append(' '); + for (;;) { + result.append(lst.at(j)); + if (++j < s) { + result.append(' '); + } else { + break; + } + } + } + break; + } else if (j > 2) { + result.append(lst.at(j)); + } + if (++j < s) { + result.append(' '); + } else { + break; + } + } + result.append('\n'); + } + } + return result; +} + +int psShowCrash(const QString &crashdump) { + QString text; + + QFile dump(crashdump); + if (dump.open(QIODevice::ReadOnly)) { + text = qsl("Crash dump file '%1':\n\n").arg(QFileInfo(crashdump).absoluteFilePath()); + text += _showCrashDump(dump.readAll(), crashdump); + } else { + text = qsl("ERROR: could not read crash dump file '%1'").arg(QFileInfo(crashdump).absoluteFilePath()); + } + + QByteArray args[] = { "" }; + int a_argc = 1; + char *a_argv[1] = { args[0].data() }; + QApplication app(a_argc, a_argv); + + ShowCrashReportWindow wnd(text); + return app.exec(); +} + bool _removeDirectory(const QString &path) { // from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c QByteArray pathRaw = QFile::encodeName(path); DIR *d = opendir(pathRaw.constData()); @@ -1243,15 +1349,15 @@ void psNewVersion() { psRegisterCustomScheme(); } -bool _execUpdater(bool update = true) { +bool _execUpdater(bool update = true, const QString &crashreport = QString()) { static const int MaxLen = 65536, MaxArgsCount = 128; char path[MaxLen] = {0}; QByteArray data(QFile::encodeName(cExeDir() + "Updater")); memcpy(path, data.constData(), data.size()); - char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_path[] = "-workpath", p_startintray[] = "-startintray", p_testmode[] = "-testmode"; - char p_datafile[MaxLen] = {0}, p_pathbuf[MaxLen] = {0}; + char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_path[] = "-workpath", p_startintray[] = "-startintray", p_testmode[] = "-testmode", p_crashreport[] = "-crashreport"; + char p_datafile[MaxLen] = {0}, p_pathbuf[MaxLen] = {0}, p_crashreportbuf[MaxLen] = {0}; int argIndex = 0; args[argIndex++] = path; if (!update) { @@ -1276,6 +1382,14 @@ bool _execUpdater(bool update = true) { args[argIndex++] = p_path; args[argIndex++] = p_pathbuf; } + if (!crashreport.isEmpty()) { + QByteArray crashreportf = crashreport.toUtf8(); + if (crashreportf.size() < MaxLen) { + memcpy(p_crashreportbuf, crashreportf.constData(), crashreportf.size()); + args[argIndex++] = p_crashreport; + args[argIndex++] = p_crashreportbuf; + } + } pid_t pid = fork(); switch (pid) { @@ -1291,8 +1405,8 @@ void psExecUpdater() { } } -void psExecTelegram() { - _execUpdater(false); +void psExecTelegram(const QString &crashreport) { + _execUpdater(false, crashreport); } bool psShowOpenWithMenu(int x, int y, const QString &file) { diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index 6fd3356515..6248cc072d 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -113,6 +113,10 @@ private: uint64 _psLastIndicatorUpdate; }; +void psWriteDump(); +void psWriteStackTrace(int file); +int psShowCrash(const QString &crashdump); + void psDeleteDir(const QString &dir); void psUserActionDone(); @@ -144,7 +148,7 @@ int psCleanup(); int psFixPrevious(); void psExecUpdater(); -void psExecTelegram(); +void psExecTelegram(const QString &arg = QString()); bool psShowOpenWithMenu(int x, int y, const QString &file); diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 3407fc421e..effea15595 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -2267,15 +2267,16 @@ void psExecUpdater() { } } -void psExecTelegram() { - QString targs = qsl("-noupdate"); - if (cRestartingToSettings()) targs += qsl(" -tosettings"); - if (cLaunchMode() == LaunchModeAutoStart) targs += qsl(" -autostart"); - if (cDebug()) targs += qsl(" -debug"); - if (cStartInTray()) targs += qsl(" -startintray"); - if (cTestMode()) targs += qsl(" -testmode"); - if (cDataFile() != qsl("data")) targs += qsl(" -key \"") + cDataFile() + '"'; - +void psExecTelegram(const QString &crashreport) { + QString targs = crashreport.isEmpty() ? qsl("-noupdate") : ('"' + crashreport + '"'); + if (crashreport.isEmpty()) { + if (cRestartingToSettings()) targs += qsl(" -tosettings"); + if (cLaunchMode() == LaunchModeAutoStart) targs += qsl(" -autostart"); + if (cDebug()) targs += qsl(" -debug"); + if (cStartInTray()) targs += qsl(" -startintray"); + if (cTestMode()) targs += qsl(" -testmode"); + if (cDataFile() != qsl("data")) targs += qsl(" -key \"") + cDataFile() + '"'; + } QString telegram(QDir::toNativeSeparators(cExeDir() + cExeName())), wdir(QDir::toNativeSeparators(cWorkingDir())); DEBUG_LOG(("Application Info: executing %1 %2").arg(cExeDir() + cExeName()).arg(targs)); @@ -3059,7 +3060,7 @@ void psWriteStackTrace(int file) { #error "Platform not supported!" #endif - for (frameNum = 0; frameNum < 1000; ++frameNum) { + for (frameNum = 0; frameNum < 1024; ++frameNum) { // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h index 053945f3b0..3d46e615f9 100644 --- a/Telegram/SourceFiles/pspecific_wnd.h +++ b/Telegram/SourceFiles/pspecific_wnd.h @@ -153,7 +153,7 @@ int psCleanup(); int psFixPrevious(); void psExecUpdater(); -void psExecTelegram(); +void psExecTelegram(const QString &arg = QString()); bool psShowOpenWithMenu(int x, int y, const QString &file);