From 17a319fdb3120c7891a0f3a85b9e7dabd906a70f Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sun, 31 Jan 2016 19:13:51 +0300
Subject: [PATCH] improved crash reports for linux

---
 Telegram/SourceFiles/boxes/aboutbox.cpp  |   5 +-
 Telegram/SourceFiles/logs.cpp            |  26 ++++--
 Telegram/SourceFiles/pspecific_linux.cpp | 113 ++++++++++++++++++++---
 Telegram/SourceFiles/window.cpp          |   6 +-
 Telegram/SourceFiles/window.h            |   1 +
 Telegram/Telegram.pro                    |   6 +-
 6 files changed, 129 insertions(+), 28 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp
index a9bc2e0ad..396d515f4 100644
--- a/Telegram/SourceFiles/boxes/aboutbox.cpp
+++ b/Telegram/SourceFiles/boxes/aboutbox.cpp
@@ -126,8 +126,7 @@ void AboutBox::dragEnterEvent(QDragEnterEvent *e) {
 void AboutBox::dropEvent(QDropEvent *e) {
 	if (!_getCrashReportFile(e->mimeData()).isEmpty()) {
 		e->acceptProposedAction();
-		psExecTelegram(_getCrashReportFile(e->mimeData()));
-		App::quit();
+		psShowCrash(_getCrashReportFile(e->mimeData()));
 	}
 }
 
@@ -142,4 +141,4 @@ QString telegramFaqLink() {
 		}
 	}
 	return result;
