summaryrefslogtreecommitdiffstats
path: root/src/crashhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/crashhandler.cpp')
-rw-r--r--src/crashhandler.cpp226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/crashhandler.cpp b/src/crashhandler.cpp
new file mode 100644
index 0000000..64e745b
--- /dev/null
+++ b/src/crashhandler.cpp
@@ -0,0 +1,226 @@
+// Code from Amarok.
+
+/***************************************************************************
+ * Copyright (C) 2005 Max Howell <max.howell@methylblue.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+//#include "amarok.h"
+//#include "amarokconfig.h"
+#include "crashhandler.h"
+//#include "debug.h"
+#include "config.h"
+
+#include <kapplication.h> //invokeMailer()
+#include <kaboutdata.h>
+#include <kdeversion.h>
+#include <klocale.h>
+#include <ktempfile.h>
+
+#include <qfile.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+#include <cstdio> //popen, fread
+#include <iostream>
+#include <sys/types.h> //pid_t
+#include <sys/wait.h> //waitpid
+//#include <taglib/taglib.h>
+#include <unistd.h> //write, getpid
+
+
+
+//#ifndef TAGLIB_PATCH_VERSION
+//// seems to be wheel's style
+//#define TAGLIB_PATCH_VERSION 0
+//#endif
+
+
+ #if 0
+ class CrashHandlerWidget : public KDialog {
+ public:
+ CrashHandlerWidget();
+ };
+ #endif
+
+ static QString
+ runCommand( const QCString &command )
+ {
+ static const uint SIZE = 40960; //40 KiB
+ static char stdoutBuf[ SIZE ];
+
+// debug() << "Running: " << command << endl;
+
+ FILE *process = ::popen( command, "r" );
+ stdoutBuf[ std::fread( static_cast<void*>( stdoutBuf ), sizeof(char), SIZE-1, process ) ] = '\0';
+ ::pclose( process );
+
+ return QString::fromLocal8Bit( stdoutBuf );
+ }
+
+ void
+ Crash::crashHandler( int /*signal*/ )
+ {
+ // we need to fork to be able to get a
+ // semi-decent bt - I dunno why
+ const pid_t pid = ::fork();
+
+ if( pid <= 0 )
+ {
+ // we are the child process (the result of the fork)
+// debug() << "amaroK is crashing...\n";
+
+ QString subject = "[basket-crash] " VERSION " ";
+ QString body = i18n(
+ "%1 has crashed! We're sorry about this.\n"
+ "\n"
+ "But, all is not lost! You could potentially help us fix the crash. "
+ "Information describing the crash is below, so just click send, "
+ "or if you have time, write a brief description of how the crash happened first.\n\n"
+ "Many thanks." ).arg(kapp->aboutData()->programName()) + "\n\n";
+ body += "\n\n\n\n\n\n" + i18n(
+ "The information below is to help the developers identify the problem, "
+ "please do not modify it." ) + "\n\n\n\n";
+
+
+ body += "======== DEBUG INFORMATION =======\n"
+ "Version: " VERSION "\n"
+ "Build date: " __DATE__ "\n"
+ "CC version: " __VERSION__ "\n" //assuming we're using GCC
+ "KDElibs: " KDE_VERSION_STRING "\n"
+;// "TagLib: %2.%3.%4\n";
+
+/* body = body
+ .arg( TAGLIB_MAJOR_VERSION )
+ .arg( TAGLIB_MINOR_VERSION )
+ .arg( TAGLIB_PATCH_VERSION );*/
+
+ #ifdef NDEBUG
+ body += "NDEBUG: true";
+ #endif
+ body += "\n";
+
+ /// obtain the backtrace with gdb
+
+ KTempFile temp;
+ temp.setAutoDelete( true );
+
+ const int handle = temp.handle();
+
+// QCString gdb_command_string =
+// "file amarokapp\n"
+// "attach " + QCString().setNum( ::getppid() ) + "\n"
+// "bt\n" "echo \\n\n"
+// "thread apply all bt\n";
+
+ const QCString gdb_batch =
+ "bt\n"
+ "echo \\n\\n\n"
+ "bt full\n"
+ "echo \\n\\n\n"
+ "echo ==== (gdb) thread apply all bt ====\\n\n"
+ "thread apply all bt\n";
+
+ ::write( handle, gdb_batch, gdb_batch.length() );
+ ::fsync( handle );
+
+ // so we can read stderr too
+ ::dup2( fileno( stdout ), fileno( stderr ) );
+
+
+ QCString gdb;
+ gdb = "gdb --nw -n --batch -x ";
+ gdb += temp.name().latin1();
+ gdb += " basket ";
+ gdb += QCString().setNum( ::getppid() );
+
+ QString bt = runCommand( gdb );
+
+ /// clean up
+ bt.remove( "(no debugging symbols found)..." );
+ bt.remove( "(no debugging symbols found)\n" );
+ bt.replace( QRegExp("\n{2,}"), "\n" ); //clean up multiple \n characters
+ bt.stripWhiteSpace();
+
+ /// analyze usefulness
+ bool useful = true;
+ const QString fileCommandOutput = runCommand( "file `which basket`" );
+
+ if( fileCommandOutput.find( "not stripped", false ) == -1 )
+ subject += "[___stripped]"; //same length as below
+ else
+ subject += "[NOTstripped]";
+
+ if( !bt.isEmpty() ) {
+ const int invalidFrames = bt.contains( QRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in \\?\\?") );
+ const int validFrames = bt.contains( QRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in [^?]") );
+ const int totalFrames = invalidFrames + validFrames;
+
+ if( totalFrames > 0 ) {
+ const double validity = double(validFrames) / totalFrames;
+ subject += QString("[validity: %1]").arg( validity, 0, 'f', 2 );
+ if( validity <= 0.5 ) useful = false;
+ }
+ subject += QString("[frames: %1]").arg( totalFrames, 3 /*padding*/ );
+
+ if( bt.find( QRegExp(" at \\w*\\.cpp:\\d+\n") ) >= 0 )
+ subject += "[line numbers]";
+ }
+ else
+ useful = false;
+
+// subject += QString("[%1]").arg( AmarokConfig::soundSystem().remove( QRegExp("-?engine") ) );
+
+// debug() << subject << endl;
+
+
+ //TODO -fomit-frame-pointer buggers up the backtrace, so detect it
+ //TODO -O optimization can rearrange execution and stuff so show a warning for the developer
+ //TODO pass the CXXFLAGS used with the email
+
+ if( useful ) {
+ body += "==== file `which basket` ==========\n";
+ body += fileCommandOutput + "\n";
+ body += "==== (gdb) bt =====================\n";
+ body += bt;//+ "\n\n";
+// body += "==== kdBacktrace() ================\n";
+// body += kdBacktrace();
+
+ //TODO startup notification
+ kapp->invokeMailer(
+ /*to*/ "kelvie@ieee.org",
+ /*cc*/ QString(),
+ /*bcc*/ QString(),
+ /*subject*/ subject,
+ /*body*/ body,
+ /*messageFile*/ QString(),
+ /*attachURLs*/ QStringList(),
+ /*startup_id*/ "" );
+ }
+ else {
+ std::cout << "\n" + i18n( "%1 has crashed! We're sorry about this.\n\n"
+ "But, all is not lost! Perhaps an upgrade is already available "
+ "which fixes the problem. Please check your distribution's software repository." )
+ .arg(kapp->aboutData()->programName()).local8Bit() << std::endl;
+ }
+
+ //_exit() exits immediately, otherwise this
+ //function is called repeatedly ad finitum
+ ::_exit( 255 );
+ }
+
+ else {
+ // we are the process that crashed
+
+ ::alarm( 0 );
+
+ // wait for child to exit
+ ::waitpid( pid, NULL, 0 );
+ ::_exit( 253 );
+ }
+ }