summaryrefslogtreecommitdiffstats
path: root/parts/valgrind/valgrind_part.cpp
blob: 5aaf66aa867cb3010dbefa5691a0b327822dfb66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#include "valgrind_part.h"

#include <tqwhatsthis.h>
#include <tqregexp.h>
#include <tqfile.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( TQObject *parent, const char *name, const TQStringList& )
  : KDevPlugin( &data, parent, name ? name : "ValgrindPart" )
{
  setInstance( ValgrindFactory::instance() );
  setXMLFile( "kdevpart_valgrind.rc" );

  proc = new KShellProcess();
  connect( proc, TQT_SIGNAL(receivedStdout( KProcess*, char*, int )),
           this, TQT_SLOT(receivedStdout( KProcess*, char*, int )) );
  connect( proc, TQT_SIGNAL(receivedStderr( KProcess*, char*, int )),
           this, TQT_SLOT(receivedStderr( KProcess*, char*, int )) );
  connect( proc, TQT_SIGNAL(processExited( KProcess* )),
           this, TQT_SLOT(processExited( KProcess* )) );
  connect( core(), TQT_SIGNAL(stopButtonClicked(KDevPlugin*)),
           this, TQT_SLOT(slotStopButtonClicked(KDevPlugin*)) );
  connect( core(), TQT_SIGNAL(projectOpened()),
           this, TQT_SLOT(projectOpened()) );

  m_widget = new ValgrindWidget( this );
  m_widget->setIcon( SmallIcon("fork") );
  m_widget->setCaption(i18n("Valgrind Output"));

  TQWhatsThis::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,
	       TQT_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,
	       TQT_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()
{
  TQString fName = KFileDialog::getOpenFileName(TQString(), "*", 0, i18n("Open Valgrind Output"));
  if ( fName.isEmpty() )
    return;

  TQFile f( fName );
  if ( !f.open( IO_ReadOnly ) ) {
    KMessageBox::sorry( 0, i18n("Could not open valgrind output: %1").arg(fName) );
    return;
  }

  clear();
  getActiveFiles();

  TQTextStream stream( &f );
  while ( !stream.atEnd() ) {
    receivedString( stream.readLine() + "\n" );
  }
  f.close();
}

void ValgrindPart::getActiveFiles()
{
  activeFiles.clear();
  if ( project() ) {
    TQStringList projectFiles = project()->allFiles();
    TQString projectDirectory = project()->projectDirectory();
    KURL url;
    for ( TQStringList::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 TQStringList 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 ( TQStringList::ConstIterator it2 = activeFiles.begin(); it2 != activeFiles.end(); ++it2 ) {
      if ( (*it).url() == (*it2) ) {
        (*it).setHighlighted( true );
        return;
      }
    }
  }
}

void ValgrindPart::appendMessage( const TQString& 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() == TQDialog::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() == TQDialog::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 = TQString();
  currentPid = -1;
  lastPiece = TQString();
}

void ValgrindPart::runValgrind( const TQString& exec, const TQString& params, const TQString& valExec, const TQString& 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();

  TQStringList envVarList;
  DomUtil::PairList::ConstIterator it;
  for (it = run_envvars.begin(); it != run_envvars.end(); ++it)
  {
    envVarList << TQString("%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: " <<TQString::fromLocal8Bit( msg, len ) << endl;
}

void ValgrindPart::receivedStderr( KProcess*, char* msg, int len )
{
  receivedString( TQString::fromLocal8Bit( msg, len ) );
}

void ValgrindPart::receivedString( const TQString& str )
{
  TQString rmsg = lastPiece + str;
  TQStringList lines = TQStringList::split( "\n", rmsg );

//  kdDebug() << "got: " << TQString::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 = TQString();
  }
  appendMessages( lines );
}

void ValgrindPart::appendMessages( const TQStringList& lines )
{
  TQRegExp valRe( "==(\\d+)== (.*)" );

  for ( TQStringList::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 = TQString();
    } 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 = TQString();
    lastPiece = TQString();
    core()->running( this, false );

    if (kcInfo.runKc)
    {
        KProcess *kcProc = new KProcess;
//        kcProc->setWorkingDirectory(kcInfo.kcWorkDir);
        *kcProc << kcInfo.kcPath;
        *kcProc << TQString("callgrind.out.%1").arg(p->pid());
        kcProc->start(KProcess::DontCare);
    }
  }
}

void ValgrindPart::restorePartialProjectSession( const TQDomElement* el )
{
  TQDomElement execElem = el->namedItem( "executable" ).toElement();
  _lastExec = execElem.attribute( "path", "" );
  _lastParams = execElem.attribute( "params", "" );

  TQDomElement valElem = el->namedItem( "valgrind" ).toElement();
  _lastValExec = valElem.attribute( "path", "" );
  _lastValParams = valElem.attribute( "params", "" );

  TQDomElement ctElem = el->namedItem( "calltree" ).toElement();
  _lastCtExec = ctElem.attribute( "path", "" );
  _lastCtParams = ctElem.attribute( "params", "" );

  TQDomElement kcElem = el->namedItem( "tdecachegrind" ).toElement();
  _lastKcExec = kcElem.attribute( "path", "" );
}

void ValgrindPart::savePartialProjectSession( TQDomElement* el )
{
  TQDomDocument domDoc = el->ownerDocument();
  if ( domDoc.isNull() )
    return;

  TQDomElement execElem = domDoc.createElement( "executable" );
  execElem.setAttribute( "path", _lastExec );
  execElem.setAttribute( "params", _lastParams );

  TQDomElement valElem = domDoc.createElement( "valgrind" );
  valElem.setAttribute( "path", _lastValExec );
  valElem.setAttribute( "params", _lastValParams );

  TQDomElement ctElem = domDoc.createElement( "calltree" );
  ctElem.setAttribute( "path", _lastCtExec );
  ctElem.setAttribute( "params", _lastCtParams );

  TQDomElement kcElem = domDoc.createElement( "tdecachegrind" );
  kcElem.setAttribute( "path", _lastKcExec );

  el->appendChild( execElem );
  el->appendChild( valElem );
  el->appendChild( ctElem );
  el->appendChild( kcElem );
}

#include "valgrind_part.moc"