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
00048 QWhatsThis::add( m_widget, i18n(
"<b>Valgrind</b><p>Shows the output of the valgrind. Valgrind detects<br>"
00049
"use of uninitialised memory<br>"
00050
"reading/writing memory after it has been free'd<br>"
00051
"reading/writing off the end of malloc'd blocks<br>"
00052
"reading/writing inappropriate areas on the stack<br>"
00053
"memory leaks -- where pointers to malloc'd blocks are lost forever<br>"
00054
"passing of uninitialised and/or unaddressible memory to system calls<br>"
00055
"mismatched use of malloc/new/new [] vs free/delete/delete []<br>"
00056
"some abuses of the POSIX pthread API." ) );
00057
00058
KAction* action =
new KAction( i18n(
"&Valgrind Memory Leak Check"), 0,
this,
00059 SLOT(slotExecValgrind()), actionCollection(),
"tools_valgrind" );
00060 action->setToolTip(i18n(
"Valgrind memory leak check"));
00061 action->setWhatsThis(i18n(
"<b>Valgrind memory leak check</b><p>Runs Valgrind - a tool to help you find memory-management problems in your programs."));
00062
00063 mainWindow()->embedOutputView( m_widget,
"Valgrind", i18n(
"Valgrind memory leak check") );
00064 }
00065
00066
00067 ValgrindPart::~ValgrindPart()
00068 {
00069
if (
m_widget )
00070
mainWindow()->
removeView(
m_widget );
00071
delete m_widget;
00072
delete proc;
00073 }
00074
00075 void ValgrindPart::projectOpened()
00076 {
00077
_lastExec.truncate( 0 );
00078 }
00079
00080 void ValgrindPart::loadOutput()
00081 {
00082
QString fName = KFileDialog::getOpenFileName(QString::null,
"*", 0, i18n(
"Open Valgrind Output"));
00083
if ( fName.isEmpty() )
00084
return;
00085
00086
QFile f( fName );
00087
if ( !f.open( IO_ReadOnly ) ) {
00088 KMessageBox::sorry( 0, i18n(
"Could not open valgrind output: %1").arg(fName) );
00089
return;
00090 }
00091
00092
clear();
00093
getActiveFiles();
00094
00095
QTextStream stream( &f );
00096
while ( !stream.atEnd() ) {
00097
receivedString( stream.readLine() +
"\n" );
00098 }
00099 f.close();
00100 }
00101
00102 void ValgrindPart::getActiveFiles()
00103 {
00104
activeFiles.clear();
00105
if (
project() ) {
00106
QStringList projectFiles =
project()->
allFiles();
00107
QString projectDirectory =
project()->
projectDirectory();
00108
KURL url;
00109
for ( QStringList::Iterator it = projectFiles.begin(); it != projectFiles.end(); ++it ) {
00110
KURL url( projectDirectory +
"/" + (*it) );
00111 url.
cleanPath(
true );
00112
activeFiles += url.
path();
00113
kdDebug() <<
"set project file: " << url.
path().latin1() <<
endl;
00114 }
00115 }
00116 }
00117
00118 static void guessActiveItem(
ValgrindItem& item,
const QStringList activeFiles )
00119 {
00120
if ( activeFiles.isEmpty() && item.
backtrace().isEmpty() )
00121
return;
00122
for ( ValgrindItem::BacktraceList::Iterator it = item.
backtrace().begin(); it != item.
backtrace().end(); ++it ) {
00123
00124
for ( QStringList::ConstIterator it2 = activeFiles.begin(); it2 != activeFiles.end(); ++it2 ) {
00125
if ( (*it).url() == (*it2) ) {
00126 (*it).setHighlighted(
true );
00127
return;
00128 }
00129 }
00130 }
00131 }
00132
00133 void ValgrindPart::appendMessage(
const QString& message )
00134 {
00135
if (
message.isEmpty() )
00136
return;
00137
00138
ValgrindItem item(
message );
00139
guessActiveItem( item,
activeFiles );
00140
m_widget->addMessage( item );
00141 }
00142
00143 void ValgrindPart::slotExecValgrind()
00144 {
00145
ValgrindDialog* dlg =
new ValgrindDialog();
00146
if (
project() &&
_lastExec.isEmpty() ) {
00147 dlg->
setExecutable(
project()->mainProgram() );
00148 }
else {
00149 dlg->
setExecutable(
_lastExec );
00150 }
00151 dlg->
setParameters(
_lastParams );
00152 dlg->
setValExecutable(
_lastValExec );
00153 dlg->
setValParams(
_lastValParams );
00154
if ( dlg->exec() == QDialog::Accepted ) {
00155
runValgrind( dlg->
executableName(), dlg->
parameters(), dlg->
valExecutable(), dlg->
valParams() );
00156 }
00157 }
00158
00159 void ValgrindPart::slotKillValgrind()
00160 {
00161
if (
proc )
00162
proc->
kill();
00163 }
00164
00165 void ValgrindPart::slotStopButtonClicked(
KDevPlugin* which )
00166 {
00167
if ( which != 0 && which !=
this )
00168
return;
00169
slotKillValgrind();
00170 }
00171
00172 void ValgrindPart::clear()
00173 {
00174
m_widget->clear();
00175
currentMessage = QString::null;
00176
currentPid = -1;
00177
lastPiece = QString::null;
00178 }
00179
00180 void ValgrindPart::runValgrind(
const QString& exec,
const QString& params,
const QString& valExec,
const QString& valParams )
00181 {
00182
if (
proc->
isRunning() ) {
00183 KMessageBox::sorry( 0, i18n(
"There is already an instance of valgrind running." ) );
00184
return;
00186 }
00187
00188
clear();
00189
00190
getActiveFiles();
00191
00192
proc->
clearArguments();
00193 *
proc << valExec << valParams << exec << params;
00194
proc->
start( KProcess::NotifyOnExit, KProcess::AllOutput );
00195
mainWindow()->
raiseView(
m_widget );
00196
core()->
running(
this,
true );
00197
00198
_lastExec = exec;
00199
_lastParams = params;
00200
_lastValExec = valExec;
00201
_lastValParams = valParams;
00202 }
00203
00204 void ValgrindPart::receivedStdout(
KProcess*,
char* ,
int )
00205 {
00206
00207 }
00208
00209 void ValgrindPart::receivedStderr(
KProcess*,
char* msg,
int len )
00210 {
00211
receivedString( QString::fromLocal8Bit( msg,
len ) );
00212 }
00213
00214 void ValgrindPart::receivedString(
const QString& str )
00215 {
00216
QString rmsg =
lastPiece + str;
00217
QStringList lines = QStringList::split(
"\n", rmsg );
00218
00219
00220
00221
if ( !rmsg.endsWith(
"\n" ) ) {
00222
00223
00224
lastPiece = lines.back();
00225 lines.pop_back();
00226 }
else {
00227
lastPiece = QString::null;
00228 }
00229
appendMessages( lines );
00230 }
00231
00232 void ValgrindPart::appendMessages(
const QStringList& lines )
00233 {
00234
QRegExp valRe(
"==(\\d+)== (.*)" );
00235
00236
for ( QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it ) {
00237
if ( valRe.search( *it ) < 0 )
00238
continue;
00239
00240
int cPid = valRe.cap( 1 ).toInt();
00241
00242
if ( valRe.cap( 2 ).isEmpty() ) {
00243
appendMessage(
currentMessage );
00244
currentMessage = QString::null;
00245 }
else if ( cPid !=
currentPid ) {
00246
appendMessage(
currentMessage );
00247
currentMessage = *it;
00248
currentPid = cPid;
00249 }
else {
00250
if ( !
currentMessage.isEmpty() )
00251
currentMessage +=
"\n";
00252
currentMessage += *it;
00253 }
00254 }
00255 }
00256
00257 void ValgrindPart::processExited(
KProcess* p )
00258 {
00259
if ( p ==
proc ) {
00260
appendMessage(
currentMessage +
lastPiece );
00261
currentMessage = QString::null;
00262 lastPiece = QString::null;
00263
core()->
running(
this,
false );
00264 }
00265 }
00266
00267 void ValgrindPart::restorePartialProjectSession(
const QDomElement* el )
00268 {
00269
QDomElement execElem = el->namedItem(
"executable" ).toElement();
00270
_lastExec = execElem.attribute(
"path",
"" );
00271
_lastParams = execElem.attribute(
"params",
"" );
00272
00273
QDomElement valElem = el->namedItem(
"valgrind" ).toElement();
00274
_lastValExec = valElem.attribute(
"path",
"" );
00275
_lastValParams = valElem.attribute(
"params",
"" );
00276 }
00277
00278 void ValgrindPart::savePartialProjectSession(
QDomElement* el )
00279 {
00280
QDomDocument domDoc = el->ownerDocument();
00281
if ( domDoc.isNull() )
00282
return;
00283
00284
QDomElement execElem = domDoc.createElement(
"executable" );
00285 execElem.setAttribute(
"path",
_lastExec );
00286 execElem.setAttribute(
"params",
_lastParams );
00287
00288
QDomElement valElem = domDoc.createElement(
"valgrind" );
00289 valElem.setAttribute(
"path",
_lastValExec );
00290 valElem.setAttribute(
"params",
_lastValParams );
00291
00292 el->appendChild( execElem );
00293 el->appendChild( valElem );
00294 }
00295
00296
#include "valgrind_part.moc"