3 /***************************************************************************
4 * Copyright (C) 2005 Max Howell <max.howell@methylblue.com> *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 ***************************************************************************/
14 //#include "amarokconfig.h"
15 #include "crashhandler.h"
19 #include <kapplication.h> //invokeMailer()
20 #include <kaboutdata.h>
21 #include <kdeversion.h>
23 #include <ktempfile.h>
27 #include <q3textstream.h>
31 #include <cstdio> //popen, fread
33 #include <sys/types.h> //pid_t
34 #include <sys/wait.h> //waitpid
35 //#include <taglib/taglib.h>
36 #include <unistd.h> //write, getpid
40 //#ifndef TAGLIB_PATCH_VERSION
41 //// seems to be wheel's style
42 //#define TAGLIB_PATCH_VERSION 0
47 class CrashHandlerWidget
: public KDialog
{
54 runCommand( const Q3CString
&command
)
56 static const uint SIZE
= 40960; //40 KiB
57 static char stdoutBuf
[ SIZE
];
59 // debug() << "Running: " << command << endl;
61 FILE *process
= ::popen( command
, "r" );
62 stdoutBuf
[ std::fread( static_cast<void*>( stdoutBuf
), sizeof(char), SIZE
-1, process
) ] = '\0';
65 return QString::fromLocal8Bit( stdoutBuf
);
69 Crash::crashHandler( int /*signal*/ )
71 // we need to fork to be able to get a
72 // semi-decent bt - I dunno why
73 const pid_t pid
= ::fork();
77 // we are the child process (the result of the fork)
78 // debug() << "amaroK is crashing...\n";
80 QString subject
= "CRASH: " VERSION
" ";
82 "%1 has crashed! We're sorry about this.\n"
84 "But, all is not lost! You could potentially help us fix the crash. "
85 "Information describing the crash is below, so just click send, "
86 "or if you have time, write a brief description of how the crash happened first.\n\n"
87 "Many thanks." ).arg(kapp
->aboutData()->programName()) + "\n\n";
88 body
+= "\n\n\n\n\n\n" + i18n(
89 "The information below is to help the developers identify the problem, "
90 "please do not modify it." ) + "\n\n\n\n";
93 body
+= "======== DEBUG INFORMATION =======\n"
94 "Version: " VERSION
"\n"
95 "Build date: " __DATE__
"\n"
96 "CC version: " __VERSION__
"\n" //assuming we're using GCC
97 "KDElibs: " KDE_VERSION_STRING
"\n"
98 ;// "TagLib: %2.%3.%4\n";
101 .arg( TAGLIB_MAJOR_VERSION )
102 .arg( TAGLIB_MINOR_VERSION )
103 .arg( TAGLIB_PATCH_VERSION );*/
106 body
+= "NDEBUG: true";
110 /// obtain the backtrace with gdb
113 temp
.setAutoDelete( true );
115 const int handle
= temp
.handle();
117 // QCString gdb_command_string =
118 // "file amarokapp\n"
119 // "attach " + QCString().setNum( ::getppid() ) + "\n"
120 // "bt\n" "echo \\n\n"
121 // "thread apply all bt\n";
123 const Q3CString gdb_batch
=
128 "echo ==== (gdb) thread apply all bt ====\\n\n"
129 "thread apply all bt\n";
131 ::write( handle
, gdb_batch
, gdb_batch
.length() );
134 // so we can read stderr too
135 ::dup2( fileno( stdout
), fileno( stderr
) );
139 gdb
= "gdb --nw -n --batch -x ";
140 gdb
+= temp
.name().latin1();
142 gdb
+= Q3CString().setNum( ::getppid() );
144 QString bt
= runCommand( gdb
);
147 bt
.remove( "(no debugging symbols found)..." );
148 bt
.remove( "(no debugging symbols found)\n" );
149 bt
.replace( QRegExp("\n{2,}"), "\n" ); //clean up multiple \n characters
150 bt
.stripWhiteSpace();
152 /// analyze usefulness
154 const QString fileCommandOutput
= runCommand( "file `which basket`" );
156 if( fileCommandOutput
.find( "not stripped", false ) == -1 )
157 subject
+= "[___stripped]"; //same length as below
159 subject
+= "[NOTstripped]";
161 if( !bt
.isEmpty() ) {
162 const int invalidFrames
= bt
.contains( QRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in \\?\\?") );
163 const int validFrames
= bt
.contains( QRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in [^?]") );
164 const int totalFrames
= invalidFrames
+ validFrames
;
166 if( totalFrames
> 0 ) {
167 const double validity
= double(validFrames
) / totalFrames
;
168 subject
+= QString("[validity: %1]").arg( validity
, 0, 'f', 2 );
169 if( validity
<= 0.5 ) useful
= false;
171 subject
+= QString("[frames: %1]").arg( totalFrames
, 3 /*padding*/ );
173 if( bt
.find( QRegExp(" at \\w*\\.cpp:\\d+\n") ) >= 0 )
174 subject
+= "[line numbers]";
179 // subject += QString("[%1]").arg( AmarokConfig::soundSystem().remove( QRegExp("-?engine") ) );
181 // debug() << subject << endl;
184 //TODO -fomit-frame-pointer buggers up the backtrace, so detect it
185 //TODO -O optimization can rearrange execution and stuff so show a warning for the developer
186 //TODO pass the CXXFLAGS used with the email
189 body
+= "==== file `which basket` ==========\n";
190 body
+= fileCommandOutput
+ "\n";
191 body
+= "==== (gdb) bt =====================\n";
192 body
+= bt
;//+ "\n\n";
193 // body += "==== kdBacktrace() ================\n";
194 // body += kdBacktrace();
196 //TODO startup notification
198 /*to*/ "slaout@linux62.org",
203 /*messageFile*/ QString(),
204 /*attachURLs*/ QStringList(),
208 std::cout
<< "\n" + i18n( "%1 has crashed! We're sorry about this.\n\n"
209 "But, all is not lost! Perhaps an upgrade is already available "
210 "which fixes the problem. Please check your distribution's software repository." )
211 .arg(kapp
->aboutData()->programName()).local8Bit() << std::endl
;
214 //_exit() exits immediately, otherwise this
215 //function is called repeatedly ad finitum
220 // we are the process that crashed
224 // wait for child to exit
225 ::waitpid( pid
, NULL
, 0 );