# 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 <tdefiledialog.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 ( TDEProcess * , char * , int ) ) ,
this , TQT_SLOT ( receivedStdout ( TDEProcess * , char * , int ) ) ) ;
connect ( proc , TQT_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) ,
this , TQT_SLOT ( receivedStderr ( TDEProcess * , char * , int ) ) ) ;
connect ( proc , TQT_SIGNAL ( processExited ( TDEProcess * ) ) ,
this , TQT_SLOT ( processExited ( TDEProcess * ) ) ) ;
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. " ) ) ;
TDEAction * action = new TDEAction ( 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 TDEAction ( 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 ( TDEProcess : : NotifyOnExit , TDEProcess : : AllOutput ) ;
mainWindow ( ) - > raiseView ( m_widget ) ;
core ( ) - > running ( this , true ) ;
_lastExec = exec ;
_lastParams = params ;
}
void ValgrindPart : : receivedStdout ( TDEProcess * , char * /* msg */ , int /* len */ )
{
//kdDebug() << "got StdOut: " <<TQString::fromLocal8Bit( msg, len ) << endl;
}
void ValgrindPart : : receivedStderr ( TDEProcess * , 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 ( TDEProcess * p )
{
if ( p = = proc ) {
appendMessage ( currentMessage + lastPiece ) ;
currentMessage = TQString ( ) ;
lastPiece = TQString ( ) ;
core ( ) - > running ( this , false ) ;
if ( kcInfo . runKc )
{
TDEProcess * kcProc = new TDEProcess ;
// kcProc->setWorkingDirectory(kcInfo.kcWorkDir);
* kcProc < < kcInfo . kcPath ;
* kcProc < < TQString ( " callgrind.out.%1 " ) . arg ( p - > pid ( ) ) ;
kcProc - > start ( TDEProcess : : 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"