-}
\ No newline at end of file
+}
diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp
index c660f77b3..6d117dc61 100644
--- a/Telegram/SourceFiles/logs.cpp
+++ b/Telegram/SourceFiles/logs.cpp
@@ -709,41 +709,47 @@ namespace SignalHandlers {
 #if defined Q_OS_MAC || defined Q_OS_LINUX32 || defined Q_OS_LINUX64
 		ucontext_t *uc = (ucontext_t*)ucontext;
 
-		void *addresses[128] = { 0 };
 		void *caller = 0;
-
 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
 			/* OSX < 10.6 */
 #if defined(__x86_64__)
-			caller = (void*)uap->uc_mcontext->__ss.__rip;
+			caller = (void*)uc->uc_mcontext->__ss.__rip;
 #elif defined(__i386__)
-			caller = (void*)uap->uc_mcontext->__ss.__eip;
+			caller = (void*)uc->uc_mcontext->__ss.__eip;
 #else
-			caller = (void*)uap->uc_mcontext->__ss.__srr0;
+			caller = (void*)uc->uc_mcontext->__ss.__srr0;
 #endif
 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
 			/* OSX >= 10.6 */
 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
 			caller = (void*)uc->uc_mcontext->__ss.__rip;
 #else
-			caller = (void*)uap->uc_mcontext->__ss.__eip;
+			caller = (void*)uc->uc_mcontext->__ss.__eip;
 #endif
 #elif defined(__linux__)
 			/* Linux */
 #if defined(__i386__)
-			caller = (void*)uap->uc_mcontext.gregs[14]; /* Linux 32 */
+			caller = (void*)uc->uc_mcontext.gregs[14]; /* Linux 32 */
 #elif defined(__X86_64__) || defined(__x86_64__)
-			caller = (void*)uap->uc_mcontext.gregs[16]; /* Linux 64 */
+			caller = (void*)uc->uc_mcontext.gregs[16]; /* Linux 64 */
 #elif defined(__ia64__) /* Linux IA64 */
-			caller = (void*)uap->uc_mcontext.sc_ip;
+			caller = (void*)uc->uc_mcontext.sc_ip;
 #endif
 
 #endif
 
+        void *addresses[132] = { 0 };
 		size_t size = backtrace(addresses, 128);
 
 		/* overwrite sigaction with caller's address */
-		if (caller) addresses[1] = caller;
+        if (caller) {
+            for (int i = size; i > 1; --i) {
+                addresses[i + 3] = addresses[i];
+            }
+            addresses[2] = (void*)0x1;
+            addresses[3] = caller;
+            addresses[4] = (void*)0x1;
+        }
 
 #ifdef Q_OS_MAC
 		dump() << "\nBase image addresses:\n";
diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp
index 5134efeaf..54b0227bf 100644
--- a/Telegram/SourceFiles/pspecific_linux.cpp
+++ b/Telegram/SourceFiles/pspecific_linux.cpp
@@ -979,6 +979,8 @@ void psWriteDump() {
 }
 
 QString demanglestr(const QString &mangled) {
+    if (mangled.isEmpty()) return mangled;
+
 	QByteArray cmd = ("c++filt -n " + mangled).toUtf8();
 	FILE *f = popen(cmd.constData(), "r");
 	if (!f) return "BAD_SYMBOL_" + mangled;
@@ -994,6 +996,51 @@ QString demanglestr(const QString &mangled) {
 	return result.trimmed();
 }
 
+QStringList addr2linestr(uint64 *addresses, int count) {
+	QStringList result;
+	if (!count) return result;
+
+	result.reserve(count);
+	QString cmdstr = "addr2line -e " + escapeShell(cExeDir() + cExeName());
+	for (int i = 0; i < count; ++i) {
+		if (addresses[i]) {
+			cmdstr += qsl(" 0x%1").arg(addresses[i], 0, 16);
+		}
+	}
+	QByteArray cmd = cmdstr.toUtf8();
+	FILE *f = popen(cmd.constData(), "r");
+
+	QStringList addr2lineResult;
+	if (f) {
+		char buffer[4096] = {0};
+		while (!feof(f)) {
+			if (fgets(buffer, 4096, f) != NULL) {
+				addr2lineResult.push_back(QString::fromUtf8(buffer));
+			}
+		}
+		pclose(f);
+	}
+	for (int i = 0, j = 0; i < count; ++i) {
+		if (addresses[i]) {
+			if (j < addr2lineResult.size() && !addr2lineResult.at(j).isEmpty() && !addr2lineResult.at(j).startsWith(qstr("0x"))) {
+				QString res = addr2lineResult.at(j).trimmed();
+                if (int index = res.indexOf(qstr("/Telegram/"))) {
+                    if (index > 0) {
+                        res = res.mid(index + qstr("/Telegram/").size());
+                    }
+                }
+                result.push_back(res);
+			} else {
+				result.push_back(QString());
+			}
+			++j;
+		} else {
+			result.push_back(QString());
+		}
+	}
+	return result;
+}
+
 QString _showCrashDump(const QByteArray &crashdump, QString dumpfile) {
 	QString initial = QString::fromUtf8(crashdump), result;
 	QStringList lines = initial.split('\n');
@@ -1001,6 +1048,7 @@ QString _showCrashDump(const QByteArray &crashdump, QString dumpfile) {
 	int32 i = 0, l = lines.size();
 
 	while (i < l) {
+        uint64 addresses[1024] = { 0 };
 		for (; i < l; ++i) {
 			result.append(lines.at(i)).append('\n');
 			QString line = lines.at(i).trimmed();
@@ -1010,21 +1058,59 @@ QString _showCrashDump(const QByteArray &crashdump, QString dumpfile) {
 			}
 		}
 
-		for (int32 start = i; i < l; ++i) {
+        int32 start = i;
+		for (; i < l; ++i) {
+            QString line = lines.at(i).trimmed();
+			if (line.isEmpty()) break;
+
+            QRegularExpressionMatch m1 = QRegularExpression(qsl("^(.+)\\(([^+]+)\\+([^\\)]+)\\)\\[(.+)\\]$")).match(line);
+            QRegularExpressionMatch m2 = QRegularExpression(qsl("^(.+)\\[(.+)\\]$")).match(line);
+            QString addrstr = m1.hasMatch() ? m1.captured(4) : (m2.hasMatch() ? m2.captured(2) : QString());
+            if (!addrstr.isEmpty()) {
+                uint64 addr = addrstr.startsWith(qstr("0x")) ? addrstr.mid(2).toULongLong(0, 16) : addrstr.toULongLong();
+                if (addr > 1) {
+                    addresses[i - start] = addr;
+                }
+            }
+		}
+
+		QStringList addr2line = addr2linestr(addresses, i - start);
+		for (i = start; i < l; ++i) {
 			QString line = lines.at(i).trimmed();
 			if (line.isEmpty()) break;
 
-            result.append(qsl("%1. ").arg(i + 1 - start));
+            result.append(qsl("\n%1. ").arg(i - start));
             if (line.startsWith(qstr("ERROR: "))) {
                 result.append(line).append('\n');
                 continue;
             }
-            QRegularExpressionMatch m = QRegularExpression(qsl("^(.+)\\(([^+]+)\\+([^\\)]+)\\)(.+)$")).match(line);
-            if (!m.hasMatch()) {
+            if (line == qstr("[0x1]")) {
+                result.append(qsl("(0x1 separator)\n"));
+                continue;
+            }
+
+            QRegularExpressionMatch m1 = QRegularExpression(qsl("^(.+)\\(([^+]*)\\+([^\\)]+)\\)(.+)$")).match(line);
+            QRegularExpressionMatch m2 = QRegularExpression(qsl("^(.+)\\[(.+)\\]$")).match(line);
+            if (!m1.hasMatch() && !m2.hasMatch()) {
                 result.append(qstr("BAD LINE: ")).append(line).append('\n');
                 continue;
             }
-            result.append(demanglestr(m.captured(2))).append(qsl(" + ")).append(m.captured(3)).append(qsl(" (")).append(m.captured(1)).append(qsl(") ")).append(m.captured(4)).append('\n');
+
+            if (m1.hasMatch()) {
+                result.append(demanglestr(m1.captured(2))).append(qsl(" + ")).append(m1.captured(3)).append(qsl(" [")).append(m1.captured(1)).append(qsl("] "));
+                if (!addr2line.at(i - start).isEmpty() && addr2line.at(i - start) != qsl("??:0")) {
+                    result.append(qsl(" (")).append(addr2line.at(i - start)).append(qsl(")\n"));
+                } else {
+                    result.append(m1.captured(4)).append(qsl(" (demangled)")).append('\n');
+                }
+            } else {
+                result.append('[').append(m2.captured(1)).append(']');
+                if (!addr2line.at(i - start).isEmpty() && addr2line.at(i - start) != qsl("??:0")) {
+                    result.append(qsl(" (")).append(addr2line.at(i - start)).append(qsl(")\n"));
+                } else {
+                    result.append(' ').append(m2.captured(2)).append('\n');
+                }
+            }
 		}
 	}
 	return result;
@@ -1041,13 +1127,18 @@ int psShowCrash(const QString &crashdump) {
 		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);
+    if (Sandbox::started()) {
+        ShowCrashReportWindow *wnd = new ShowCrashReportWindow(text);
+        return 0;
+    }
 
-	ShowCrashReportWindow wnd(text);
-	return app.exec();
+    QByteArray args[] = { "" };
+    int a_argc = 1;
+    char *a_argv[1] = { args[0].data() };
+    QApplication app(a_argc, a_argv);
+
+    ShowCrashReportWindow *wnd = new ShowCrashReportWindow(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
diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp
index e3c2fe3fb..18950d868 100644
--- a/Telegram/SourceFiles/window.cpp
+++ b/Telegram/SourceFiles/window.cpp
@@ -2113,7 +2113,7 @@ void LastCrashedWindow::onViewReport() {
 }
 
 void LastCrashedWindow::onSaveReport() {
-	QString to = QFileDialog::getSaveFileName(0, qsl("Telegram Crash Report"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + qsl("/report"), qsl("Telegram crash report (*.telegramcrash)"));
+	QString to = QFileDialog::getSaveFileName(0, qsl("Telegram Crash Report"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + qsl("/report.telegramcrash"), qsl("Telegram crash report (*.telegramcrash)"));
 	if (!to.isEmpty()) {
 		QFile file(to);
 		if (file.open(QIODevice::WriteOnly)) {
@@ -2874,3 +2874,7 @@ ShowCrashReportWindow::ShowCrashReportWindow(const QString &text)
 void ShowCrashReportWindow::resizeEvent(QResizeEvent *e) {
 	_log.setGeometry(rect().marginsRemoved(QMargins(basicSize(), basicSize(), basicSize(), basicSize())));
 }
+
+void ShowCrashReportWindow::closeEvent(QCloseEvent *e) {
+    deleteLater();
+}
diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h
index f7c968ce8..fe3d2da2c 100644
--- a/Telegram/SourceFiles/window.h
+++ b/Telegram/SourceFiles/window.h
@@ -547,6 +547,7 @@ public:
 protected:
 
 	void resizeEvent(QResizeEvent *e);
+    void closeEvent(QCloseEvent *e);
 
 private:
 
diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro
index 96ede8071..64d4739b0 100644
--- a/Telegram/Telegram.pro
+++ b/Telegram/Telegram.pro
@@ -281,12 +281,12 @@ QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-result -Wno-unused-parameter -Wno-unused-v
 
 CONFIG(release, debug|release) {
     QMAKE_CXXFLAGS_RELEASE -= -O2
-    QMAKE_CXXFLAGS_RELEASE += -Ofast -flto -fno-strict-aliasing
+    QMAKE_CXXFLAGS_RELEASE += -Ofast -flto -fno-strict-aliasing -g
     QMAKE_LFLAGS_RELEASE -= -O1
-    QMAKE_LFLAGS_RELEASE += -Ofast -flto -rdynamic
+    QMAKE_LFLAGS_RELEASE += -Ofast -flto -rdynamic -g
 }
 CONFIG(debug, debug|release) {
-	QMAKE_LFLAGS_DEBUG += -rdynamic
+	QMAKE_LFLAGS_DEBUG += -rdynamic -g
 }
 
 INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\