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.
tdevelop/parts/valgrind/valgrind_part.cpp

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"