KDevelop API Documentation

valgrind_part.cpp

Go to the documentation of this file.
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     // active: first line of backtrace that lies in project source file
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 //  kcInfo.kcWorkDir = KURL(dlg->executableName()).directory();
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 //  proc->setWorkingDirectory(KURL(exec).directory());
00225   proc->clearArguments();
00226   *proc << valExec << /*"--tool=memcheck" << */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* /* msg */, int /* len */ )
00236 {
00237   //kdDebug() << "got StdOut: " <<QString::fromLocal8Bit( msg, len ) << endl;
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 //  kdDebug() << "got: " << QString::fromLocal8Bit( msg, len ) << endl;
00251 
00252   if ( !rmsg.endsWith( "\n" ) ) {
00253     // the last message is trucated, we'll receive
00254     // the rest in the next call
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 //        kcProc->setWorkingDirectory(kcInfo.kcWorkDir);
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"
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 22 09:22:42 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003