diff options
Diffstat (limited to 'src/crashhandler.cpp')
-rw-r--r-- | src/crashhandler.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/crashhandler.cpp b/src/crashhandler.cpp new file mode 100644 index 0000000..5e072e6 --- /dev/null +++ b/src/crashhandler.cpp @@ -0,0 +1,293 @@ +/*************************************************************************** + * Copyright (C) 2006 - 2008 Robert Hogan * + * [email protected] * + * Copyright (C) 2005 Max Howell <[email protected]> * + * * + * 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 "tork.h" +#include "torkconfig.h" +#include "crashhandler.h" + +#include <kapplication.h> //invokeMailer() +#include <kdebug.h> //kdBacktrace() +#include <kdeversion.h> +#include <klocale.h> +#include <ktempfile.h> + +#include <qfile.h> +#include <qregexp.h> +#include <qtextstream.h> +#include <qglobal.h> //qVersion() + +#include <cstdio> //popen, fread +#include <iostream> +#include <sys/types.h> //pid_t +#include <sys/wait.h> //waitpid +#include <unistd.h> //write, getpid + + + + + +namespace torK +{ + #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 ] = {0}; + + std::cout << "Running: " << command << std::endl; + + FILE *process = ::popen( command, "r" ); + if ( process ) + { + 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 ) + { + std::cout << "forking crash reporter failed\n"; + // continuing now can't do no good + _exit( 1 ); + } + else if ( pid == 0 ) + { + // we are the child process (the result of the fork) + std::cout << "TorK is crashing...\n"; + + QString subject = TORK_VERSION " "; + QString body = i18n( + "TorK has crashed! We are terribly 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.\n\n" ); + body += i18n( "\n\n\n\n\n\n" + "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: " TORK_VERSION "\n" + "Build date: " __DATE__ "\n" + "CC version: " __VERSION__ "\n" //assuming we're using GCC + "KDElibs: " KDE_VERSION_STRING "\n" + "Qt: %2\n" + "CPU count: %6\n"; + + QString cpucount = "unknown"; +#ifdef __linux__ + QString line; + uint cpuCount = 0; + QFile cpuinfo( "/proc/cpuinfo" ); + if ( cpuinfo.open( IO_ReadOnly ) ) { + while ( cpuinfo.readLine( line, 20000 ) != -1 ) { + if ( line.startsWith( "processor" ) ) { + ++cpuCount; + } + } + } + cpucount = QString::number( cpuCount ); +#endif + + + body = body.arg( qVersion() ) + .arg( cpucount ); + + #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 tork\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 += " tork "; + 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 tork`" ); + + 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; + + + std::cout << subject.latin1() << std::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 tork` =======\n"; + body += fileCommandOutput + "\n\n"; + body += "==== (gdb) bt =====================\n"; + body += bt + "\n\n"; + body += "==== kdBacktrace() ================\n"; + body += kdBacktrace(); + + //TODO startup notification + kapp->invokeMailer( + /*to*/ "[email protected]", + /*cc*/ QString(), + /*bcc*/ QString(), + /*subject*/ subject, + /*body*/ body, + /*messageFile*/ QString(), + /*attachURLs*/ QStringList(), + /*startup_id*/ "" ); + } + else { + std::cout << i18n( "\nTorK has crashed! We are terribly 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.\n" ).local8Bit(); + } + + //_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 ); + } + } +} + + +#if 0 + +#include <qlabel.h> +#include <qlayout.h> +#include <qvbox.h> +#include <kdialog.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kstandarddirs.h> + +torK::CrashHandlerWidget::CrashHandlerWidget() +{ + QBoxLayout *layout = new QHBoxLayout( this, 18, 12 ); + + { + QBoxLayout *lay = new QVBoxLayout( layout ); + QLabel *label = new QLabel( this ); + label->setPixmap( locate( "data", "drkonqi/pics/konqi.png" ) ); + label->setFrameStyle( QFrame::Plain | QFrame::Box ); + lay->add( label ); + lay->addItem( new QSpacerItem( 3, 3, QSizePolicy::Minimum, QSizePolicy::Expanding ) ); + } + + layout = new QVBoxLayout( layout, 6 ); + + layout->add( new QLabel( /*i18n*/( + "<p>" "TorK has crashed! We are terribly sorry about this :(" + "<p>" "However you now have an opportunity to help us fix this crash so that it doesn't " + "happen again! Click <b>Send Email</b> and TorK will prepare an email that you " + "can send to us that contains information about the crash, and we'll try to fix it " + "as soon as possible." + "<p>" "Thanks for choosing TorK.<br>" ), this ) ); + + layout = new QHBoxLayout( layout, 6 ); + + layout->addItem( new QSpacerItem( 6, 6, QSizePolicy::Expanding ) ); + layout->add( new KPushButton( KGuiItem( i18n("Send Email"), "mail_send" ), this, "email" ) ); + layout->add( new KPushButton( KStdGuiItem::close(), this, "close" ) ); + + static_cast<QPushButton*>(child("email"))->setDefault( true ); + + connect( child( "email" ), SIGNAL(clicked()), SLOT(accept()) ); + connect( child( "close" ), SIGNAL(clicked()), SLOT(reject()) ); + + setCaption( i18n("Crash Handler") ); + setFixedSize( sizeHint() ); +} +#endif |