You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
10 KiB
294 lines
10 KiB
/***************************************************************************
|
|
* Copyright (C) 2006 - 2008 Robert Hogan *
|
|
* robert@roberthogan.net *
|
|
* 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 "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 <ntqfile.h>
|
|
#include <ntqregexp.h>
|
|
#include <ntqtextstream.h>
|
|
#include <ntqglobal.h> //tqVersion()
|
|
|
|
#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 TQString
|
|
runCommand( const TQCString &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 TQString::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";
|
|
|
|
TQString subject = TORK_VERSION " ";
|
|
TQString 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"
|
|
"TQt: %2\n"
|
|
"CPU count: %6\n";
|
|
|
|
TQString cpucount = "unknown";
|
|
#ifdef __linux__
|
|
TQString line;
|
|
uint cpuCount = 0;
|
|
TQFile cpuinfo( "/proc/cpuinfo" );
|
|
if ( cpuinfo.open( IO_ReadOnly ) ) {
|
|
while ( cpuinfo.readLine( line, 20000 ) != -1 ) {
|
|
if ( line.startsWith( "processor" ) ) {
|
|
++cpuCount;
|
|
}
|
|
}
|
|
}
|
|
cpucount = TQString::number( cpuCount );
|
|
#endif
|
|
|
|
|
|
body = body.arg( tqVersion() )
|
|
.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();
|
|
|
|
// TQCString gdb_command_string =
|
|
// "file tork\n"
|
|
// "attach " + TQCString().setNum( ::getppid() ) + "\n"
|
|
// "bt\n" "echo \\n\n"
|
|
// "thread apply all bt\n";
|
|
|
|
const TQCString 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 ) );
|
|
|
|
|
|
TQCString gdb;
|
|
gdb = "gdb --nw -n --batch -x ";
|
|
gdb += temp.name().latin1();
|
|
gdb += " tork ";
|
|
gdb += TQCString().setNum( ::getppid() );
|
|
|
|
TQString bt = runCommand( gdb );
|
|
|
|
/// clean up
|
|
bt.remove( "(no debugging symbols found)..." );
|
|
bt.remove( "(no debugging symbols found)\n" );
|
|
bt.replace( TQRegExp("\n{2,}"), "\n" ); //clean up multiple \n characters
|
|
bt.stripWhiteSpace();
|
|
|
|
/// analyze usefulness
|
|
bool useful = true;
|
|
const TQString 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( TQRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in \\?\\?") );
|
|
const int validFrames = bt.contains( TQRegExp("\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 += TQString("[validity: %1]").arg( validity, 0, 'f', 2 );
|
|
if( validity <= 0.5 ) useful = false;
|
|
}
|
|
subject += TQString("[frames: %1]").arg( totalFrames, 3 /*padding*/ );
|
|
|
|
if( bt.find( TQRegExp(" 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*/ "tork-backtraces@lists.sf.net",
|
|
/*cc*/ TQString(),
|
|
/*bcc*/ TQString(),
|
|
/*subject*/ subject,
|
|
/*body*/ body,
|
|
/*messageFile*/ TQString(),
|
|
/*attachURLs*/ TQStringList(),
|
|
/*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 <ntqlabel.h>
|
|
#include <ntqlayout.h>
|
|
#include <ntqvbox.h>
|
|
#include <kdialog.h>
|
|
#include <kpushbutton.h>
|
|
#include <kstdguiitem.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
torK::CrashHandlerWidget::CrashHandlerWidget()
|
|
{
|
|
TQBoxLayout *layout = new TQHBoxLayout( this, 18, 12 );
|
|
|
|
{
|
|
TQBoxLayout *lay = new TQVBoxLayout( layout );
|
|
TQLabel *label = new TQLabel( this );
|
|
label->setPixmap( locate( "data", "drkonqi/pics/konqi.png" ) );
|
|
label->setFrameStyle( TQFrame::Plain | TQFrame::Box );
|
|
lay->add( label );
|
|
lay->addItem( new TQSpacerItem( 3, 3, TQSizePolicy::Minimum, TQSizePolicy::Expanding ) );
|
|
}
|
|
|
|
layout = new TQVBoxLayout( layout, 6 );
|
|
|
|
layout->add( new TQLabel( /*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 TQHBoxLayout( layout, 6 );
|
|
|
|
layout->addItem( new TQSpacerItem( 6, 6, TQSizePolicy::Expanding ) );
|
|
layout->add( new KPushButton( KGuiItem( i18n("Send Email"), "mail_send" ), this, "email" ) );
|
|
layout->add( new KPushButton( KStdGuiItem::close(), this, "close" ) );
|
|
|
|
static_cast<TQPushButton*>(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
|