summaryrefslogtreecommitdiffstats
path: root/parts/valgrind/valgrind_part.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'parts/valgrind/valgrind_part.cpp')
-rw-r--r--parts/valgrind/valgrind_part.cpp366
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"