You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
367 lines
11 KiB
367 lines
11 KiB
#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"
|