00001 #include <qwhatsthis.h>
00002 #include <qregexp.h>
00003 #include <qfile.h>
00004
00005 #include <kiconloader.h>
00006 #include <klocale.h>
00007 #include <kdevgenericfactory.h>
00008 #include <kaction.h>
00009 #include <kprocess.h>
00010 #include <kmessagebox.h>
00011 #include <kfiledialog.h>
00012 #include <kdebug.h>
00013
00014 #include "kdevcore.h"
00015 #include "kdevmainwindow.h"
00016 #include "kdevproject.h"
00017
00018 #include "valgrind_widget.h"
00019 #include "valgrind_part.h"
00020 #include "valgrind_dialog.h"
00021 #include "valgrinditem.h"
00022
00023 typedef KDevGenericFactory<ValgrindPart> ValgrindFactory;
00024 static const KAboutData data("kdevvalgrind", "Valgrind", "1.0");
00025 K_EXPORT_COMPONENT_FACTORY( libkdevvalgrind, ValgrindFactory( &data ) )
00026
00027 ValgrindPart::ValgrindPart( QObject *parent, const char *name, const QStringList& )
00028 : KDevPlugin( "Valgrind", "valgrind", parent, name ? name : "ValgrindPart" )
00029 {
00030 setInstance( ValgrindFactory::instance() );
00031 setXMLFile( "kdevpart_valgrind.rc" );
00032
00033 proc = new KShellProcess();
00034 connect( proc, SIGNAL(receivedStdout( KProcess*, char*, int )),
00035 this, SLOT(receivedStdout( KProcess*, char*, int )) );
00036 connect( proc, SIGNAL(receivedStderr( KProcess*, char*, int )),
00037 this, SLOT(receivedStderr( KProcess*, char*, int )) );
00038 connect( proc, SIGNAL(processExited( KProcess* )),
00039 this, SLOT(processExited( KProcess* )) );
00040 connect( core(), SIGNAL(stopButtonClicked(KDevPlugin*)),
00041 this, SLOT(slotStopButtonClicked(KDevPlugin*)) );
00042 connect( core(), SIGNAL(projectOpened()),
00043 this, SLOT(projectOpened()) );
00044
00045 m_widget = new ValgrindWidget( this );
00046 m_widget->setIcon( SmallIcon("fork") );
00047 m_widget->setCaption(i18n("Valgrind Output"));
00048
00049 QWhatsThis::add( m_widget, i18n( "<b>Valgrind</b><p>Shows the output of the valgrind. Valgrind detects<br>"
00050 "use of uninitialized memory<br>"
00051 "reading/writing memory after it has been free'd<br>"
00052 "reading/writing off the end of malloc'd blocks<br>"
00053 "reading/writing inappropriate areas on the stack<br>"
00054 "memory leaks -- where pointers to malloc'd blocks are lost forever<br>"
00055 "passing of uninitialised and/or unaddressable memory to system calls<br>"
00056 "mismatched use of malloc/new/new [] vs free/delete/delete []<br>"
00057 "some abuses of the POSIX pthread API." ) );
00058
00059 KAction* action = new KAction( i18n("&Valgrind Memory Leak Check"), 0, this,
00060 SLOT(slotExecValgrind()), actionCollection(), "tools_valgrind" );
00061 action->setToolTip(i18n("Valgrind memory leak check"));
00062 action->setWhatsThis(i18n("<b>Valgrind memory leak check</b><p>Runs Valgrind - a tool to help you find memory-management problems in your programs."));
00063
00064 action = new KAction( i18n("P&rofile with KCachegrind"), 0, this,
00065 SLOT(slotExecCalltree()), actionCollection(), "tools_calltree" );
00066 action->setToolTip(i18n("Profile with KCachegrind"));
00067 action->setWhatsThis(i18n("<b>Profile with KCachegrind</b><p>Runs your program in calltree and then displays profiler information in KCachegrind."));
00068
00069 mainWindow()->embedOutputView( m_widget, "Valgrind", i18n("Valgrind memory leak check") );
00070 }
00071
00072
00073 ValgrindPart::~ValgrindPart()
00074 {
00075 if ( m_widget )
00076 mainWindow()->removeView( m_widget );
00077 delete m_widget;
00078 delete proc;
00079 }
00080
00081 void ValgrindPart::projectOpened()
00082 {
00083 _lastExec.truncate( 0 );
00084 }
00085
00086 void ValgrindPart::loadOutput()
00087 {
00088 QString fName = KFileDialog::getOpenFileName(QString::null, "*", 0, i18n("Open Valgrind Output"));
00089 if ( fName.isEmpty() )
00090 return;
00091
00092 QFile f( fName );
00093 if ( !f.open( IO_ReadOnly ) ) {
00094 KMessageBox::sorry( 0, i18n("Could not open valgrind output: %1").arg(fName) );
00095 return;
00096 }
00097
00098 clear();
00099 getActiveFiles();
00100
00101 QTextStream stream( &f );
00102 while ( !stream.atEnd() ) {
00103 receivedString( stream.readLine() + "\n" );
00104 }
00105 f.close();
00106 }
00107
00108 void ValgrindPart::getActiveFiles()
00109 {
00110 activeFiles.clear();
00111 if ( project() ) {
00112 QStringList projectFiles = project()->allFiles();
00113 QString projectDirectory = project()->projectDirectory();
00114 KURL url;
00115 for ( QStringList::Iterator it = projectFiles.begin(); it != projectFiles.end(); ++it ) {
00116 KURL url( projectDirectory + "/" + (*it) );
00117 url.cleanPath( true );
00118 activeFiles += url.path();
00119 kdDebug() << "set project file: " << url.path().latin1() << endl;
00120 }
00121 }
00122 }
00123
00124 static void guessActiveItem( ValgrindItem& item, const QStringList activeFiles )
00125 {
00126 if ( activeFiles.isEmpty() && item.backtrace().isEmpty() )
00127 return;
00128 for ( ValgrindItem::BacktraceList::Iterator it = item.backtrace().begin(); it != item.backtrace().end(); ++it ) {
00129
00130 for ( QStringList::ConstIterator it2 = activeFiles.begin(); it2 != activeFiles.end(); ++it2 ) {
00131 if ( (*it).url() == (*it2) ) {
00132 (*it).setHighlighted( true );
00133 return;
00134 }
00135 }
00136 }
00137 }
00138
00139 void ValgrindPart::appendMessage( const QString& message )
00140 {
00141 if ( message.isEmpty() )
00142 return;
00143
00144 ValgrindItem item( message );
00145 guessActiveItem( item, activeFiles );
00146 m_widget->addMessage( item );
00147 }
00148
00149 void ValgrindPart::slotExecValgrind()
00150 {
00151 ValgrindDialog* dlg = new ValgrindDialog(ValgrindDialog::Memcheck);
00152 if ( project() && _lastExec.isEmpty() ) {
00153 dlg->setExecutable( project()->mainProgram() );
00154 } else {
00155 dlg->setExecutable( _lastExec );
00156 }
00157 dlg->setParameters( _lastParams );
00158 dlg->setValExecutable( _lastValExec );
00159 dlg->setValParams( _lastValParams );
00160 kcInfo.runKc = false;
00161 _lastValExec = dlg->valExecutable();
00162 _lastValParams = dlg->valParams();
00163 if ( dlg->exec() == QDialog::Accepted ) {
00164 runValgrind( dlg->executableName(), dlg->parameters(), dlg->valExecutable(), dlg->valParams() );
00165 }
00166 }
00167
00168 void ValgrindPart::slotExecCalltree()
00169 {
00170 ValgrindDialog* dlg = new ValgrindDialog(ValgrindDialog::Calltree);
00171 if ( project() && _lastExec.isEmpty() ) {
00172 dlg->setExecutable( project()->mainProgram() );
00173 } else {
00174 dlg->setExecutable( _lastExec );
00175 }
00176 dlg->setParameters( _lastParams );
00177 dlg->setCtExecutable( _lastCtExec );
00178 dlg->setKcExecutable( _lastKcExec );
00179 dlg->setCtParams( _lastCtParams );
00180 kcInfo.runKc = true;
00181 kcInfo.kcPath = dlg->kcExecutable();
00182
00183 if ( dlg->exec() == QDialog::Accepted ) {
00184 runValgrind( dlg->executableName(), dlg->parameters(), dlg->ctExecutable(), dlg->ctParams() );
00185 }
00186 _lastKcExec = dlg->kcExecutable();
00187 _lastCtExec = dlg->ctExecutable();
00188 _lastCtParams = dlg->ctParams();
00189 }
00190
00191 void ValgrindPart::slotKillValgrind()
00192 {
00193 if ( proc )
00194 proc->kill();
00195 }
00196
00197 void ValgrindPart::slotStopButtonClicked( KDevPlugin* which )
00198 {
00199 if ( which != 0 && which != this )
00200 return;
00201 slotKillValgrind();
00202 }
00203
00204 void ValgrindPart::clear()
00205 {
00206 m_widget->clear();
00207 currentMessage = QString::null;
00208 currentPid = -1;
00209 lastPiece = QString::null;
00210 }
00211
00212 void ValgrindPart::runValgrind( const QString& exec, const QString& params, const QString& valExec, const QString& valParams )
00213 {
00214 if ( proc->isRunning() ) {
00215 KMessageBox::sorry( 0, i18n( "There is already an instance of valgrind running." ) );
00216 return;
00218 }
00219
00220 clear();
00221
00222 getActiveFiles();
00223
00224
00225 proc->clearArguments();
00226 *proc << valExec << valParams << exec << params;
00227 proc->start( KProcess::NotifyOnExit, KProcess::AllOutput );
00228 mainWindow()->raiseView( m_widget );
00229 core()->running( this, true );
00230
00231 _lastExec = exec;
00232 _lastParams = params;
00233 }
00234
00235 void ValgrindPart::receivedStdout( KProcess*, char* , int )
00236 {
00237
00238 }
00239
00240 void ValgrindPart::receivedStderr( KProcess*, char* msg, int len )
00241 {
00242 receivedString( QString::fromLocal8Bit( msg, len ) );
00243 }
00244
00245 void ValgrindPart::receivedString( const QString& str )
00246 {
00247 QString rmsg = lastPiece + str;
00248 QStringList lines = QStringList::split( "\n", rmsg );
00249
00250
00251
00252 if ( !rmsg.endsWith( "\n" ) ) {
00253
00254
00255 lastPiece = lines.back();
00256 lines.pop_back();
00257 } else {
00258 lastPiece = QString::null;
00259 }
00260 appendMessages( lines );
00261 }
00262
00263 void ValgrindPart::appendMessages( const QStringList& lines )
00264 {
00265 QRegExp valRe( "==(\\d+)== (.*)" );
00266
00267 for ( QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it ) {
00268 if ( valRe.search( *it ) < 0 )
00269 continue;
00270
00271 int cPid = valRe.cap( 1 ).toInt();
00272
00273 if ( valRe.cap( 2 ).isEmpty() ) {
00274 appendMessage( currentMessage );
00275 currentMessage = QString::null;
00276 } else if ( cPid != currentPid ) {
00277 appendMessage( currentMessage );
00278 currentMessage = *it;
00279 currentPid = cPid;
00280 } else {
00281 if ( !currentMessage.isEmpty() )
00282 currentMessage += "\n";
00283 currentMessage += *it;
00284 }
00285 }
00286 }
00287
00288 void ValgrindPart::processExited( KProcess* p )
00289 {
00290 if ( p == proc ) {
00291 appendMessage( currentMessage + lastPiece );
00292 currentMessage = QString::null;
00293 lastPiece = QString::null;
00294 core()->running( this, false );
00295
00296 if (kcInfo.runKc)
00297 {
00298 KProcess *kcProc = new KProcess;
00299
00300 *kcProc << kcInfo.kcPath;
00301 *kcProc << QString("cachegrind.out.%1").arg(p->pid());
00302 kcProc->start(KProcess::DontCare);
00303 }
00304 }
00305 }
00306
00307 void ValgrindPart::restorePartialProjectSession( const QDomElement* el )
00308 {
00309 QDomElement execElem = el->namedItem( "executable" ).toElement();
00310 _lastExec = execElem.attribute( "path", "" );
00311 _lastParams = execElem.attribute( "params", "" );
00312
00313 QDomElement valElem = el->namedItem( "valgrind" ).toElement();
00314 _lastValExec = valElem.attribute( "path", "" );
00315 _lastValParams = valElem.attribute( "params", "" );
00316
00317 QDomElement ctElem = el->namedItem( "calltree" ).toElement();
00318 _lastCtExec = ctElem.attribute( "path", "" );
00319 _lastCtParams = ctElem.attribute( "params", "" );
00320
00321 QDomElement kcElem = el->namedItem( "kcachegrind" ).toElement();
00322 _lastKcExec = kcElem.attribute( "path", "" );
00323 }
00324
00325 void ValgrindPart::savePartialProjectSession( QDomElement* el )
00326 {
00327 QDomDocument domDoc = el->ownerDocument();
00328 if ( domDoc.isNull() )
00329 return;
00330
00331 QDomElement execElem = domDoc.createElement( "executable" );
00332 execElem.setAttribute( "path", _lastExec );
00333 execElem.setAttribute( "params", _lastParams );
00334
00335 QDomElement valElem = domDoc.createElement( "valgrind" );
00336 valElem.setAttribute( "path", _lastValExec );
00337 valElem.setAttribute( "params", _lastValParams );
00338
00339 QDomElement ctElem = domDoc.createElement( "calltree" );
00340 ctElem.setAttribute( "path", _lastCtExec );
00341 ctElem.setAttribute( "params", _lastCtParams );
00342
00343 QDomElement kcElem = domDoc.createElement( "kcachegrind" );
00344 kcElem.setAttribute( "path", _lastKcExec );
00345
00346 el->appendChild( execElem );
00347 el->appendChild( valElem );
00348 el->appendChild( ctElem );
00349 el->appendChild( kcElem );
00350 }
00351
00352 #include "valgrind_part.moc"