diff options
Diffstat (limited to 'parts/valgrind/valgrind_part.cpp')
-rw-r--r-- | parts/valgrind/valgrind_part.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/parts/valgrind/valgrind_part.cpp b/parts/valgrind/valgrind_part.cpp new file mode 100644 index 00000000..868faf28 --- /dev/null +++ b/parts/valgrind/valgrind_part.cpp @@ -0,0 +1,366 @@ +#include "valgrind_part.h" + +#include <qwhatsthis.h> +#include <qregexp.h> +#include <qfile.h> + +#include <kiconloader.h> +#include <klocale.h> +#include <kdevgenericfactory.h> +#include <kaction.h> +#include <kprocess.h> +#include <kmessagebox.h> +#include <kfiledialog.h> +#include <kdebug.h> + +#include "kdevcore.h" +#include "kdevmainwindow.h" +#include "kdevproject.h" +#include "kdevplugininfo.h" + +#include "valgrind_widget.h" +#include "valgrind_dialog.h" +#include "valgrinditem.h" + +typedef KDevGenericFactory<ValgrindPart> ValgrindFactory; +static const KDevPluginInfo data("kdevvalgrind"); +K_EXPORT_COMPONENT_FACTORY( libkdevvalgrind, ValgrindFactory( data ) ) + +ValgrindPart::ValgrindPart( QObject *parent, const char *name, const QStringList& ) + : KDevPlugin( &data, parent, name ? name : "ValgrindPart" ) +{ + setInstance( ValgrindFactory::instance() ); + setXMLFile( "kdevpart_valgrind.rc" ); + + proc = new KShellProcess(); + connect( proc, SIGNAL(receivedStdout( KProcess*, char*, int )), + this, SLOT(receivedStdout( KProcess*, char*, int )) ); + connect( proc, SIGNAL(receivedStderr( KProcess*, char*, int )), + this, SLOT(receivedStderr( KProcess*, char*, int )) ); + connect( proc, SIGNAL(processExited( KProcess* )), + this, SLOT(processExited( KProcess* )) ); + connect( core(), SIGNAL(stopButtonClicked(KDevPlugin*)), + this, SLOT(slotStopButtonClicked(KDevPlugin*)) ); + connect( core(), SIGNAL(projectOpened()), + this, SLOT(projectOpened()) ); + + m_widget = new ValgrindWidget( this ); + m_widget->setIcon( SmallIcon("fork") ); + m_widget->setCaption(i18n("Valgrind Output")); + + QWhatsThis::add( m_widget, i18n( "<b>Valgrind</b><p>Shows the output of the valgrind. Valgrind detects<br>" + "use of uninitialized memory<br>" + "reading/writing memory after it has been free'd<br>" + "reading/writing off the end of malloc'd blocks<br>" + "reading/writing inappropriate areas on the stack<br>" + "memory leaks -- where pointers to malloc'd blocks are lost forever<br>" + "passing of uninitialised and/or unaddressable memory to system calls<br>" + "mismatched use of malloc/new/new [] vs free/delete/delete []<br>" + "some abuses of the POSIX pthread API." ) ); + + KAction* action = new KAction( i18n("&Valgrind Memory Leak Check"), 0, this, + SLOT(slotExecValgrind()), actionCollection(), "tools_valgrind" ); + action->setToolTip(i18n("Valgrind memory leak check")); + action->setWhatsThis(i18n("<b>Valgrind memory leak check</b><p>Runs Valgrind - a tool to help you find memory-management problems in your programs.")); + + action = new KAction( i18n("P&rofile with KCachegrind"), 0, this, + SLOT(slotExecCalltree()), actionCollection(), "tools_calltree" ); + action->setToolTip(i18n("Profile with KCachegrind")); + action->setWhatsThis(i18n("<b>Profile with KCachegrind</b><p>Runs your program in calltree and then displays profiler information in KCachegrind.")); + + mainWindow()->embedOutputView( m_widget, "Valgrind", i18n("Valgrind memory leak check") ); +} + + +ValgrindPart::~ValgrindPart() +{ + if ( m_widget ) + mainWindow()->removeView( m_widget ); + delete m_widget; + delete proc; +} + +void ValgrindPart::projectOpened() +{ + _lastExec.truncate( 0 ); +} + +void ValgrindPart::loadOutput() +{ + QString fName = KFileDialog::getOpenFileName(QString::null, "*", 0, i18n("Open Valgrind Output")); + if ( fName.isEmpty() ) + return; + + QFile f( fName ); + if ( !f.open( IO_ReadOnly ) ) { + KMessageBox::sorry( 0, i18n("Could not open valgrind output: %1").arg(fName) ); + return; + } + + clear(); + getActiveFiles(); + + QTextStream stream( &f ); + while ( !stream.atEnd() ) { + receivedString( stream.readLine() + "\n" ); + } + f.close(); +} + +void ValgrindPart::getActiveFiles() +{ + activeFiles.clear(); + if ( project() ) { + QStringList projectFiles = project()->allFiles(); + QString projectDirectory = project()->projectDirectory(); + KURL url; + for ( QStringList::Iterator it = projectFiles.begin(); it != projectFiles.end(); ++it ) { + KURL url( projectDirectory + "/" + (*it) ); + url.cleanPath( true ); + activeFiles += url.path(); + kdDebug() << "set project file: " << url.path().latin1() << endl; + } + } +} + +static void guessActiveItem( ValgrindItem& item, const QStringList activeFiles ) +{ + if ( activeFiles.isEmpty() && item.backtrace().isEmpty() ) + return; + for ( ValgrindItem::BacktraceList::Iterator it = item.backtrace().begin(); it != item.backtrace().end(); ++it ) { + // active: first line of backtrace that lies in project source file + for ( QStringList::ConstIterator it2 = activeFiles.begin(); it2 != activeFiles.end(); ++it2 ) { + if ( (*it).url() == (*it2) ) { + (*it).setHighlighted( true ); + return; + } + } + } +} + +void ValgrindPart::appendMessage( const QString& message ) +{ + if ( message.isEmpty() ) + return; + + ValgrindItem item( message ); + guessActiveItem( item, activeFiles ); + m_widget->addMessage( item ); +} + +void ValgrindPart::slotExecValgrind() +{ + ValgrindDialog* dlg = new ValgrindDialog(ValgrindDialog::Memcheck); + if ( project() && _lastExec.isEmpty() ) { + dlg->setExecutable( project()->mainProgram() ); + } else { + dlg->setExecutable( _lastExec ); + } + dlg->setParameters( _lastParams ); + dlg->setValExecutable( _lastValExec ); + dlg->setValParams( _lastValParams ); + kcInfo.runKc = false; + _lastValExec = dlg->valExecutable(); + _lastValParams = dlg->valParams(); + if ( dlg->exec() == QDialog::Accepted ) { + runValgrind( dlg->executableName(), dlg->parameters(), dlg->valExecutable(), dlg->valParams() ); + } +} + +void ValgrindPart::slotExecCalltree() +{ + ValgrindDialog* dlg = new ValgrindDialog(ValgrindDialog::Calltree); + if ( project() && _lastExec.isEmpty() ) { + dlg->setExecutable( project()->mainProgram() ); + } else { + dlg->setExecutable( _lastExec ); + } + dlg->setParameters( _lastParams ); + dlg->setCtExecutable( _lastCtExec ); + dlg->setKcExecutable( _lastKcExec ); + dlg->setCtParams( _lastCtParams ); + kcInfo.runKc = true; + kcInfo.kcPath = dlg->kcExecutable(); +// kcInfo.kcWorkDir = KURL(dlg->executableName()).directory(); + if ( dlg->exec() == QDialog::Accepted ) { + runValgrind( dlg->executableName(), dlg->parameters(), dlg->ctExecutable(), dlg->ctParams() ); + } + _lastKcExec = dlg->kcExecutable(); + _lastCtExec = dlg->ctExecutable(); + _lastCtParams = dlg->ctParams(); +} + +void ValgrindPart::slotKillValgrind() +{ + if ( proc ) + proc->kill(); +} + +void ValgrindPart::slotStopButtonClicked( KDevPlugin* which ) +{ + if ( which != 0 && which != this ) + return; + slotKillValgrind(); +} + +void ValgrindPart::clear() +{ + m_widget->clear(); + currentMessage = QString::null; + currentPid = -1; + lastPiece = QString::null; +} + +void ValgrindPart::runValgrind( const QString& exec, const QString& params, const QString& valExec, const QString& valParams ) +{ + if ( proc->isRunning() ) { + KMessageBox::sorry( 0, i18n( "There is already an instance of valgrind running." ) ); + return; + /// @todo - ask for forced kill + } + + clear(); + + getActiveFiles(); + +// proc->setWorkingDirectory(KURL(exec).directory()); + proc->clearArguments(); + + DomUtil::PairList run_envvars; + if (project()) + run_envvars = project()->runEnvironmentVars(); + + QStringList envVarList; + DomUtil::PairList::ConstIterator it; + for (it = run_envvars.begin(); it != run_envvars.end(); ++it) + { + envVarList << QString("%1=\"%2\" ").arg((*it).first).arg((*it).second); + } + + *proc << envVarList.join("") << valExec << valParams << exec << params; + proc->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + mainWindow()->raiseView( m_widget ); + core()->running( this, true ); + + _lastExec = exec; + _lastParams = params; +} + +void ValgrindPart::receivedStdout( KProcess*, char* /* msg */, int /* len */ ) +{ + //kdDebug() << "got StdOut: " <<QString::fromLocal8Bit( msg, len ) << endl; +} + +void ValgrindPart::receivedStderr( KProcess*, char* msg, int len ) +{ + receivedString( QString::fromLocal8Bit( msg, len ) ); +} + +void ValgrindPart::receivedString( const QString& str ) +{ + QString rmsg = lastPiece + str; + QStringList lines = QStringList::split( "\n", rmsg ); + +// kdDebug() << "got: " << QString::fromLocal8Bit( msg, len ) << endl; + + if ( !rmsg.endsWith( "\n" ) ) { + // the last message is trucated, we'll receive + // the rest in the next call + lastPiece = lines.back(); + lines.pop_back(); + } else { + lastPiece = QString::null; + } + appendMessages( lines ); +} + +void ValgrindPart::appendMessages( const QStringList& lines ) +{ + QRegExp valRe( "==(\\d+)== (.*)" ); + + for ( QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it ) { + if ( valRe.search( *it ) < 0 ) + continue; + + int cPid = valRe.cap( 1 ).toInt(); + + if ( valRe.cap( 2 ).isEmpty() ) { + appendMessage( currentMessage ); + currentMessage = QString::null; + } else if ( cPid != currentPid ) { + appendMessage( currentMessage ); + currentMessage = *it; + currentPid = cPid; + } else { + if ( !currentMessage.isEmpty() ) + currentMessage += "\n"; + currentMessage += *it; + } + } +} + +void ValgrindPart::processExited( KProcess* p ) +{ + if ( p == proc ) { + appendMessage( currentMessage + lastPiece ); + currentMessage = QString::null; + lastPiece = QString::null; + core()->running( this, false ); + + if (kcInfo.runKc) + { + KProcess *kcProc = new KProcess; +// kcProc->setWorkingDirectory(kcInfo.kcWorkDir); + *kcProc << kcInfo.kcPath; + *kcProc << QString("callgrind.out.%1").arg(p->pid()); + kcProc->start(KProcess::DontCare); + } + } +} + +void ValgrindPart::restorePartialProjectSession( const QDomElement* el ) +{ + QDomElement execElem = el->namedItem( "executable" ).toElement(); + _lastExec = execElem.attribute( "path", "" ); + _lastParams = execElem.attribute( "params", "" ); + + QDomElement valElem = el->namedItem( "valgrind" ).toElement(); + _lastValExec = valElem.attribute( "path", "" ); + _lastValParams = valElem.attribute( "params", "" ); + + QDomElement ctElem = el->namedItem( "calltree" ).toElement(); + _lastCtExec = ctElem.attribute( "path", "" ); + _lastCtParams = ctElem.attribute( "params", "" ); + + QDomElement kcElem = el->namedItem( "kcachegrind" ).toElement(); + _lastKcExec = kcElem.attribute( "path", "" ); +} + +void ValgrindPart::savePartialProjectSession( QDomElement* el ) +{ + QDomDocument domDoc = el->ownerDocument(); + if ( domDoc.isNull() ) + return; + + QDomElement execElem = domDoc.createElement( "executable" ); + execElem.setAttribute( "path", _lastExec ); + execElem.setAttribute( "params", _lastParams ); + + QDomElement valElem = domDoc.createElement( "valgrind" ); + valElem.setAttribute( "path", _lastValExec ); + valElem.setAttribute( "params", _lastValParams ); + + QDomElement ctElem = domDoc.createElement( "calltree" ); + ctElem.setAttribute( "path", _lastCtExec ); + ctElem.setAttribute( "params", _lastCtParams ); + + QDomElement kcElem = domDoc.createElement( "kcachegrind" ); + kcElem.setAttribute( "path", _lastKcExec ); + + el->appendChild( execElem ); + el->appendChild( valElem ); + el->appendChild( ctElem ); + el->appendChild( kcElem ); +} + +#include "valgrind_part.moc" |