| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | /***************************************************************************
 | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** This file is part of Qt Creator | 
					
						
							|  |  |  | ** | 
					
						
							| 
									
										
										
										
											2009-01-13 19:21:51 +01:00
										 |  |  | ** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | ** | 
					
						
							|  |  |  | ** Contact:  Qt Software Information (qt-info@nokia.com) | 
					
						
							|  |  |  | ** | 
					
						
							| 
									
										
										
										
											2008-12-02 14:17:16 +01:00
										 |  |  | ** | 
					
						
							|  |  |  | ** Non-Open Source Usage | 
					
						
							|  |  |  | ** | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | ** Licensees may use this file in accordance with the Qt Beta Version | 
					
						
							|  |  |  | ** License Agreement, Agreement version 2.2 provided with the Software or, | 
					
						
							|  |  |  | ** alternatively, in accordance with the terms contained in a written | 
					
						
							| 
									
										
										
										
											2008-12-02 14:17:16 +01:00
										 |  |  | ** agreement between you and Nokia. | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** GNU General Public License Usage | 
					
						
							|  |  |  | ** | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | ** Alternatively, this file may be used under the terms of the GNU General | 
					
						
							|  |  |  | ** Public License versions 2.0 or 3.0 as published by the Free Software | 
					
						
							|  |  |  | ** Foundation and appearing in the file LICENSE.GPL included in the packaging | 
					
						
							|  |  |  | ** of this file.  Please review the following information to ensure GNU | 
					
						
							|  |  |  | ** General Public Licensing requirements will be met: | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
 | 
					
						
							|  |  |  | ** http://www.gnu.org/copyleft/gpl.html.
 | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** In addition, as a special exception, Nokia gives you certain additional | 
					
						
							| 
									
										
										
										
											2008-12-02 14:17:16 +01:00
										 |  |  | ** rights. These rights are described in the Nokia Qt GPL Exception | 
					
						
							| 
									
										
										
										
											2008-12-16 17:20:00 +01:00
										 |  |  | ** version 1.3, included in the file GPL_EXCEPTION.txt in this package. | 
					
						
							| 
									
										
										
										
											2008-12-02 14:17:16 +01:00
										 |  |  | ** | 
					
						
							|  |  |  | ***************************************************************************/ | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "gdbengine.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "debuggerconstants.h"
 | 
					
						
							|  |  |  | #include "debuggermanager.h"
 | 
					
						
							|  |  |  | #include "gdbmi.h"
 | 
					
						
							|  |  |  | #include "procinterrupt.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "disassemblerhandler.h"
 | 
					
						
							|  |  |  | #include "breakhandler.h"
 | 
					
						
							|  |  |  | #include "moduleshandler.h"
 | 
					
						
							|  |  |  | #include "registerhandler.h"
 | 
					
						
							|  |  |  | #include "stackhandler.h"
 | 
					
						
							|  |  |  | #include "watchhandler.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "startexternaldialog.h"
 | 
					
						
							|  |  |  | #include "attachexternaldialog.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-09 16:18:28 +01:00
										 |  |  | #include <utils/qtcassert.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | #include <QtCore/QDebug>
 | 
					
						
							|  |  |  | #include <QtCore/QDir>
 | 
					
						
							|  |  |  | #include <QtCore/QFileInfo>
 | 
					
						
							|  |  |  | #include <QtCore/QTime>
 | 
					
						
							|  |  |  | #include <QtCore/QTimer>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QtGui/QAction>
 | 
					
						
							|  |  |  | #include <QtGui/QLabel>
 | 
					
						
							|  |  |  | #include <QtGui/QMainWindow>
 | 
					
						
							|  |  |  | #include <QtGui/QMessageBox>
 | 
					
						
							|  |  |  | #include <QtGui/QToolTip>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <dlfcn.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace Debugger; | 
					
						
							|  |  |  | using namespace Debugger::Internal; | 
					
						
							|  |  |  | using namespace Debugger::Constants; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Q_DECLARE_METATYPE(Debugger::Internal::GdbMi); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#define DEBUG_PENDING  1
 | 
					
						
							|  |  |  | //#define DEBUG_SUBITEM  1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if DEBUG_PENDING
 | 
					
						
							|  |  |  | #   define PENDING_DEBUG(s) qDebug() << s
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #   define PENDING_DEBUG(s) 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const QString tooltipIName = "tooltip"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GdbCommandType
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum GdbCommandType | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GdbInvalidCommand = 0, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GdbShowVersion = 100, | 
					
						
							|  |  |  |     GdbFileExecAndSymbols, | 
					
						
							|  |  |  |     GdbQueryPwd, | 
					
						
							|  |  |  |     GdbQuerySources, | 
					
						
							|  |  |  |     GdbAsyncOutput2, | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |     GdbStart, | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     GdbExecRun, | 
					
						
							|  |  |  |     GdbExecRunToFunction, | 
					
						
							|  |  |  |     GdbExecStep, | 
					
						
							|  |  |  |     GdbExecNext, | 
					
						
							|  |  |  |     GdbExecStepI, | 
					
						
							|  |  |  |     GdbExecNextI, | 
					
						
							|  |  |  |     GdbExecContinue, | 
					
						
							|  |  |  |     GdbExecFinish, | 
					
						
							|  |  |  |     GdbExecJumpToLine, | 
					
						
							|  |  |  |     GdbExecInterrupt, | 
					
						
							|  |  |  |     GdbInfoShared, | 
					
						
							|  |  |  |     GdbInfoProc, | 
					
						
							| 
									
										
										
										
											2009-02-13 17:11:34 +01:00
										 |  |  |     GdbInfoThreads, | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     GdbQueryDataDumper1, | 
					
						
							|  |  |  |     GdbQueryDataDumper2, | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     GdbTemporaryContinue, | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     BreakCondition = 200, | 
					
						
							|  |  |  |     BreakEnablePending, | 
					
						
							|  |  |  |     BreakSetAnnotate, | 
					
						
							|  |  |  |     BreakDelete, | 
					
						
							|  |  |  |     BreakList, | 
					
						
							|  |  |  |     BreakIgnore, | 
					
						
							|  |  |  |     BreakInfo, | 
					
						
							|  |  |  |     BreakInsert, | 
					
						
							|  |  |  |     BreakInsert1, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     DisassemblerList = 300, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ModulesList = 400, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     RegisterListNames = 500, | 
					
						
							|  |  |  |     RegisterListValues, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     StackSelectThread = 600, | 
					
						
							|  |  |  |     StackListThreads, | 
					
						
							|  |  |  |     StackListFrames, | 
					
						
							|  |  |  |     StackListLocals, | 
					
						
							|  |  |  |     StackListArguments, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     WatchVarAssign = 700,             // data changed by user
 | 
					
						
							|  |  |  |     WatchVarListChildren, | 
					
						
							|  |  |  |     WatchVarCreate, | 
					
						
							|  |  |  |     WatchEvaluateExpression, | 
					
						
							|  |  |  |     WatchToolTip, | 
					
						
							|  |  |  |     WatchDumpCustomSetup, | 
					
						
							|  |  |  |     WatchDumpCustomValue1,           // waiting for gdb ack
 | 
					
						
							| 
									
										
										
										
											2009-01-21 11:41:32 +01:00
										 |  |  |     WatchDumpCustomValue2,           // waiting for actual data
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     WatchDumpCustomEditValue, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString dotEscape(QString str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     str.replace(' ', '.'); | 
					
						
							|  |  |  |     str.replace('\\', '.'); | 
					
						
							|  |  |  |     str.replace('/', '.'); | 
					
						
							|  |  |  |     return str; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString currentTime() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return QTime::currentTime().toString("hh:mm:ss.zzz"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ¤tToken() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static int token = 0; | 
					
						
							|  |  |  |     return token; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isSkippableFunction(const QString &funcName, const QString &fileName) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/qobject.cpp")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/moc_qobject.cpp")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/qmetaobject.cpp")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith(".moc")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (funcName.endsWith("::qt_metacall")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isLeavableFunction(const QString &funcName, const QString &fileName) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (funcName.endsWith("QObjectPrivate::setCurrentSender")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/qmetaobject.cpp") | 
					
						
							|  |  |  |             && funcName.endsWith("QMetaObject::methodOffset")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/qobject.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/qobject.cpp") | 
					
						
							|  |  |  |             && funcName.endsWith("QObjectConnectionListVector::at")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("kernel/qobject.cpp") | 
					
						
							|  |  |  |             && funcName.endsWith("~QObject")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("thread/qmutex.cpp")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("thread/qthread.cpp")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("thread/qthread_unix.cpp")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("thread/qmutex.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.contains("thread/qbasicatomic")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.contains("thread/qorderedmutexlocker_p")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.contains("arch/qatomic")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("tools/qvector.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("tools/qlist.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("tools/qhash.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("tools/qmap.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("tools/qstring.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (fileName.endsWith("global/qglobal.h")) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  | static QString startSymbolName() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |     return "WinMainCRTStartup"; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     return "_start"; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GdbEngine
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GdbEngine::GdbEngine(DebuggerManager *parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     q = parent; | 
					
						
							|  |  |  |     qq = parent->engineInterface(); | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  |     initializeVariables(); | 
					
						
							|  |  |  |     initializeConnections(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GdbEngine::~GdbEngine() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     // prevent sending error messages afterwards
 | 
					
						
							|  |  |  |     m_gdbProc.disconnect(this); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  | void GdbEngine::initializeConnections() | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     // Gdb Process interaction
 | 
					
						
							|  |  |  |     connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this, | 
					
						
							|  |  |  |         SLOT(gdbProcError(QProcess::ProcessError))); | 
					
						
							|  |  |  |     connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this, | 
					
						
							|  |  |  |         SLOT(readGdbStandardOutput())); | 
					
						
							|  |  |  |     connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this, | 
					
						
							|  |  |  |         SLOT(readGdbStandardError())); | 
					
						
							|  |  |  |     connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q, | 
					
						
							|  |  |  |         SLOT(exitDebugger())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Output
 | 
					
						
							| 
									
										
										
										
											2009-01-15 14:47:14 +01:00
										 |  |  |     connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)), | 
					
						
							|  |  |  |             SLOT(readDebugeeOutput(QByteArray))); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     connect(this, SIGNAL(gdbResponseAvailable()), | 
					
						
							|  |  |  |         this, SLOT(handleResponse()), Qt::QueuedConnection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     connect(this, SIGNAL(gdbOutputAvailable(QString,QString)), | 
					
						
							|  |  |  |         q, SLOT(showDebuggerOutput(QString,QString)), | 
					
						
							|  |  |  |         Qt::QueuedConnection); | 
					
						
							|  |  |  |     connect(this, SIGNAL(gdbInputAvailable(QString,QString)), | 
					
						
							|  |  |  |         q, SLOT(showDebuggerInput(QString,QString)), | 
					
						
							|  |  |  |         Qt::QueuedConnection); | 
					
						
							| 
									
										
										
										
											2009-01-15 14:30:49 +01:00
										 |  |  |     connect(this, SIGNAL(applicationOutputAvailable(QString)), | 
					
						
							|  |  |  |         q, SLOT(showApplicationOutput(QString)), | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         Qt::QueuedConnection); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  | void GdbEngine::initializeVariables() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_dataDumperState = DataDumperUninitialized; | 
					
						
							|  |  |  |     m_gdbVersion = 100; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_fullToShortName.clear(); | 
					
						
							|  |  |  |     m_shortToFullName.clear(); | 
					
						
							|  |  |  |     m_varToType.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_modulesListOutdated = true; | 
					
						
							|  |  |  |     m_oldestAcceptableToken = -1; | 
					
						
							|  |  |  |     m_outputCodec = QTextCodec::codecForLocale(); | 
					
						
							|  |  |  |     m_pendingRequests = 0; | 
					
						
							|  |  |  |     m_waitingForBreakpointSynchronizationToContinue = false; | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |     m_waitingForFirstBreakpointToBeHit = false; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     m_commandsToRunOnTemporaryBreak.clear(); | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | void GdbEngine::gdbProcError(QProcess::ProcessError error) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QString msg; | 
					
						
							|  |  |  |     switch (error) { | 
					
						
							|  |  |  |         case QProcess::FailedToStart: | 
					
						
							|  |  |  |             msg = QString(tr("The Gdb process failed to start. Either the " | 
					
						
							|  |  |  |                 "invoked program '%1' is missing, or you may have insufficient " | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |                 "permissions to invoke the program.")).arg(q->settings()->m_gdbCmd); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case QProcess::Crashed: | 
					
						
							|  |  |  |             msg = tr("The Gdb process crashed some time after starting " | 
					
						
							|  |  |  |                 "successfully."); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case QProcess::Timedout: | 
					
						
							|  |  |  |             msg = tr("The last waitFor...() function timed out. " | 
					
						
							|  |  |  |                 "The state of QProcess is unchanged, and you can try calling " | 
					
						
							|  |  |  |                 "waitFor...() again."); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case QProcess::WriteError: | 
					
						
							|  |  |  |             msg = tr("An error occurred when attempting to write " | 
					
						
							|  |  |  |                 "to the Gdb process. For example, the process may not be running, " | 
					
						
							|  |  |  |                 "or it may have closed its input channel."); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case QProcess::ReadError: | 
					
						
							|  |  |  |             msg = tr("An error occurred when attempting to read from " | 
					
						
							|  |  |  |                 "the Gdb process. For example, the process may not be running."); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             msg = tr("An unknown error in the Gdb process occurred. " | 
					
						
							|  |  |  |                 "This is the default return value of error()."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(msg); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     QMessageBox::critical(q->mainWindow(), tr("Error"), msg); | 
					
						
							|  |  |  |     // act as if it was closed by the core
 | 
					
						
							|  |  |  |     q->exitDebugger(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void skipSpaces(const char *&from, const char *to) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     while (from != to && QChar(*from).isSpace()) | 
					
						
							|  |  |  |         ++from; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline bool isNameChar(char c) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // could be 'stopped' or 'shlibs-added'
 | 
					
						
							|  |  |  |     return (c >= 'a' && c <= 'z') || c == '-'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | static void dump(const char *first, const char *middle, const QString & to) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QByteArray ba(first, middle - first); | 
					
						
							|  |  |  |     Q_UNUSED(to); | 
					
						
							|  |  |  |     // note that qDebug cuts off output after a certain size... (bug?)
 | 
					
						
							|  |  |  |     qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n", | 
					
						
							|  |  |  |         qPrintable(currentTime()), | 
					
						
							|  |  |  |         qPrintable(QString(ba).trimmed()), | 
					
						
							|  |  |  |         qPrintable(to.trimmed())); | 
					
						
							|  |  |  |     //qDebug() << "";
 | 
					
						
							|  |  |  |     //qDebug() << qPrintable(currentTime())
 | 
					
						
							|  |  |  |     //    << " Reading response:  " << QString(ba).trimmed() << "\n";
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void skipTerminator(const char *&from, const char *to) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     skipSpaces(from, to); | 
					
						
							|  |  |  |     // skip '(gdb)'
 | 
					
						
							|  |  |  |     if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')') | 
					
						
							|  |  |  |         from += 5; | 
					
						
							|  |  |  |     skipSpaces(from, to); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 14:47:14 +01:00
										 |  |  | void GdbEngine::readDebugeeOutput(const QByteArray &data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     emit applicationOutputAvailable(m_outputCodec->toUnicode( | 
					
						
							|  |  |  |             data.constData(), data.length(), &m_outputCodecState)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  | void GdbEngine::debugMessage(const QString &msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     emit gdbOutputAvailable("debug:", msg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | // called asyncronously as response to Gdb stdout output in
 | 
					
						
							|  |  |  | // gdbResponseAvailable()
 | 
					
						
							|  |  |  | void GdbEngine::handleResponse() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static QTime lastTime; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     emit gdbOutputAvailable("            ", currentTime()); | 
					
						
							|  |  |  |     emit gdbOutputAvailable("stdout:", m_inbuffer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  |     qDebug() // << "#### start response handling #### "
 | 
					
						
							|  |  |  |         << currentTime() | 
					
						
							|  |  |  |         << lastTime.msecsTo(QTime::currentTime()) << "ms," | 
					
						
							|  |  |  |         << "buf: " << m_inbuffer.left(1500) << "..." | 
					
						
							|  |  |  |         //<< "buf: " << m_inbuffer
 | 
					
						
							|  |  |  |         << "size:" << m_inbuffer.size(); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     //qDebug() << "buf: " << m_inbuffer;
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     lastTime = QTime::currentTime(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (1) { | 
					
						
							|  |  |  |         if (m_inbuffer.isEmpty()) | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const char *from = m_inbuffer.constData(); | 
					
						
							|  |  |  |         // FIXME: check for line ending in '\n(gdb)\n'
 | 
					
						
							|  |  |  |         const char *to = from + m_inbuffer.size(); | 
					
						
							|  |  |  |         const char *inner; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //const char *oldfrom = from;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //skipSpaces(from, to);
 | 
					
						
							|  |  |  |         skipTerminator(from, to); | 
					
						
							|  |  |  |         int token = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // token is a sequence of numbers
 | 
					
						
							|  |  |  |         for (inner = from; inner != to; ++inner) | 
					
						
							|  |  |  |             if (*inner < '0' || *inner > '9') | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |         if (from != inner) { | 
					
						
							|  |  |  |             token = QString(QByteArray(from, inner - from)).toInt(); | 
					
						
							|  |  |  |             from = inner; | 
					
						
							|  |  |  |             //qDebug() << "found token " << token;
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (from == to) { | 
					
						
							|  |  |  |             //qDebug() << "Returning: " << toString();
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // next char decides kind of record
 | 
					
						
							|  |  |  |         const char c = *from++; | 
					
						
							|  |  |  |         //qDebug() << "CODE:" << c;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch (c) { | 
					
						
							|  |  |  |             case '*': | 
					
						
							|  |  |  |             case '+': | 
					
						
							|  |  |  |             case '=': { | 
					
						
							|  |  |  |                 QByteArray asyncClass; | 
					
						
							|  |  |  |                 for (; from != to; ++from) { | 
					
						
							|  |  |  |                     const char c = *from; | 
					
						
							|  |  |  |                     if (!isNameChar(c)) | 
					
						
							| 
									
										
										
										
											2008-12-08 18:26:09 +01:00
										 |  |  |                         break; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                     asyncClass += *from; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 //qDebug() << "ASYNCCLASS" << asyncClass;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 GdbMi record; | 
					
						
							|  |  |  |                 while (from != to && *from == ',') { | 
					
						
							|  |  |  |                     ++from; // skip ','
 | 
					
						
							|  |  |  |                     GdbMi data; | 
					
						
							|  |  |  |                     data.parseResultOrValue(from, to); | 
					
						
							|  |  |  |                     if (data.isValid()) { | 
					
						
							|  |  |  |                         //qDebug() << "parsed response: " << data.toString();
 | 
					
						
							|  |  |  |                         record.m_children += data; | 
					
						
							|  |  |  |                         record.m_type = GdbMi::Tuple; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 //dump(oldfrom, from, record.toString());
 | 
					
						
							|  |  |  |                 skipTerminator(from, to); | 
					
						
							|  |  |  |                 m_inbuffer = QByteArray(from, to - from); | 
					
						
							|  |  |  |                 if (asyncClass == "stopped") { | 
					
						
							|  |  |  |                     handleAsyncOutput(record); | 
					
						
							| 
									
										
										
										
											2008-12-03 14:54:19 +01:00
										 |  |  |                 } else if (asyncClass == "running") { | 
					
						
							|  |  |  |                     // Archer has 'thread-id="all"' here
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 } else { | 
					
						
							| 
									
										
										
										
											2008-12-03 14:54:19 +01:00
										 |  |  |                     qDebug() << "IGNORED ASYNC OUTPUT " << record.toString(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |             case '~': { | 
					
						
							|  |  |  |                 QString data = GdbMi::parseCString(from, to); | 
					
						
							|  |  |  |                 m_pendingConsoleStreamOutput += data; | 
					
						
							|  |  |  |                 m_inbuffer = QByteArray(from, to - from); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case '@': { | 
					
						
							|  |  |  |                 QString data = GdbMi::parseCString(from, to); | 
					
						
							|  |  |  |                 m_pendingTargetStreamOutput += data; | 
					
						
							|  |  |  |                 m_inbuffer = QByteArray(from, to - from); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             case '&': { | 
					
						
							|  |  |  |                 QString data = GdbMi::parseCString(from, to); | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |                 m_pendingLogStreamOutput += data; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 m_inbuffer = QByteArray(from, to - from); | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |                 // On Windows, the contents seem to depend on the debugger
 | 
					
						
							|  |  |  |                 // version and/or OS version used.
 | 
					
						
							|  |  |  |                 if (data.startsWith("warning:")) | 
					
						
							|  |  |  |                     qq->showApplicationOutput(data); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             case '^': { | 
					
						
							|  |  |  |                 GdbResultRecord record; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 record.token = token; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for (inner = from; inner != to; ++inner) | 
					
						
							|  |  |  |                     if (*inner < 'a' || *inner > 'z') | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 QByteArray resultClass(from, inner - from); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (resultClass == "done") | 
					
						
							|  |  |  |                     record.resultClass = GdbResultDone; | 
					
						
							|  |  |  |                 else if (resultClass == "running") | 
					
						
							|  |  |  |                     record.resultClass = GdbResultRunning; | 
					
						
							|  |  |  |                 else if (resultClass == "connected") | 
					
						
							|  |  |  |                     record.resultClass = GdbResultConnected; | 
					
						
							|  |  |  |                 else if (resultClass == "error") | 
					
						
							|  |  |  |                     record.resultClass = GdbResultError; | 
					
						
							|  |  |  |                 else if (resultClass == "exit") | 
					
						
							|  |  |  |                     record.resultClass = GdbResultExit; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     record.resultClass = GdbResultUnknown; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 from = inner; | 
					
						
							|  |  |  |                 skipSpaces(from, to); | 
					
						
							|  |  |  |                 if (from != to && *from == ',') { | 
					
						
							|  |  |  |                     ++from; | 
					
						
							|  |  |  |                     record.data.parseTuple_helper(from, to); | 
					
						
							|  |  |  |                     record.data.m_type = GdbMi::Tuple; | 
					
						
							|  |  |  |                     record.data.m_name = "data"; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 skipSpaces(from, to); | 
					
						
							|  |  |  |                 skipTerminator(from, to); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
 | 
					
						
							|  |  |  |                 //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
 | 
					
						
							|  |  |  |                 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
 | 
					
						
							|  |  |  |                 record.data.setStreamOutput("logstreamoutput", | 
					
						
							|  |  |  |                     m_pendingLogStreamOutput); | 
					
						
							|  |  |  |                 record.data.setStreamOutput("targetstreamoutput", | 
					
						
							|  |  |  |                     m_pendingTargetStreamOutput); | 
					
						
							|  |  |  |                 record.data.setStreamOutput("consolestreamoutput", | 
					
						
							|  |  |  |                     m_pendingConsoleStreamOutput); | 
					
						
							|  |  |  |                 QByteArray custom = m_customOutputForToken[token]; | 
					
						
							|  |  |  |                 if (!custom.isEmpty()) | 
					
						
							|  |  |  |                     record.data.setStreamOutput("customvaluecontents", | 
					
						
							|  |  |  |                         '{' + custom + '}'); | 
					
						
							|  |  |  |                 //m_customOutputForToken.remove(token);
 | 
					
						
							|  |  |  |                 m_pendingLogStreamOutput.clear(); | 
					
						
							|  |  |  |                 m_pendingTargetStreamOutput.clear(); | 
					
						
							|  |  |  |                 m_pendingConsoleStreamOutput.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //dump(oldfrom, from, record.toString());
 | 
					
						
							|  |  |  |                 m_inbuffer = QByteArray(from, to - from); | 
					
						
							|  |  |  |                 handleResultRecord(record); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             default: { | 
					
						
							|  |  |  |                 qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer; | 
					
						
							|  |  |  |                 m_inbuffer = QByteArray(from, to - from); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //qDebug() << "##### end response handling ####\n\n\n"
 | 
					
						
							|  |  |  |     //    << currentTime() << lastTime.msecsTo(QTime::currentTime());
 | 
					
						
							|  |  |  |     lastTime = QTime::currentTime(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  | static void fixMac(QByteArray &out) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice.
 | 
					
						
							|  |  |  |     // it returns:   9^done,locals={{name="a"},{name="w"}}
 | 
					
						
							|  |  |  |     // instead of:   9^done,locals=[{name="a"},{name="w"}]
 | 
					
						
							|  |  |  |     if (!out.contains("locals={{name")) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static const QByteArray termArray("(gdb) "); | 
					
						
							|  |  |  |     int pos = out.indexOf(termArray); | 
					
						
							|  |  |  |     if (pos == -1) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int pos1 = out.indexOf("={{"); | 
					
						
							|  |  |  |     if (pos1 == -1) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int pos2 = out.indexOf("]]"); | 
					
						
							|  |  |  |     if (pos2 == -1) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pos1 < pos && pos2 < pos) { | 
					
						
							|  |  |  |         out[pos1 + 1] = '['; | 
					
						
							|  |  |  |         out[pos2 + 1] = ']'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::readGdbStandardError() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-15 14:47:14 +01:00
										 |  |  |     qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::readGdbStandardOutput() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // This is the function called whenever the Gdb process created
 | 
					
						
							|  |  |  |     // output. As a rule of thumb, stdout contains _real_ Gdb output
 | 
					
						
							| 
									
										
										
										
											2008-12-03 16:10:15 +01:00
										 |  |  |     // as responses to our command
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     // and "spontaneous" events like messages on loaded shared libraries.
 | 
					
						
							| 
									
										
										
										
											2008-12-03 16:10:15 +01:00
										 |  |  |     // OTOH, stderr contains application output produced by qDebug etc.
 | 
					
						
							|  |  |  |     // There is no organized way to pass application stdout output.
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     QByteArray out = m_gdbProc.readAllStandardOutput(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //qDebug() << "\n\n\nPLUGIN OUT: '" <<  out.data() << "'\n\n\n";
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |     fixMac(out); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_inbuffer.append(out); | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     //QTC_ASSERT(!m_inbuffer.isEmpty(), return);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     char c = m_inbuffer[m_inbuffer.size() - 1]; | 
					
						
							|  |  |  |     static const QByteArray termArray("(gdb) "); | 
					
						
							|  |  |  |     if (out.indexOf(termArray) == -1 && c != 10 && c != 13) { | 
					
						
							|  |  |  |         //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive";
 | 
					
						
							|  |  |  |         //qDebug() << m_inbuffer.data() << m_inbuffer.size();
 | 
					
						
							|  |  |  |         //qDebug() << "\n\n";
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     emit gdbResponseAvailable(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::interruptInferior() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     qq->notifyInferiorStopRequested(); | 
					
						
							|  |  |  |     if (m_gdbProc.state() == QProcess::NotRunning) { | 
					
						
							|  |  |  |         debugMessage("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"); | 
					
						
							|  |  |  |         qq->notifyInferiorExited(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-05 14:56:59 +01:00
										 |  |  |     if (q->m_attachedPID > 0) { | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |         if (!interruptProcess(q->m_attachedPID)) | 
					
						
							|  |  |  |         //    qq->notifyInferiorStopped();
 | 
					
						
							|  |  |  |         //else
 | 
					
						
							|  |  |  |             debugMessage(QString("CANNOT INTERRUPT %1").arg(q->m_attachedPID)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |     sendCommand("-exec-interrupt", GdbExecInterrupt); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     //qq->notifyInferiorStopped();
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     if (!interruptChildProcess(m_gdbProc.pid())) | 
					
						
							|  |  |  |     //    qq->notifyInferiorStopped();
 | 
					
						
							|  |  |  |     //else
 | 
					
						
							|  |  |  |         debugMessage(QString("CANNOT STOP INFERIOR")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int pid = pid0.toInt(); | 
					
						
							|  |  |  |     if (pid == 0) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage(QString("Cannot parse PID from %1").arg(pid0)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-01-05 14:56:59 +01:00
										 |  |  |     if (pid == q->m_attachedPID) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     debugMessage(QString("FOUND PID %1").arg(pid)); | 
					
						
							| 
									
										
										
										
											2009-01-05 14:56:59 +01:00
										 |  |  |     q->m_attachedPID = pid; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     qq->notifyInferiorPidChanged(pid);  | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::sendSynchronizedCommand(const QString & command, | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     int type, const QVariant &cookie, StopNeeded needStop) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     sendCommand(command, type, cookie, needStop, Synchronized); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::sendCommand(const QString &command, int type, | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     const QVariant &cookie, StopNeeded needStop, Synchronization synchronized) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     if (m_gdbProc.state() == QProcess::NotRunning) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("NO GDB PROCESS RUNNING, CMD IGNORED: " + command); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (synchronized) { | 
					
						
							|  |  |  |         ++m_pendingRequests; | 
					
						
							|  |  |  |         PENDING_DEBUG("   TYPE " << type << " INCREMENTS PENDING TO: " | 
					
						
							|  |  |  |             << m_pendingRequests << command); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         PENDING_DEBUG("   UNKNOWN TYPE " << type << " LEAVES PENDING AT: " | 
					
						
							|  |  |  |             << m_pendingRequests << command); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GdbCookie cmd; | 
					
						
							|  |  |  |     cmd.synchronized = synchronized; | 
					
						
							|  |  |  |     cmd.command = command; | 
					
						
							|  |  |  |     cmd.type = type; | 
					
						
							|  |  |  |     cmd.cookie = cookie; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     if (needStop && q->status() != DebuggerInferiorStopped | 
					
						
							|  |  |  |             && q->status() != DebuggerProcessStartingUp) { | 
					
						
							|  |  |  |         // queue the commands that we cannot send at once
 | 
					
						
							|  |  |  |         QTC_ASSERT(q->status() == DebuggerInferiorRunning, | 
					
						
							|  |  |  |             qDebug() << "STATUS: " << q->status()); | 
					
						
							|  |  |  |         q->showStatusMessage(tr("Stopping temporarily.")); | 
					
						
							|  |  |  |         debugMessage("QUEUING COMMAND " + cmd.command); | 
					
						
							|  |  |  |         m_commandsToRunOnTemporaryBreak.append(cmd); | 
					
						
							|  |  |  |         interruptInferior(); | 
					
						
							|  |  |  |     } else if (!command.isEmpty()) { | 
					
						
							|  |  |  |         ++currentToken(); | 
					
						
							|  |  |  |         m_cookieForToken[currentToken()] = cmd; | 
					
						
							|  |  |  |         cmd.command = QString::number(currentToken()) + cmd.command; | 
					
						
							|  |  |  |         if (cmd.command.contains("%1")) | 
					
						
							|  |  |  |             cmd.command = cmd.command.arg(currentToken()); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         m_gdbProc.write(cmd.command.toLatin1() + "\r\n"); | 
					
						
							|  |  |  |         //emit gdbInputAvailable(QString(), "         " +  currentTime());
 | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         //emit gdbInputAvailable(QString(), "[" + currentTime() + "]    " + cmd.command);
 | 
					
						
							|  |  |  |         emit gdbInputAvailable(QString(), cmd.command); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleResultRecord(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //qDebug() << "TOKEN: " << record.token
 | 
					
						
							|  |  |  |     //    << " ACCEPTABLE: " << m_oldestAcceptableToken;
 | 
					
						
							|  |  |  |     //qDebug() << "";
 | 
					
						
							|  |  |  |     //qDebug() << "\nRESULT" << record.token << record.toString();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int token = record.token; | 
					
						
							|  |  |  |     if (token == -1) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GdbCookie cmd = m_cookieForToken.take(token); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // FIXME: this falsely rejects results from the custom dumper recognition
 | 
					
						
							|  |  |  |     // procedure, too...
 | 
					
						
							|  |  |  |     if (record.token < m_oldestAcceptableToken) { | 
					
						
							|  |  |  |         //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
 | 
					
						
							|  |  |  |         //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  |     qDebug() << "# handleOutput, " | 
					
						
							|  |  |  |         << "cmd type: " << cmd.type | 
					
						
							|  |  |  |         << "cmd synchronized: " << cmd.synchronized | 
					
						
							|  |  |  |         << "\n record: " << record.toString(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // << "\n data: " << record.data.toString(true);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cmd.type != GdbInvalidCommand) | 
					
						
							|  |  |  |         handleResult(record, cmd.type, cmd.cookie); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cmd.synchronized) { | 
					
						
							|  |  |  |         --m_pendingRequests; | 
					
						
							|  |  |  |         PENDING_DEBUG("   TYPE " << cmd.type << " DECREMENTS PENDING TO: " | 
					
						
							|  |  |  |             << m_pendingRequests << cmd.command); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         if (m_pendingRequests <= 0) { | 
					
						
							|  |  |  |             PENDING_DEBUG(" ....  AND TRIGGERS MODEL UPDATE"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             updateWatchModel2(); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         PENDING_DEBUG("   UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: " | 
					
						
							|  |  |  |             << m_pendingRequests << cmd.command); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleResult(const GdbResultRecord & record, int type, | 
					
						
							|  |  |  |     const QVariant & cookie) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (type) { | 
					
						
							|  |  |  |         case GdbExecNext: | 
					
						
							|  |  |  |         case GdbExecStep: | 
					
						
							|  |  |  |         case GdbExecNextI: | 
					
						
							|  |  |  |         case GdbExecStepI: | 
					
						
							|  |  |  |         case GdbExecContinue: | 
					
						
							|  |  |  |         case GdbExecFinish: | 
					
						
							|  |  |  |             // evil code sharing
 | 
					
						
							|  |  |  |             handleExecRun(record); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |         case GdbStart: | 
					
						
							|  |  |  |             handleStart(record); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |             break; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         case GdbInfoProc: | 
					
						
							|  |  |  |             handleInfoProc(record); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2009-02-13 17:11:34 +01:00
										 |  |  |         case GdbInfoThreads: | 
					
						
							|  |  |  |             handleInfoThreads(record); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         case GdbShowVersion: | 
					
						
							|  |  |  |             handleShowVersion(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbFileExecAndSymbols: | 
					
						
							|  |  |  |             handleFileExecAndSymbols(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbExecRunToFunction: | 
					
						
							|  |  |  |             // that should be "^running". We need to handle the resulting
 | 
					
						
							|  |  |  |             // "Stopped"
 | 
					
						
							|  |  |  |             //handleExecRunToFunction(record);
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbExecInterrupt: | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |             qq->notifyInferiorStopped(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case GdbExecJumpToLine: | 
					
						
							|  |  |  |             handleExecJumpToLine(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbQueryPwd: | 
					
						
							|  |  |  |             handleQueryPwd(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbQuerySources: | 
					
						
							|  |  |  |             handleQuerySources(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbAsyncOutput2: | 
					
						
							|  |  |  |             handleAsyncOutput2(cookie.value<GdbMi>()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbInfoShared: | 
					
						
							|  |  |  |             handleInfoShared(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbQueryDataDumper1: | 
					
						
							|  |  |  |             handleQueryDataDumper1(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case GdbQueryDataDumper2: | 
					
						
							|  |  |  |             handleQueryDataDumper2(record); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |         case GdbTemporaryContinue: | 
					
						
							|  |  |  |             continueInferior(); | 
					
						
							|  |  |  |             q->showStatusMessage(tr("Continuing after temporary stop.")); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         case BreakList: | 
					
						
							|  |  |  |             handleBreakList(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BreakInsert: | 
					
						
							|  |  |  |             handleBreakInsert(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BreakInsert1: | 
					
						
							|  |  |  |             handleBreakInsert1(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BreakInfo: | 
					
						
							|  |  |  |             handleBreakInfo(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BreakEnablePending: | 
					
						
							|  |  |  |         case BreakDelete: | 
					
						
							|  |  |  |             // nothing
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BreakIgnore: | 
					
						
							|  |  |  |             handleBreakIgnore(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BreakCondition: | 
					
						
							|  |  |  |             handleBreakCondition(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case DisassemblerList: | 
					
						
							|  |  |  |             handleDisassemblerList(record, cookie.toString()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case ModulesList: | 
					
						
							|  |  |  |             handleModulesList(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case RegisterListNames: | 
					
						
							|  |  |  |             handleRegisterListNames(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case RegisterListValues: | 
					
						
							|  |  |  |             handleRegisterListValues(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case StackListFrames: | 
					
						
							|  |  |  |             handleStackListFrames(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case StackListThreads: | 
					
						
							|  |  |  |             handleStackListThreads(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case StackSelectThread: | 
					
						
							|  |  |  |             handleStackSelectThread(record, cookie.toInt()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case StackListLocals: | 
					
						
							|  |  |  |             handleStackListLocals(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case StackListArguments: | 
					
						
							|  |  |  |             handleStackListArguments(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case WatchVarListChildren: | 
					
						
							|  |  |  |             handleVarListChildren(record, cookie.value<WatchData>()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchVarCreate: | 
					
						
							|  |  |  |             handleVarCreate(record, cookie.value<WatchData>()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchVarAssign: | 
					
						
							|  |  |  |             handleVarAssign(); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchEvaluateExpression: | 
					
						
							|  |  |  |             handleEvaluateExpression(record, cookie.value<WatchData>()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchToolTip: | 
					
						
							|  |  |  |             handleToolTip(record, cookie.toString()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchDumpCustomValue1: | 
					
						
							|  |  |  |             handleDumpCustomValue1(record, cookie.value<WatchData>()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchDumpCustomValue2: | 
					
						
							|  |  |  |             handleDumpCustomValue2(record, cookie.value<WatchData>()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case WatchDumpCustomSetup: | 
					
						
							|  |  |  |             handleDumpCustomSetup(record); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         default: | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             debugMessage(QString("FIXME: GdbEngine::handleResult: " | 
					
						
							|  |  |  |                 "should not happen %1").arg(type)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::executeDebuggerCommand(const QString &command) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //createGdbProcessIfNeeded();
 | 
					
						
							|  |  |  |     if (m_gdbProc.state() == QProcess::NotRunning) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " + command); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GdbCookie cmd; | 
					
						
							|  |  |  |     cmd.command = command; | 
					
						
							|  |  |  |     cmd.type = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     emit gdbInputAvailable(QString(), cmd.command); | 
					
						
							|  |  |  |     m_gdbProc.write(cmd.command.toLatin1() + "\r\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleQueryPwd(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: remove this special case as soon as 'pwd'
 | 
					
						
							|  |  |  |     // is supported by MI
 | 
					
						
							|  |  |  |     //qDebug() << "PWD OUTPUT:" <<  record.toString();
 | 
					
						
							|  |  |  |     // Gdb responses _unless_ we get an error first.
 | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  | #ifdef Q_OS_LINUX
 | 
					
						
							|  |  |  |         // "5^done,{logstreamoutput="pwd ",consolestreamoutput
 | 
					
						
							|  |  |  |         // ="Working directory /home/apoenitz/dev/work/test1.  "}
 | 
					
						
							|  |  |  |         m_pwd = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         int pos = m_pwd.indexOf("Working directory"); | 
					
						
							|  |  |  |         m_pwd = m_pwd.mid(pos + 18); | 
					
						
							|  |  |  |         m_pwd = m_pwd.trimmed(); | 
					
						
							|  |  |  |         if (m_pwd.endsWith('.')) | 
					
						
							|  |  |  |             m_pwd.chop(1); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |         // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
 | 
					
						
							|  |  |  |         m_pwd = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         m_pwd = m_pwd.trimmed(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("PWD RESULT: " + m_pwd); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleQuerySources(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         m_shortToFullName.clear(); | 
					
						
							|  |  |  |         m_fullToShortName.clear(); | 
					
						
							|  |  |  |         // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
 | 
					
						
							|  |  |  |         // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
 | 
					
						
							|  |  |  |         GdbMi files = record.data.findChild("files"); | 
					
						
							|  |  |  |         foreach (const GdbMi &item, files.children()) { | 
					
						
							|  |  |  |             QString fileName = item.findChild("file").data(); | 
					
						
							|  |  |  |             GdbMi fullName = item.findChild("fullname"); | 
					
						
							|  |  |  |             QString full = fullName.data(); | 
					
						
							|  |  |  |             #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |             full = QDir::cleanPath(full); | 
					
						
							|  |  |  |             #endif
 | 
					
						
							|  |  |  |             if (fullName.isValid() && QFileInfo(full).isReadable()) { | 
					
						
							|  |  |  |                 //qDebug() << "STORING 2: " << fileName << full;
 | 
					
						
							|  |  |  |                 m_shortToFullName[fileName] = full; | 
					
						
							|  |  |  |                 m_fullToShortName[full] = fileName; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 17:11:34 +01:00
										 |  |  | void GdbEngine::handleInfoThreads(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // FIXME: use something more robust
 | 
					
						
							|  |  |  |         // WIN:     * 3 Thread 2312.0x4d0  0x7c91120f in ?? () 
 | 
					
						
							|  |  |  |         // LINUX:   * 1 Thread 0x7f466273c6f0 (LWP 21455)  0x0000000000404542 in ...
 | 
					
						
							|  |  |  |         QRegExp re(QLatin1String("Thread (\\d+)\\.0x.* in")); | 
					
						
							|  |  |  |         QString data = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         if (re.indexIn(data) != -1) | 
					
						
							|  |  |  |             maybeHandleInferiorPidChanged(re.cap(1)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | void GdbEngine::handleInfoProc(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         #if defined(Q_OS_MAC)
 | 
					
						
							|  |  |  |         //^done,process-id="85075"
 | 
					
						
							|  |  |  |         maybeHandleInferiorPidChanged(record.data.findChild("process-id").data()); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
 | 
					
						
							|  |  |  |         // FIXME: use something more robust
 | 
					
						
							|  |  |  |         QRegExp re(QLatin1String("process (\\d+)")); | 
					
						
							|  |  |  |         QString data = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         if (re.indexIn(data) != -1) | 
					
						
							|  |  |  |             maybeHandleInferiorPidChanged(re.cap(1)); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleInfoShared(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // let the modules handler do the parsing
 | 
					
						
							|  |  |  |         handleModulesList(record); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: remove this special case as soon as 'jump'
 | 
					
						
							|  |  |  |     // is supported by MI
 | 
					
						
							|  |  |  |     // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
 | 
					
						
							|  |  |  |     // ~"Continuing at 0x4058f3."
 | 
					
						
							|  |  |  |     // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
 | 
					
						
							|  |  |  |     // ~"242\t x *= 2;"
 | 
					
						
							|  |  |  |     //109^done"
 | 
					
						
							|  |  |  |     qq->notifyInferiorStopped(); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Jumped. Stopped.")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     QString output = record.data.findChild("logstreamoutput").data(); | 
					
						
							|  |  |  |     if (!output.isEmpty()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     QString fileAndLine = output.section(' ', 1, 1); | 
					
						
							|  |  |  |     QString file = fileAndLine.section(':', 0, 0); | 
					
						
							|  |  |  |     int line = fileAndLine.section(':', 1, 1).toInt(); | 
					
						
							|  |  |  |     q->gotoLocation(file, line, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: remove this special case as soon as there's a real
 | 
					
						
							|  |  |  |     // reason given when the temporary breakpoint is hit.
 | 
					
						
							|  |  |  |     // reight now we get:
 | 
					
						
							|  |  |  |     // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
 | 
					
						
							|  |  |  |     // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
 | 
					
						
							|  |  |  |     // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
 | 
					
						
							|  |  |  |     qq->notifyInferiorStopped(); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Run to Function finished. Stopped.")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     GdbMi frame = record.data.findChild("frame"); | 
					
						
							|  |  |  |     QString file = frame.findChild("fullname").data(); | 
					
						
							|  |  |  |     int line = frame.findChild("line").data().toInt(); | 
					
						
							|  |  |  |     qDebug() << "HIT: " << file << line << " IN " << frame.toString() | 
					
						
							|  |  |  |         << " -- " << record.toString(); | 
					
						
							|  |  |  |     q->gotoLocation(file, line, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isExitedReason(const QString &reason) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return reason == QLatin1String("exited-normally")   // inferior exited normally
 | 
					
						
							|  |  |  |         || reason == QLatin1String("exited-signalled")  // inferior exited because of a signal
 | 
					
						
							|  |  |  |         //|| reason == QLatin1String("signal-received") // inferior received signal
 | 
					
						
							|  |  |  |         || reason == QLatin1String("exited");           // inferior exited
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isStoppedReason(const QString &reason) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return reason == QLatin1String("function-finished")  // -exec-finish
 | 
					
						
							|  |  |  |         || reason == QLatin1String("signal-received")  // handled as "isExitedReason"
 | 
					
						
							|  |  |  |         || reason == QLatin1String("breakpoint-hit")     // -exec-continue
 | 
					
						
							|  |  |  |         || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
 | 
					
						
							|  |  |  |         || reason == QLatin1String("location-reached")   // -exec-until
 | 
					
						
							|  |  |  |         || reason == QLatin1String("access-watchpoint-trigger") | 
					
						
							|  |  |  |         || reason == QLatin1String("read-watchpoint-trigger") | 
					
						
							|  |  |  | #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |         || reason.isEmpty() | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     ; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleAsyncOutput(const GdbMi &data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const QString reason = data.findChild("reason").data(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 17:11:34 +01:00
										 |  |  |     //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
 | 
					
						
							|  |  |  |     //!MAC: startSymbolName == data.findChild("frame").findChild("func")
 | 
					
						
							|  |  |  |     if (m_waitingForFirstBreakpointToBeHit) { | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |         qq->notifyInferiorStopped(); | 
					
						
							| 
									
										
										
										
											2009-02-13 17:11:34 +01:00
										 |  |  |         m_waitingForFirstBreakpointToBeHit = false; | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |         //
 | 
					
						
							|  |  |  |         // that's the "early stop"
 | 
					
						
							|  |  |  |         //  
 | 
					
						
							|  |  |  |         #if defined(Q_OS_WIN)
 | 
					
						
							| 
									
										
										
										
											2009-02-13 17:11:34 +01:00
										 |  |  |         sendCommand("info thread", GdbInfoThreads); | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |         #endif
 | 
					
						
							|  |  |  |         #if defined(Q_OS_LINUX)
 | 
					
						
							|  |  |  |         sendCommand("info proc", GdbInfoProc); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         #if defined(Q_OS_MAC)
 | 
					
						
							|  |  |  |         sendCommand("info pid", GdbInfoProc, QVariant(), true); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         sendCommand("-file-list-exec-source-files", GdbQuerySources); | 
					
						
							|  |  |  |         tryLoadCustomDumpers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // intentionally after tryLoadCustomDumpers(),
 | 
					
						
							|  |  |  |         // otherwise we'd interupt solib loading.
 | 
					
						
							|  |  |  |         if (qq->wantsAllPluginBreakpoints()) { | 
					
						
							|  |  |  |             sendCommand("set auto-solib-add on"); | 
					
						
							|  |  |  |             sendCommand("set stop-on-solib-events 0"); | 
					
						
							|  |  |  |             sendCommand("sharedlibrary .*"); | 
					
						
							|  |  |  |         } else if (qq->wantsSelectedPluginBreakpoints()) { | 
					
						
							|  |  |  |             sendCommand("set auto-solib-add on"); | 
					
						
							|  |  |  |             sendCommand("set stop-on-solib-events 1"); | 
					
						
							|  |  |  |             sendCommand("sharedlibrary "+qq->selectedPluginBreakpointsPattern()); | 
					
						
							|  |  |  |         } else if (qq->wantsNoPluginBreakpoints()) { | 
					
						
							|  |  |  |             // should be like that already
 | 
					
						
							|  |  |  |             sendCommand("set auto-solib-add off"); | 
					
						
							|  |  |  |             sendCommand("set stop-on-solib-events 0"); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |         // nicer to see a bit of the world we live in
 | 
					
						
							|  |  |  |         reloadModules(); | 
					
						
							|  |  |  |         // this will "continue" if done
 | 
					
						
							|  |  |  |         m_waitingForBreakpointSynchronizationToContinue = true; | 
					
						
							|  |  |  |         QTimer::singleShot(0, this, SLOT(attemptBreakpointSynchronization())); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     if (!m_commandsToRunOnTemporaryBreak.isEmpty()) { | 
					
						
							|  |  |  |         QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,  | 
					
						
							|  |  |  |             qDebug() << "STATUS: " << q->status()) | 
					
						
							|  |  |  |         qq->notifyInferiorStopped(); | 
					
						
							|  |  |  |         q->showStatusMessage(tr("Temporarily stopped.")); | 
					
						
							|  |  |  |         // FIXME: racy
 | 
					
						
							|  |  |  |         foreach (const GdbCookie &cmd, m_commandsToRunOnTemporaryBreak) { | 
					
						
							|  |  |  |             debugMessage(QString("RUNNING QUEUED COMMAND %1 %2") | 
					
						
							|  |  |  |                 .arg(cmd.command).arg(cmd.type)); | 
					
						
							|  |  |  |             sendCommand(cmd.command, cmd.type, cmd.cookie); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         sendCommand("p temporaryStop", GdbTemporaryContinue); | 
					
						
							|  |  |  |         m_commandsToRunOnTemporaryBreak.clear(); | 
					
						
							|  |  |  |         q->showStatusMessage(tr("Handling queued commands.")); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |     QString msg = data.findChild("consolestreamoutput").data(); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:39:20 +01:00
										 |  |  |     if (msg.contains("Stopped due to shared library event") || reason.isEmpty()) { | 
					
						
							|  |  |  |         if (qq->wantsSelectedPluginBreakpoints()) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             debugMessage("SHARED LIBRARY EVENT: " + data.toString()); | 
					
						
							|  |  |  |             debugMessage("PATTERN: " + qq->selectedPluginBreakpointsPattern()); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:39:20 +01:00
										 |  |  |             sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern()); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             continueInferior(); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:39:20 +01:00
										 |  |  |             q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString()))); | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  |         m_modulesListOutdated = true; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:39:20 +01:00
										 |  |  |         // fall through
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |     // seen on XP after removing a breakpoint while running
 | 
					
						
							|  |  |  |     //  stdout:945*stopped,reason="signal-received",signal-name="SIGTRAP",
 | 
					
						
							|  |  |  |     //  signal-meaning="Trace/breakpoint trap",thread-id="2",
 | 
					
						
							|  |  |  |     //  frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
 | 
					
						
							|  |  |  |     //  args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
 | 
					
						
							|  |  |  |     if (reason == "signal-received" | 
					
						
							|  |  |  |           && data.findChild("signal-name").toString() == "SIGTRAP") { | 
					
						
							|  |  |  |         continueInferior(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (isExitedReason(reason)) { | 
					
						
							|  |  |  |         qq->notifyInferiorExited(); | 
					
						
							|  |  |  |         QString msg = "Program exited normally"; | 
					
						
							|  |  |  |         if (reason == "exited") { | 
					
						
							|  |  |  |             msg = "Program exited with exit code " | 
					
						
							|  |  |  |                 + data.findChild("exit-code").toString(); | 
					
						
							|  |  |  |         } else if (reason == "exited-signalled") { | 
					
						
							|  |  |  |             msg = "Program exited after receiving signal " | 
					
						
							|  |  |  |                 + data.findChild("signal-name").toString(); | 
					
						
							|  |  |  |         } else if (reason == "signal-received") { | 
					
						
							|  |  |  |             msg = "Program exited after receiving signal " | 
					
						
							|  |  |  |                 + data.findChild("signal-name").toString(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |         q->showStatusMessage(msg); | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         // FIXME: shouldn't this use a statis change?
 | 
					
						
							|  |  |  |         debugMessage("CALLING PARENT EXITDEBUGGER"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         q->exitDebugger(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |     //tryLoadCustomDumpers();
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // jump over well-known frames
 | 
					
						
							|  |  |  |     static int stepCounter = 0; | 
					
						
							|  |  |  |     if (qq->skipKnownFrames()) { | 
					
						
							|  |  |  |         if (reason == "end-stepping-range" || reason == "function-finished") { | 
					
						
							|  |  |  |             GdbMi frame = data.findChild("frame"); | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             //debugMessage(frame.toString());
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             m_currentFrame = frame.findChild("addr").data() + '%' + | 
					
						
							|  |  |  |                  frame.findChild("func").data() + '%'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             QString funcName = frame.findChild("func").data(); | 
					
						
							|  |  |  |             QString fileName = frame.findChild("file").data(); | 
					
						
							|  |  |  |             if (isLeavableFunction(funcName, fileName)) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |                 //debugMessage("LEAVING" + funcName);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 ++stepCounter; | 
					
						
							|  |  |  |                 q->stepOutExec(); | 
					
						
							|  |  |  |                 //stepExec();
 | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (isSkippableFunction(funcName, fileName)) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |                 //debugMessage("SKIPPING" + funcName);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 ++stepCounter; | 
					
						
							|  |  |  |                 q->stepExec(); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             //if (stepCounter)
 | 
					
						
							|  |  |  |             //    qDebug() << "STEPCOUNTER:" << stepCounter;
 | 
					
						
							|  |  |  |             stepCounter = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (isStoppedReason(reason) || reason.isEmpty()) { | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  |         if (m_modulesListOutdated) { | 
					
						
							| 
									
										
										
										
											2009-02-17 17:39:20 +01:00
										 |  |  |             reloadModules(); | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  |             m_modulesListOutdated = false; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:39:20 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         // Need another round trip
 | 
					
						
							|  |  |  |         if (reason == "breakpoint-hit") { | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |             q->showStatusMessage(tr("Stopped at breakpoint")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             GdbMi frame = data.findChild("frame"); | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             //debugMessage("HIT BREAKPOINT: " + frame.toString());
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             m_currentFrame = frame.findChild("addr").data() + '%' + | 
					
						
							|  |  |  |                  frame.findChild("func").data() + '%'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-05 11:24:18 +01:00
										 |  |  |             QApplication::alert(q->mainWindow(), 3000); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             sendCommand("-file-list-exec-source-files", GdbQuerySources); | 
					
						
							|  |  |  |             sendCommand("-break-list", BreakList); | 
					
						
							|  |  |  |             QVariant var = QVariant::fromValue<GdbMi>(data); | 
					
						
							|  |  |  |             sendCommand("p 0", GdbAsyncOutput2, var);  // dummy
 | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2008-12-08 18:26:09 +01:00
										 |  |  |             q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             handleAsyncOutput2(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     debugMessage("STOPPED FOR UNKNOWN REASON: " + data.toString()); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     // Ignore it. Will be handled with full response later in the
 | 
					
						
							|  |  |  |     // JumpToLine or RunToFunction handlers
 | 
					
						
							|  |  |  | #if 1
 | 
					
						
							|  |  |  |     // FIXME: remove this special case as soon as there's a real
 | 
					
						
							|  |  |  |     // reason given when the temporary breakpoint is hit.
 | 
					
						
							|  |  |  |     // reight now we get:
 | 
					
						
							|  |  |  |     // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
 | 
					
						
							|  |  |  |     // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
 | 
					
						
							|  |  |  |     // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // MAC yields sometimes:
 | 
					
						
							|  |  |  |     // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
 | 
					
						
							|  |  |  |     // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
 | 
					
						
							|  |  |  |     q->resetLocation(); | 
					
						
							|  |  |  |     qq->notifyInferiorStopped(); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Run to Function finished. Stopped.")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     GdbMi frame = data.findChild("frame"); | 
					
						
							|  |  |  |     QString file = frame.findChild("fullname").data(); | 
					
						
							|  |  |  |     int line = frame.findChild("line").data().toInt(); | 
					
						
							|  |  |  |     qDebug() << "HIT: " << file << line << " IN " << frame.toString() | 
					
						
							|  |  |  |         << " -- " << data.toString(); | 
					
						
							|  |  |  |     q->gotoLocation(file, line, true); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleAsyncOutput2(const GdbMi &data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qq->notifyInferiorStopped(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Stack
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     qq->stackHandler()->setCurrentIndex(0); | 
					
						
							|  |  |  |     updateLocals(); // Quick shot
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int currentId = data.findChild("thread-id").data().toInt(); | 
					
						
							|  |  |  |     sendSynchronizedCommand("-stack-list-frames", StackListFrames); | 
					
						
							|  |  |  |     if (supportsThreads()) | 
					
						
							|  |  |  |         sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Disassembler
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Linux:
 | 
					
						
							|  |  |  |     //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
 | 
					
						
							|  |  |  |     //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
 | 
					
						
							|  |  |  |     //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
 | 
					
						
							|  |  |  |     //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
 | 
					
						
							|  |  |  |     // Mac: (but only sometimes)
 | 
					
						
							|  |  |  |     m_address = data.findChild("frame").findChild("addr").data(); | 
					
						
							|  |  |  |     qq->reloadDisassembler(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Registers
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     qq->reloadRegisters(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleShowVersion(const GdbResultRecord &response) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (response.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         m_gdbVersion = 100; | 
					
						
							|  |  |  |         QString msg = response.data.findChild("consolestreamoutput").data(); | 
					
						
							| 
									
										
										
										
											2008-12-17 15:19:22 +01:00
										 |  |  |         QRegExp supported("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?"); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |         if (supported.indexIn(msg) == -1) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             debugMessage("UNSUPPORTED GDB VERSION " + msg); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             QStringList list = msg.split("\n"); | 
					
						
							|  |  |  |             while (list.size() > 2) | 
					
						
							|  |  |  |                 list.removeLast(); | 
					
						
							|  |  |  |             msg = tr("The debugger you are using identifies itself as:") | 
					
						
							|  |  |  |                 + "<p><p>" + list.join("<br>") + "<p><p>" | 
					
						
							|  |  |  |                 + tr("This version is not officially supported by Qt Creator.\n" | 
					
						
							|  |  |  |                      "Debugging will most likely not work well.\n" | 
					
						
							|  |  |  |                      "Using gdb 6.7 or later is strongly recommended."); | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  |             // ugly, but 'Show again' check box...
 | 
					
						
							|  |  |  |             static QErrorMessage *err = new QErrorMessage(m_mainWindow); | 
					
						
							|  |  |  |             err->setMinimumSize(400, 300); | 
					
						
							|  |  |  |             err->showMessage(msg); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |             //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             m_gdbVersion = 10000 * supported.cap(2).toInt() | 
					
						
							|  |  |  |                          +   100 * supported.cap(3).toInt() | 
					
						
							| 
									
										
										
										
											2008-12-17 15:19:22 +01:00
										 |  |  |                          +     1 * supported.cap(5).toInt(); | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             //debugMessage(QString("GDB VERSION: %1").arg(m_gdbVersion));
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleFileExecAndSymbols | 
					
						
							|  |  |  |     (const GdbResultRecord &response) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (response.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         //m_breakHandler->clearBreakMarkers();
 | 
					
						
							|  |  |  |     } else if (response.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         QString msg = response.data.findChild("msg").data(); | 
					
						
							|  |  |  |         QMessageBox::critical(q->mainWindow(), tr("Error"), | 
					
						
							|  |  |  |             tr("Starting executable failed:\n") + msg); | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |         QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         interruptInferior(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleExecRun(const GdbResultRecord &response) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (response.resultClass == GdbResultRunning) { | 
					
						
							|  |  |  |         qq->notifyInferiorRunning(); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |         q->showStatusMessage(tr("Running...")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } else if (response.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         QString msg = response.data.findChild("msg").data(); | 
					
						
							|  |  |  |         if (msg == "Cannot find bounds of current function") { | 
					
						
							|  |  |  |             qq->notifyInferiorStopped(); | 
					
						
							|  |  |  |             //q->showStatusMessage(tr("No debug information available. "
 | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |             //  "Leaving function..."));
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             //stepOutExec();
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             QMessageBox::critical(q->mainWindow(), tr("Error"), | 
					
						
							|  |  |  |                 tr("Starting executable failed:\n") + msg); | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |             QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             interruptInferior(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::queryFullName(const QString &fileName, QString *full) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     *full = fullName(fileName); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString GdbEngine::shortName(const QString &fullName) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return m_fullToShortName.value(fullName, QString()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString GdbEngine::fullName(const QString &fileName) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
 | 
					
						
							|  |  |  |     if (fileName.isEmpty()) | 
					
						
							|  |  |  |         return QString(); | 
					
						
							|  |  |  |     QString full = m_shortToFullName.value(fileName, QString()); | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     //debugMessage("RESOLVING: " + fileName + " " +  full);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (!full.isEmpty()) | 
					
						
							|  |  |  |         return full; | 
					
						
							|  |  |  |     QFileInfo fi(fileName); | 
					
						
							|  |  |  |     if (!fi.isReadable()) | 
					
						
							|  |  |  |         return QString(); | 
					
						
							|  |  |  |     full = fi.absoluteFilePath(); | 
					
						
							|  |  |  |     #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |     full = QDir::cleanPath(full); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     //debugMessage("STORING: " + fileName + " " + full);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     m_shortToFullName[fileName] = full; | 
					
						
							|  |  |  |     m_fullToShortName[full] = fileName; | 
					
						
							|  |  |  |     return full; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString GdbEngine::fullName(const QStringList &candidates) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QString full; | 
					
						
							|  |  |  |     foreach (const QString &fileName, candidates) { | 
					
						
							|  |  |  |         full = fullName(fileName); | 
					
						
							|  |  |  |         if (!full.isEmpty()) | 
					
						
							|  |  |  |             return full; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     foreach (const QString &fileName, candidates) { | 
					
						
							|  |  |  |         if (!fileName.isEmpty()) | 
					
						
							|  |  |  |             return fileName; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return full; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::shutdown() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     exitDebugger(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::exitDebugger() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     debugMessage(QString("GDBENGINE EXITDEBUFFER: %1").arg(m_gdbProc.state())); | 
					
						
							|  |  |  |     if (m_gdbProc.state() == QProcess::Starting) { | 
					
						
							|  |  |  |         debugMessage(QString("WAITING FOR GDB STARTUP TO SHUTDOWN: %1") | 
					
						
							|  |  |  |             .arg(m_gdbProc.state())); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         m_gdbProc.waitForStarted(); | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (m_gdbProc.state() == QProcess::Running) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage(QString("WAITING FOR RUNNING GDB TO SHUTDOWN: %1") | 
					
						
							|  |  |  |             .arg(m_gdbProc.state())); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         interruptInferior(); | 
					
						
							|  |  |  |         sendCommand("kill"); | 
					
						
							|  |  |  |         sendCommand("-gdb-exit"); | 
					
						
							|  |  |  |         // 20s can easily happen when loading webkit debug information
 | 
					
						
							|  |  |  |         m_gdbProc.waitForFinished(20000); | 
					
						
							|  |  |  |         if (m_gdbProc.state() != QProcess::Running) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             debugMessage(QString("FORCING TERMINATION: %1") | 
					
						
							|  |  |  |                 .arg(m_gdbProc.state())); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             m_gdbProc.terminate(); | 
					
						
							|  |  |  |             m_gdbProc.waitForFinished(20000); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (m_gdbProc.state() != QProcess::NotRunning) | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("PROBLEM STOPPING DEBUGGER"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 14:47:14 +01:00
										 |  |  |     m_outputCollector.shutdown(); | 
					
						
							| 
									
										
										
										
											2009-02-13 13:50:50 +01:00
										 |  |  |     initializeVariables(); | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     //q->settings()->m_debugDumpers = false;
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int GdbEngine::currentFrame() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return qq->stackHandler()->currentIndex(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool GdbEngine::startDebugger() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QStringList gdbArgs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QFileInfo fi(q->m_executable); | 
					
						
							|  |  |  |     QString fileName = '"' + fi.absoluteFilePath() + '"'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (m_gdbProc.state() != QProcess::NotRunning) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("GDB IS ALREADY RUNNING!"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 14:47:14 +01:00
										 |  |  |     if (!m_outputCollector.listen()) { | 
					
						
							|  |  |  |         QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"), | 
					
						
							|  |  |  |                               tr("Cannot set up communication with child process: %1") | 
					
						
							|  |  |  |                               .arg(m_outputCollector.errorString())); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     //gdbArgs.prepend(QLatin1String("--quiet"));
 | 
					
						
							|  |  |  |     gdbArgs.prepend(QLatin1String("mi")); | 
					
						
							|  |  |  |     gdbArgs.prepend(QLatin1String("-i")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!q->m_workingDir.isEmpty()) | 
					
						
							|  |  |  |         m_gdbProc.setWorkingDirectory(q->m_workingDir); | 
					
						
							|  |  |  |     if (!q->m_environment.isEmpty()) | 
					
						
							|  |  |  |         m_gdbProc.setEnvironment(q->m_environment); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #if 0
 | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     qDebug() << "Command: " << q->settings()->m_gdbCmd; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory(); | 
					
						
							| 
									
										
										
										
											2009-01-19 16:46:08 +01:00
										 |  |  |     qDebug() << "ScriptFile: " << q->settings()->m_scriptFile; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     qDebug() << "Environment: " << m_gdbProc.environment(); | 
					
						
							|  |  |  |     qDebug() << "Arguments: " << gdbArgs; | 
					
						
							|  |  |  |     qDebug() << "BuildDir: " << q->m_buildDir; | 
					
						
							|  |  |  |     qDebug() << "ExeFile: " << q->m_executable; | 
					
						
							|  |  |  |     #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Starting Debugger")); | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     emit gdbInputAvailable(QString(), q->settings()->m_gdbCmd + ' ' + gdbArgs.join(" ")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     m_gdbProc.start(q->settings()->m_gdbCmd, gdbArgs); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     m_gdbProc.waitForStarted(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-15 14:38:28 +01:00
										 |  |  |     if (m_gdbProc.state() != QProcess::Running) { | 
					
						
							|  |  |  |         QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"), | 
					
						
							|  |  |  |                               tr("Cannot start debugger: %1").arg(m_gdbProc.errorString())); | 
					
						
							| 
									
										
										
										
											2009-01-15 14:47:14 +01:00
										 |  |  |         m_outputCollector.shutdown(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2009-01-15 14:38:28 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Gdb Running")); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     sendCommand("show version", GdbShowVersion); | 
					
						
							|  |  |  |     //sendCommand("-enable-timings");
 | 
					
						
							|  |  |  |     sendCommand("set print static-members off"); // Seemingly doesn't work.
 | 
					
						
							|  |  |  |     //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
 | 
					
						
							|  |  |  |     //sendCommand("define hook-stop\nprint 4\nend");
 | 
					
						
							|  |  |  |     //sendCommand("define hookpost-stop\nprint 5\nend");
 | 
					
						
							|  |  |  |     //sendCommand("define hook-call\nprint 6\nend");
 | 
					
						
							|  |  |  |     //sendCommand("define hookpost-call\nprint 7\nend");
 | 
					
						
							|  |  |  |     //sendCommand("set print object on"); // works with CLI, but not MI
 | 
					
						
							|  |  |  |     //sendCommand("set step-mode on");  // we can't work with that yes
 | 
					
						
							|  |  |  |     //sendCommand("set exec-done-display on");
 | 
					
						
							|  |  |  |     //sendCommand("set print pretty on");
 | 
					
						
							|  |  |  |     //sendCommand("set confirm off");
 | 
					
						
							|  |  |  |     //sendCommand("set pagination off");
 | 
					
						
							|  |  |  |     sendCommand("set breakpoint pending on", BreakEnablePending); | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |     sendCommand("set print elements 10000"); | 
					
						
							| 
									
										
										
										
											2009-02-16 16:00:46 +01:00
										 |  |  |     sendCommand("-data-list-register-names", RegisterListNames); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // one of the following is needed to prevent crashes in gdb on code like:
 | 
					
						
							|  |  |  |     //  template <class T> T foo() { return T(0); }
 | 
					
						
							|  |  |  |     //  int main() { return foo<int>(); }
 | 
					
						
							|  |  |  |     //  (gdb) call 'int foo<int>'()
 | 
					
						
							|  |  |  |     //  /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
 | 
					
						
							|  |  |  |     sendCommand("set overload-resolution off"); | 
					
						
							|  |  |  |     //sendCommand("set demangle-style none");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // From the docs:
 | 
					
						
							|  |  |  |     //  Stop means reenter debugger if this signal happens (implies print).
 | 
					
						
							|  |  |  |     //  Print means print a message if this signal happens.
 | 
					
						
							|  |  |  |     //  Pass means let program see this signal;
 | 
					
						
							|  |  |  |     //  otherwise program doesn't know.
 | 
					
						
							|  |  |  |     //  Pass and Stop may be combined.
 | 
					
						
							|  |  |  |     // We need "print" as otherwise we would get no feedback whatsoever
 | 
					
						
							|  |  |  |     // Custom Dumper crashs which happen regularily for when accessing
 | 
					
						
							|  |  |  |     // uninitialized variables.
 | 
					
						
							|  |  |  |     sendCommand("handle SIGSEGV nopass stop print"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // This is useful to kill the inferior whenever gdb dies.
 | 
					
						
							|  |  |  |     //sendCommand("handle SIGTERM pass nostop print");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sendCommand("set unwindonsignal on"); | 
					
						
							|  |  |  |     sendCommand("pwd", GdbQueryPwd); | 
					
						
							| 
									
										
										
										
											2009-02-16 18:38:10 +01:00
										 |  |  |     sendCommand("set width 0"); | 
					
						
							|  |  |  |     sendCommand("set height 0"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |     sendCommand("-gdb-set inferior-auto-start-cfm off"); | 
					
						
							|  |  |  |     sendCommand("-gdb-set sharedLibrary load-rules " | 
					
						
							| 
									
										
										
										
											2008-12-03 11:27:20 +01:00
										 |  |  |             "dyld \".*libSystem.*\" all " | 
					
						
							|  |  |  |             "dyld \".*libauto.*\" all " | 
					
						
							|  |  |  |             "dyld \".*AppKit.*\" all " | 
					
						
							|  |  |  |             "dyld \".*PBGDBIntrospectionSupport.*\" all " | 
					
						
							|  |  |  |             "dyld \".*Foundation.*\" all " | 
					
						
							|  |  |  |             "dyld \".*CFDataFormatters.*\" all " | 
					
						
							|  |  |  |             "dyld \".*libobjc.*\" all " | 
					
						
							|  |  |  |             "dyld \".*CarbonDataFormatters.*\" all"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     #endif
 | 
					
						
							| 
									
										
										
										
											2009-01-19 16:46:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     QString scriptFileName = q->settings()->m_scriptFile; | 
					
						
							|  |  |  |     if (!scriptFileName.isEmpty()) { | 
					
						
							|  |  |  |         QFile scriptFile(scriptFileName); | 
					
						
							|  |  |  |         if (scriptFile.open(QIODevice::ReadOnly)) { | 
					
						
							|  |  |  |             sendCommand("source " + scriptFileName); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             QMessageBox::warning(q->mainWindow(), | 
					
						
							|  |  |  |             tr("Cannot find debugger initialization script"), | 
					
						
							|  |  |  |             tr("The debugger settings point to a script file at '%1' " | 
					
						
							|  |  |  |                "which is not accessible. If a script file is not needed, " | 
					
						
							|  |  |  |                "consider clearing that entry to avoid this warning. " | 
					
						
							|  |  |  |               ).arg(scriptFileName)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-16 13:58:43 +01:00
										 |  |  |     if (q->startMode() == DebuggerManager::AttachExternal) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:41:57 +01:00
										 |  |  |         sendCommand("attach " + QString::number(q->m_attachedPID)); | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:58:43 +01:00
										 |  |  |         // StartInternal or StartExternal
 | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |         emit gdbInputAvailable(QString(), QString()); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols); | 
					
						
							|  |  |  |         //sendCommand("file " + fileName, GdbFileExecAndSymbols);
 | 
					
						
							|  |  |  |         #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |         sendCommand("sharedlibrary apply-load-rules all"); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |         setTokenBarrier(); | 
					
						
							|  |  |  |         if (!q->m_processArgs.isEmpty()) | 
					
						
							|  |  |  |             sendCommand("-exec-arguments " + q->m_processArgs.join(" ")); | 
					
						
							|  |  |  |         sendCommand("set auto-solib-add off"); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("x/2i " + startSymbolName(), GdbStart); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     // set all to "pending"
 | 
					
						
							| 
									
										
										
										
											2009-02-16 13:58:43 +01:00
										 |  |  |     if (q->startMode() == DebuggerManager::AttachExternal) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         qq->breakHandler()->removeAllBreakpoints(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         qq->breakHandler()->setAllPending(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::continueInferior() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     q->resetLocation(); | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     emit gdbInputAvailable(QString(), QString()); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     sendCommand("-exec-continue", GdbExecContinue); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  | void GdbEngine::handleStart(const GdbResultRecord &response) | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     if (response.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // stdout:&"x/2i _start\n"
 | 
					
						
							|  |  |  |         // stdout:~"0x404540 <_start>:\txor    %ebp,%ebp\n"
 | 
					
						
							|  |  |  |         // stdout:~"0x404542 <_start+2>:\tmov    %rdx,%r9\n"
 | 
					
						
							|  |  |  |         QString msg = response.data.findChild("consolestreamoutput").data(); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         QRegExp needle("0x([0-9a-f]+) <" + startSymbolName() + "\\+.*>:"); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         if (needle.indexIn(msg) != -1) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             //debugMessage("STREAM: " + msg + " " + needle.cap(1));
 | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |             sendCommand("tbreak *0x" + needle.cap(1)); | 
					
						
							| 
									
										
										
										
											2009-02-13 16:55:24 +01:00
										 |  |  |             m_waitingForFirstBreakpointToBeHit = true; | 
					
						
							| 
									
										
										
										
											2009-02-12 13:31:19 +01:00
										 |  |  |             qq->notifyInferiorRunningRequested(); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |             sendCommand("-exec-run"); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |             debugMessage("PARSING START ADDRESS FAILED: " + msg); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } else if (response.resultClass == GdbResultError) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("PARSING START ADDRESS FAILED: " + response.toString()); | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | void GdbEngine::stepExec() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     emit gdbInputAvailable(QString(), QString()); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     sendCommand("-exec-step", GdbExecStep); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::stepIExec() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							|  |  |  |     sendCommand("-exec-step-instruction", GdbExecStepI); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::stepOutExec() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							|  |  |  |     sendCommand("-exec-finish", GdbExecFinish); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::nextExec() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     emit gdbInputAvailable(QString(), QString()); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     sendCommand("-exec-next", GdbExecNext); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::nextIExec() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							|  |  |  |     sendCommand("-exec-next-instruction", GdbExecNextI); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::runToLineExec(const QString &fileName, int lineNumber) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							|  |  |  |     sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::runToFunctionExec(const QString &functionName) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setTokenBarrier(); | 
					
						
							|  |  |  |     sendCommand("-break-insert -t " + functionName); | 
					
						
							|  |  |  |     qq->notifyInferiorRunningRequested(); | 
					
						
							|  |  |  |     sendCommand("-exec-continue", GdbExecRunToFunction); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | #if 1
 | 
					
						
							|  |  |  |     // not available everywhere?
 | 
					
						
							|  |  |  |     //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
 | 
					
						
							|  |  |  |     sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber)); | 
					
						
							|  |  |  |     sendCommand("jump " + fileName + ":" + QString::number(lineNumber)); | 
					
						
							|  |  |  |     // will produce something like
 | 
					
						
							|  |  |  |     //  &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
 | 
					
						
							|  |  |  |     //  ~"Continuing at 0x4058f3."
 | 
					
						
							|  |  |  |     //  ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
 | 
					
						
							|  |  |  |     //  ~"242\t x *= 2;"
 | 
					
						
							|  |  |  |     //  23^done"
 | 
					
						
							|  |  |  |     q->gotoLocation(fileName, lineNumber, true); | 
					
						
							|  |  |  |     //setBreakpoint();
 | 
					
						
							|  |  |  |     //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     q->gotoLocation(fileName, lineNumber, true); | 
					
						
							|  |  |  |     setBreakpoint(fileName, lineNumber); | 
					
						
							|  |  |  |     sendCommand("jump " + fileName + ":" + QString::number(lineNumber)); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |     \fn void GdbEngine::setTokenBarrier() | 
					
						
							|  |  |  |     \brief Sets up internal structures to handle a new debugger turn. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This method is called at the beginnign of all step/next/finish etc. | 
					
						
							|  |  |  |     debugger functions. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::setTokenBarrier() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_oldestAcceptableToken = currentToken(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::setDebugDumpers(bool on) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (on) { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("SWITCHING ON DUMPER DEBUGGING"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         sendCommand("set unwindonsignal off"); | 
					
						
							|  |  |  |         q->breakByFunction("qDumpObjectData440"); | 
					
						
							|  |  |  |         //updateLocals();
 | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2009-02-16 13:29:57 +01:00
										 |  |  |         debugMessage("SWITCHING OFF DUMPER DEBUGGING"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         sendCommand("set unwindonsignal on"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Breakpoint specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!bkpt.isValid()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     if (!data) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     data->pending = false; | 
					
						
							|  |  |  |     data->bpMultiple = false; | 
					
						
							|  |  |  |     data->bpCondition.clear(); | 
					
						
							|  |  |  |     QStringList files; | 
					
						
							|  |  |  |     foreach (const GdbMi &child, bkpt.children()) { | 
					
						
							|  |  |  |         if (child.hasName("number")) { | 
					
						
							|  |  |  |             data->bpNumber = child.data(); | 
					
						
							|  |  |  |         } else if (child.hasName("func")) { | 
					
						
							|  |  |  |             data->bpFuncName = child.data(); | 
					
						
							|  |  |  |         } else if (child.hasName("addr")) { | 
					
						
							|  |  |  |             // <MULTIPLE> happens in constructors. In this case there are
 | 
					
						
							|  |  |  |             // _two_ fields named "addr" in the response. On Linux that is...
 | 
					
						
							|  |  |  |             if (child.data() == "<MULTIPLE>") | 
					
						
							|  |  |  |                 data->bpMultiple = true; | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 data->bpAddress = child.data(); | 
					
						
							|  |  |  |         } else if (child.hasName("file")) { | 
					
						
							|  |  |  |             files.append(child.data()); | 
					
						
							|  |  |  |         } else if (child.hasName("fullname")) { | 
					
						
							|  |  |  |             QString fullName = child.data(); | 
					
						
							|  |  |  |             #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |             fullName = QDir::cleanPath(fullName); | 
					
						
							|  |  |  |             #endif
 | 
					
						
							|  |  |  |             files.prepend(fullName); | 
					
						
							|  |  |  |         } else if (child.hasName("line")) { | 
					
						
							|  |  |  |             data->bpLineNumber = child.data(); | 
					
						
							|  |  |  |             if (child.data().toInt()) | 
					
						
							|  |  |  |                 data->markerLineNumber = child.data().toInt(); | 
					
						
							|  |  |  |         } else if (child.hasName("cond")) { | 
					
						
							|  |  |  |             data->bpCondition = child.data(); | 
					
						
							|  |  |  |             // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
 | 
					
						
							|  |  |  |             if (data->bpCondition != data->condition && data->conditionsMatch()) | 
					
						
							|  |  |  |                 data->condition = data->bpCondition; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (child.hasName("pending")) { | 
					
						
							|  |  |  |             data->pending = true; | 
					
						
							|  |  |  |             int pos = child.data().lastIndexOf(':'); | 
					
						
							|  |  |  |             if (pos > 0) { | 
					
						
							|  |  |  |                 data->bpLineNumber = child.data().mid(pos + 1); | 
					
						
							|  |  |  |                 data->markerLineNumber = child.data().mid(pos + 1).toInt(); | 
					
						
							|  |  |  |                 files.prepend(child.data().left(pos)); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 files.prepend(child.data()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // This field is not present.  Contents needs to be parsed from
 | 
					
						
							|  |  |  |     // the plain "ignore" response.
 | 
					
						
							|  |  |  |     //else if (child.hasName("ignore"))
 | 
					
						
							|  |  |  |     //    data->bpIgnoreCount = child.data();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QString name = fullName(files); | 
					
						
							|  |  |  |     if (data->bpFileName.isEmpty()) | 
					
						
							|  |  |  |         data->bpFileName = name; | 
					
						
							|  |  |  |     if (data->markerFileName.isEmpty()) | 
					
						
							|  |  |  |         data->markerFileName = name; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::sendInsertBreakpoint(int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const BreakpointData *data = qq->breakHandler()->at(index); | 
					
						
							|  |  |  |     QString where; | 
					
						
							|  |  |  |     if (data->funcName.isEmpty()) { | 
					
						
							|  |  |  |         where = data->fileName; | 
					
						
							|  |  |  | #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |         // full names do not work on Mac/MI
 | 
					
						
							|  |  |  |         QFileInfo fi(data->fileName); | 
					
						
							|  |  |  |         where = fi.fileName(); | 
					
						
							|  |  |  |         //where = fi.absoluteFilePath();
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |         // full names do not work on Mac/MI
 | 
					
						
							|  |  |  |         QFileInfo fi(data->fileName); | 
					
						
							|  |  |  |         where = fi.fileName(); | 
					
						
							|  |  |  |     //where = m_manager->shortName(data->fileName);
 | 
					
						
							|  |  |  |         //if (where.isEmpty())
 | 
					
						
							|  |  |  |         //    where = data->fileName;
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         // we need something like   "\"file name.cpp\":100"  to
 | 
					
						
							|  |  |  |         // survive the gdb command line parser with file names intact
 | 
					
						
							|  |  |  |         where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\""; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         where = data->funcName; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // set up fallback in case of pending breakpoints which aren't handled
 | 
					
						
							|  |  |  |     // by the MI interface
 | 
					
						
							|  |  |  | #ifdef Q_OS_LINUX
 | 
					
						
							|  |  |  |     QString cmd = "-break-insert "; | 
					
						
							|  |  |  |     //if (!data->condition.isEmpty())
 | 
					
						
							|  |  |  |     //    cmd += "-c " + data->condition + " ";
 | 
					
						
							|  |  |  |     cmd += where; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |     QString cmd = "-break-insert -l -1 "; | 
					
						
							|  |  |  |     //if (!data->condition.isEmpty())
 | 
					
						
							|  |  |  |     //    cmd += "-c " + data->condition + " ";
 | 
					
						
							|  |  |  |     cmd += where; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |     QString cmd = "-break-insert "; | 
					
						
							|  |  |  |     //if (!data->condition.isEmpty())
 | 
					
						
							|  |  |  |     //    cmd += "-c " + data->condition + " ";
 | 
					
						
							|  |  |  |     cmd += where; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |     debugMessage(QString("Current state: %1").arg(q->status())); | 
					
						
							|  |  |  |     sendCommand(cmd, BreakInsert, index, NeedsStop); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakList(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
 | 
					
						
							|  |  |  |     // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
 | 
					
						
							|  |  |  |     // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
 | 
					
						
							|  |  |  |     //  addr="0x000000000040109e",func="main",file="app.cpp",
 | 
					
						
							|  |  |  |     //  fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
 | 
					
						
							|  |  |  |     //  line="11",times="1"},
 | 
					
						
							|  |  |  |     //  bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
 | 
					
						
							|  |  |  |     //  addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         GdbMi table = record.data.findChild("BreakpointTable"); | 
					
						
							|  |  |  |         if (table.isValid()) | 
					
						
							|  |  |  |             handleBreakList(table); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakList(const GdbMi &table) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //qDebug() << "GdbEngine::handleOutput: table: "
 | 
					
						
							|  |  |  |     //  << table.toString();
 | 
					
						
							|  |  |  |     GdbMi body = table.findChild("body"); | 
					
						
							|  |  |  |     //qDebug() << "GdbEngine::handleOutput: body: "
 | 
					
						
							|  |  |  |     //  << body.toString();
 | 
					
						
							|  |  |  |     QList<GdbMi> bkpts; | 
					
						
							|  |  |  |     if (body.isValid()) { | 
					
						
							|  |  |  |         // Non-Mac
 | 
					
						
							|  |  |  |         bkpts = body.children(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // Mac
 | 
					
						
							|  |  |  |         bkpts = table.children(); | 
					
						
							|  |  |  |         // remove the 'hdr' and artificial items
 | 
					
						
							|  |  |  |         //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
 | 
					
						
							|  |  |  |         for (int i = bkpts.size(); --i >= 0; ) { | 
					
						
							|  |  |  |             int num = bkpts.at(i).findChild("number").data().toInt(); | 
					
						
							|  |  |  |             if (num <= 0) { | 
					
						
							|  |  |  |                 //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
 | 
					
						
							|  |  |  |                 bkpts.removeAt(i); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  |     for (int index = 0; index != bkpts.size(); ++index) { | 
					
						
							|  |  |  |         BreakpointData temp(handler); | 
					
						
							|  |  |  |         breakpointDataFromOutput(&temp, bkpts.at(index)); | 
					
						
							|  |  |  |         int found = handler->findBreakpoint(temp); | 
					
						
							|  |  |  |         if (found != -1) | 
					
						
							|  |  |  |             breakpointDataFromOutput(handler->at(found), bkpts.at(index)); | 
					
						
							|  |  |  |         //else
 | 
					
						
							|  |  |  |             //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attemptBreakpointSynchronization(); | 
					
						
							|  |  |  |     handler->updateMarkers(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // gdb 6.8:
 | 
					
						
							|  |  |  |     // ignore 2 0:
 | 
					
						
							|  |  |  |     // ~"Will stop next time breakpoint 2 is reached.\n"
 | 
					
						
							|  |  |  |     // 28^done
 | 
					
						
							|  |  |  |     // ignore 2 12:
 | 
					
						
							|  |  |  |     // &"ignore 2 12\n"
 | 
					
						
							|  |  |  |     // ~"Will ignore next 12 crossings of breakpoint 2.\n"
 | 
					
						
							|  |  |  |     // 29^done
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // gdb 6.3 does not produce any console output
 | 
					
						
							|  |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone && index < handler->size()) { | 
					
						
							|  |  |  |         QString msg = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         //if (msg.contains("Will stop next time breakpoint")) {
 | 
					
						
							|  |  |  |         //    data->bpIgnoreCount = "0";
 | 
					
						
							|  |  |  |         //} else if (msg.contains("Will ignore next")) {
 | 
					
						
							|  |  |  |         //    data->bpIgnoreCount = data->ignoreCount;
 | 
					
						
							|  |  |  |         //}
 | 
					
						
							|  |  |  |         // FIXME: this assumes it is doing the right thing...
 | 
					
						
							|  |  |  |         data->bpIgnoreCount = data->ignoreCount; | 
					
						
							|  |  |  |         attemptBreakpointSynchronization(); | 
					
						
							|  |  |  |         handler->updateMarkers(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // we just assume it was successful. otherwise we had to parse
 | 
					
						
							|  |  |  |         // the output stream data
 | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
 | 
					
						
							|  |  |  |         data->bpCondition = data->condition; | 
					
						
							|  |  |  |         attemptBreakpointSynchronization(); | 
					
						
							|  |  |  |         handler->updateMarkers(); | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         QString msg = record.data.findChild("msg").data(); | 
					
						
							|  |  |  |         // happens on Mac
 | 
					
						
							|  |  |  |         if (1 || msg.startsWith("Error parsing breakpoint condition. " | 
					
						
							|  |  |  |                 " Will try again when we hit the breakpoint.")) { | 
					
						
							|  |  |  |             BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |             //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
 | 
					
						
							|  |  |  |             data->bpCondition = data->condition; | 
					
						
							|  |  |  |             attemptBreakpointSynchronization(); | 
					
						
							|  |  |  |             handler->updateMarkers(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         //qDebug() << "HANDLE BREAK INSERT " << index;
 | 
					
						
							|  |  |  | //#ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |         // interesting only on Mac?
 | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         GdbMi bkpt = record.data.findChild("bkpt"); | 
					
						
							|  |  |  |         //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
 | 
					
						
							|  |  |  |         breakpointDataFromOutput(data, bkpt); | 
					
						
							|  |  |  | //#endif
 | 
					
						
							|  |  |  |         attemptBreakpointSynchronization(); | 
					
						
							|  |  |  |         handler->updateMarkers(); | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         const BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  | #ifdef Q_OS_LINUX
 | 
					
						
							|  |  |  |         //QString where = "\"\\\"" + data->fileName + "\\\":"
 | 
					
						
							|  |  |  |         //    + data->lineNumber + "\"";
 | 
					
						
							|  |  |  |         QString where = "\"" + data->fileName + "\":" | 
					
						
							|  |  |  |             + data->lineNumber; | 
					
						
							|  |  |  |         sendCommand("break " + where, BreakInsert1, index); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |         QFileInfo fi(data->fileName); | 
					
						
							|  |  |  |         QString where = "\"" + fi.fileName() + "\":" | 
					
						
							|  |  |  |             + data->lineNumber; | 
					
						
							|  |  |  |         sendCommand("break " + where, BreakInsert1, index); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |         QFileInfo fi(data->fileName); | 
					
						
							|  |  |  |         QString where = "\"" + fi.fileName() + "\":" | 
					
						
							|  |  |  |             + data->lineNumber; | 
					
						
							|  |  |  |         //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
 | 
					
						
							|  |  |  |         sendCommand("break " + where, BreakInsert1, index); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     data->bpFileName = "<MULTIPLE>"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //qDebug() << output;
 | 
					
						
							|  |  |  |     if (output.isEmpty()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     // "Num     Type           Disp Enb Address            What
 | 
					
						
							|  |  |  |     // 4       breakpoint     keep y   <MULTIPLE>         0x00000000004066ad
 | 
					
						
							|  |  |  |     // 4.1                         y     0x00000000004066ad in CTorTester
 | 
					
						
							|  |  |  |     //  at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
 | 
					
						
							|  |  |  |     // - or - 
 | 
					
						
							|  |  |  |     // everything on a single line on Windows for constructors of classes
 | 
					
						
							|  |  |  |     // within namespaces.
 | 
					
						
							|  |  |  |     // Sometimes the path is relative too.
 | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |     QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)"); | 
					
						
							|  |  |  |     re.setMinimal(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (re.indexIn(output) != -1) { | 
					
						
							|  |  |  |         data->bpAddress = re.cap(1); | 
					
						
							|  |  |  |         data->bpFuncName = re.cap(2).trimmed(); | 
					
						
							|  |  |  |         data->bpLineNumber = re.cap(4); | 
					
						
							|  |  |  |         QString full = fullName(re.cap(3)); | 
					
						
							|  |  |  |         data->markerLineNumber = data->bpLineNumber.toInt(); | 
					
						
							|  |  |  |         data->markerFileName = full; | 
					
						
							|  |  |  |         data->bpFileName = full; | 
					
						
							|  |  |  |         //qDebug() << "FOUND BREAKPOINT\n" << output
 | 
					
						
							|  |  |  |         //    << re.cap(1) << "\n" << re.cap(2) << "\n"
 | 
					
						
							|  |  |  |         //    << re.cap(3) << "\n" << re.cap(4) << "\n";
 | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output; | 
					
						
							|  |  |  |         data->bpNumber = "<unavailable>"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // Old-style output for multiple breakpoints, presumably in a
 | 
					
						
							|  |  |  |         // constructor
 | 
					
						
							|  |  |  |         int found = handler->findBreakpoint(bpNumber); | 
					
						
							|  |  |  |         if (found != -1) { | 
					
						
							|  |  |  |             QString str = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |             extractDataFromInfoBreak(str, handler->at(found)); | 
					
						
							|  |  |  |             handler->updateMarkers(); | 
					
						
							|  |  |  |             attemptBreakpointSynchronization(); // trigger "ready"
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // Pending breakpoints in dylibs on Mac only?
 | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         GdbMi bkpt = record.data.findChild("bkpt"); | 
					
						
							|  |  |  |         breakpointDataFromOutput(data, bkpt); | 
					
						
							|  |  |  |         attemptBreakpointSynchronization(); // trigger "ready"
 | 
					
						
							|  |  |  |         handler->updateMarkers(); | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP"; | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         data->bpNumber = "<unavailable>"; | 
					
						
							|  |  |  |         attemptBreakpointSynchronization(); // trigger "ready"
 | 
					
						
							|  |  |  |         handler->updateMarkers(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::attemptBreakpointSynchronization() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |     // Non-lethal check for nested calls
 | 
					
						
							|  |  |  |     static bool inBreakpointSychronization = false; | 
					
						
							|  |  |  |     QTC_ASSERT(!inBreakpointSychronization, /**/); | 
					
						
							|  |  |  |     inBreakpointSychronization = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     BreakHandler *handler = qq->breakHandler(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) { | 
					
						
							|  |  |  |         QString bpNumber = data->bpNumber; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |         debugMessage(QString("DELETING BP %1 IN %2").arg(bpNumber) | 
					
						
							|  |  |  |             .arg(data->markerFileName)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         if (!bpNumber.trimmed().isEmpty()) | 
					
						
							| 
									
										
										
										
											2009-02-17 17:42:08 +01:00
										 |  |  |             sendCommand("-break-delete " + bpNumber, BreakDelete, QVariant(), | 
					
						
							|  |  |  |                 NeedsStop); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         delete data; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool updateNeeded = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int index = 0; index != handler->size(); ++index) { | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         // multiple breakpoints?
 | 
					
						
							|  |  |  |         if (data->bpMultiple && data->bpFileName.isEmpty()) { | 
					
						
							|  |  |  |             sendCommand(QString("info break %1").arg(data->bpNumber), | 
					
						
							|  |  |  |                 BreakInfo, data->bpNumber.toInt()); | 
					
						
							|  |  |  |             updateNeeded = true; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int index = 0; index != handler->size(); ++index) { | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         // unset breakpoints?
 | 
					
						
							|  |  |  |         if (data->bpNumber.isEmpty()) { | 
					
						
							|  |  |  |             data->bpNumber = " "; | 
					
						
							|  |  |  |             sendInsertBreakpoint(index); | 
					
						
							|  |  |  |             //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
 | 
					
						
							|  |  |  |             updateNeeded = true; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!updateNeeded) { | 
					
						
							|  |  |  |         for (int index = 0; index != handler->size(); ++index) { | 
					
						
							|  |  |  |             BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |             // update conditions if needed
 | 
					
						
							|  |  |  |             if (data->bpNumber.toInt() && data->condition != data->bpCondition | 
					
						
							|  |  |  |                    && !data->conditionsMatch()) { | 
					
						
							|  |  |  |                 sendCommand(QString("condition %1 %2").arg(data->bpNumber) | 
					
						
							|  |  |  |                     .arg(data->condition), BreakCondition, index); | 
					
						
							|  |  |  |                 //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
 | 
					
						
							|  |  |  |                 //    << data->condition << data->bpCondition;
 | 
					
						
							|  |  |  |                 updateNeeded = true; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // update ignorecount if needed
 | 
					
						
							|  |  |  |             if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) { | 
					
						
							|  |  |  |                 sendCommand(QString("ignore %1 %2").arg(data->bpNumber) | 
					
						
							|  |  |  |                     .arg(data->ignoreCount), BreakIgnore, index); | 
					
						
							|  |  |  |                 updateNeeded = true; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int index = 0; index != handler->size(); ++index) { | 
					
						
							|  |  |  |         // happens sometimes on Mac. Brush over symptoms
 | 
					
						
							|  |  |  |         BreakpointData *data = handler->at(index); | 
					
						
							|  |  |  |         if (data->markerFileName.startsWith("../")) { | 
					
						
							|  |  |  |             data->markerFileName = fullName(data->markerFileName); | 
					
						
							|  |  |  |             handler->updateMarkers(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |     if (!updateNeeded && m_waitingForBreakpointSynchronizationToContinue) { | 
					
						
							|  |  |  |         m_waitingForBreakpointSynchronizationToContinue = false; | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |         // we continue the execution
 | 
					
						
							|  |  |  |         continueInferior(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     inBreakpointSychronization = false; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Disassembler specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::reloadDisassembler() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     emit sendCommand("disassemble", DisassemblerList, m_address); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleDisassemblerList(const GdbResultRecord &record, | 
					
						
							|  |  |  |     const QString &cookie) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QList<DisassemblerLine> lines; | 
					
						
							|  |  |  |     static const QString pad = QLatin1String("    "); | 
					
						
							|  |  |  |     int currentLine = -1; | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         QString res = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         QTextStream ts(&res, QIODevice::ReadOnly); | 
					
						
							|  |  |  |         while (!ts.atEnd()) { | 
					
						
							|  |  |  |             //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
 | 
					
						
							|  |  |  |             //    jmpq   *2151890(%rip)    # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
 | 
					
						
							|  |  |  |             //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
 | 
					
						
							|  |  |  |             //    pushq  $0x4d
 | 
					
						
							|  |  |  |             //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
 | 
					
						
							|  |  |  |             //    jmpq   0x405af8 <_init+24>
 | 
					
						
							|  |  |  |             //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
 | 
					
						
							|  |  |  |             //    jmpq   *2151882(%rip)    # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
 | 
					
						
							|  |  |  |             QString str = ts.readLine(); | 
					
						
							|  |  |  |             if (!str.startsWith(QLatin1String("0x"))) { | 
					
						
							|  |  |  |                 //qDebug() << "IGNORING DISASSEMBLER" << str;
 | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             DisassemblerLine line; | 
					
						
							|  |  |  |             QTextStream ts(&str, QIODevice::ReadOnly); | 
					
						
							|  |  |  |             ts >> line.address >> line.symbol; | 
					
						
							|  |  |  |             line.mnemonic = ts.readLine().trimmed(); | 
					
						
							|  |  |  |             if (line.symbol.endsWith(QLatin1Char(':'))) | 
					
						
							|  |  |  |                 line.symbol.chop(1); | 
					
						
							|  |  |  |             line.addressDisplay = line.address + pad; | 
					
						
							|  |  |  |             if (line.addressDisplay.startsWith(QLatin1String("0x00000000"))) | 
					
						
							|  |  |  |                 line.addressDisplay.replace(2, 8, QString()); | 
					
						
							|  |  |  |             line.symbolDisplay = line.symbol + pad; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (line.address == cookie) | 
					
						
							|  |  |  |                 currentLine = lines.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             lines.append(line); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         DisassemblerLine line; | 
					
						
							|  |  |  |         line.addressDisplay = tr("<could not retreive module information>"); | 
					
						
							|  |  |  |         lines.append(line); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qq->disassemblerHandler()->setLines(lines); | 
					
						
							|  |  |  |     if (currentLine != -1) | 
					
						
							|  |  |  |         qq->disassemblerHandler()->setCurrentLine(currentLine); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Modules specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::loadSymbols(const QString &moduleName) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: gdb does not understand quoted names here (tested with 6.8)
 | 
					
						
							|  |  |  |     sendCommand("sharedlibrary " + dotEscape(moduleName)); | 
					
						
							|  |  |  |     reloadModules(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::loadAllSymbols() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     sendCommand("sharedlibrary .*"); | 
					
						
							|  |  |  |     reloadModules(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::reloadModules() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     sendCommand("info shared", ModulesList, QVariant()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleModulesList(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QList<Module> modules; | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         QString data = record.data.findChild("consolestreamoutput").data(); | 
					
						
							|  |  |  |         QTextStream ts(&data, QIODevice::ReadOnly); | 
					
						
							|  |  |  |         while (!ts.atEnd()) { | 
					
						
							|  |  |  |             QString line = ts.readLine(); | 
					
						
							|  |  |  |             if (!line.startsWith("0x")) | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             Module module; | 
					
						
							|  |  |  |             QString symbolsRead; | 
					
						
							|  |  |  |             QTextStream ts(&line, QIODevice::ReadOnly); | 
					
						
							|  |  |  |             ts >> module.startAddress >> module.endAddress >> symbolsRead; | 
					
						
							|  |  |  |             module.moduleName = ts.readLine().trimmed(); | 
					
						
							|  |  |  |             module.symbolsRead = (symbolsRead == "Yes"); | 
					
						
							|  |  |  |             modules.append(module); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qq->modulesHandler()->setModules(modules); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Stack specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Q_UNUSED(record); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
 | 
					
						
							|  |  |  |     q->showStatusMessage(tr("Retrieving data for stack view..."), 3000); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     sendCommand("-stack-list-frames", StackListFrames); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleStackListFrames(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QList<StackFrame> stackFrames; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const GdbMi stack = record.data.findChild("stack"); | 
					
						
							|  |  |  |     QString dummy = stack.toString(); | 
					
						
							|  |  |  |     if (!stack.isValid()) { | 
					
						
							|  |  |  |         qDebug() << "FIXME: stack: " << stack.toString(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int topFrame = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i != stack.childCount(); ++i) { | 
					
						
							|  |  |  |         //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
 | 
					
						
							|  |  |  |         const GdbMi frameMi = stack.childAt(i); | 
					
						
							|  |  |  |         StackFrame frame; | 
					
						
							|  |  |  |         frame.level = i; | 
					
						
							|  |  |  |         QStringList files; | 
					
						
							|  |  |  |         files.append(frameMi.findChild("fullname").data()); | 
					
						
							|  |  |  |         files.append(frameMi.findChild("file").data()); | 
					
						
							|  |  |  |         frame.file = fullName(files); | 
					
						
							|  |  |  |         frame.function = frameMi.findChild("func").data(); | 
					
						
							|  |  |  |         frame.from = frameMi.findChild("from").data(); | 
					
						
							|  |  |  |         frame.line = frameMi.findChild("line").data().toInt(); | 
					
						
							|  |  |  |         frame.address = frameMi.findChild("addr").data(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stackFrames.append(frame); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef Q_OS_WIN
 | 
					
						
							|  |  |  |         const bool isBogus = | 
					
						
							|  |  |  |             // Assume this is wrong and points to some strange stl_algobase
 | 
					
						
							|  |  |  |             // implementation. Happens on Karsten's XP system with Gdb 5.50
 | 
					
						
							|  |  |  |             (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150) | 
					
						
							|  |  |  |             // Also wrong. Happens on Vista with Gdb 5.50
 | 
					
						
							|  |  |  |                || (frame.function == "operator new" && frame.line == 151); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // immediately leave bogus frames
 | 
					
						
							|  |  |  |         if (topFrame == -1 && isBogus) { | 
					
						
							|  |  |  |             sendCommand("-exec-finish"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Initialize top frame to the first valid frame
 | 
					
						
							|  |  |  |         const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty(); | 
					
						
							|  |  |  |         if (isValid && topFrame == -1) | 
					
						
							|  |  |  |             topFrame = i; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qq->stackHandler()->setFrames(stackFrames); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  |     if (0 && topFrame != -1) { | 
					
						
							|  |  |  |         // updates of locals already triggered early
 | 
					
						
							|  |  |  |         const StackFrame &frame = qq->stackHandler()->currentFrame(); | 
					
						
							|  |  |  |         bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); | 
					
						
							|  |  |  |         if (usable) | 
					
						
							|  |  |  |             q->gotoLocation(frame.file, frame.line, true); | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         activateFrame(topFrame); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     if (topFrame != -1) { | 
					
						
							|  |  |  |         // updates of locals already triggered early
 | 
					
						
							|  |  |  |         const StackFrame &frame = qq->stackHandler()->currentFrame(); | 
					
						
							|  |  |  |         bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); | 
					
						
							|  |  |  |         if (usable) | 
					
						
							|  |  |  |             q->gotoLocation(frame.file, frame.line, true); | 
					
						
							|  |  |  |         else | 
					
						
							| 
									
										
										
										
											2009-01-05 14:38:51 +01:00
										 |  |  |             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::selectThread(int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //reset location arrow
 | 
					
						
							|  |  |  |     q->resetLocation(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ThreadsHandler *threadsHandler = qq->threadsHandler(); | 
					
						
							|  |  |  |     threadsHandler->setCurrentThread(index); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QList<ThreadData> threads = threadsHandler->threads(); | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(index < threads.size(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     int id = threads.at(index).id; | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Retrieving data for stack view..."), 10000); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     sendCommand(QLatin1String("-thread-select ") + QString::number(id), | 
					
						
							|  |  |  |         StackSelectThread); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::activateFrame(int frameIndex) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (q->status() != DebuggerInferiorStopped) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     StackHandler *stackHandler = qq->stackHandler(); | 
					
						
							|  |  |  |     int oldIndex = stackHandler->currentIndex(); | 
					
						
							|  |  |  |     //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
 | 
					
						
							|  |  |  |     //    << stackHandler->currentIndex();
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(frameIndex < stackHandler->stackSize(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (oldIndex != frameIndex) { | 
					
						
							|  |  |  |         // Assuming this always succeeds saves a roundtrip.
 | 
					
						
							|  |  |  |         // Otherwise the lines below would need to get triggered
 | 
					
						
							|  |  |  |         // after a response to this -stack-select-frame here.
 | 
					
						
							|  |  |  |         sendCommand("-stack-select-frame " + QString::number(frameIndex)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stackHandler->setCurrentIndex(frameIndex); | 
					
						
							|  |  |  |         updateLocals(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const StackFrame &frame = stackHandler->currentFrame(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); | 
					
						
							|  |  |  |     if (usable) | 
					
						
							|  |  |  |         q->gotoLocation(frame.file, frame.line, true); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         qDebug() << "FULL NAME NOT USABLE: " << frame.file; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
 | 
					
						
							|  |  |  |     const QList<GdbMi> items = record.data.findChild("thread-ids").children(); | 
					
						
							|  |  |  |     QList<ThreadData> threads; | 
					
						
							|  |  |  |     int currentIndex = -1; | 
					
						
							|  |  |  |     for (int index = 0, n = items.size(); index != n; ++index) { | 
					
						
							|  |  |  |         ThreadData thread; | 
					
						
							|  |  |  |         thread.id = items.at(index).data().toInt(); | 
					
						
							|  |  |  |         threads.append(thread); | 
					
						
							|  |  |  |         if (thread.id == id) { | 
					
						
							|  |  |  |             //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
 | 
					
						
							|  |  |  |             currentIndex = index; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ThreadsHandler *threadsHandler = qq->threadsHandler(); | 
					
						
							|  |  |  |     threadsHandler->setThreads(threads); | 
					
						
							|  |  |  |     threadsHandler->setCurrentThread(currentIndex); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Register specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::reloadRegisters() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString(); | 
					
						
							|  |  |  |     sendCommand("-data-list-register-values " + format, RegisterListValues); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleRegisterListNames(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record.resultClass != GdbResultDone) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QList<Register> registers; | 
					
						
							|  |  |  |     foreach (const GdbMi &item, record.data.findChild("register-names").children()) | 
					
						
							|  |  |  |         registers.append(Register(item.data())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qq->registerHandler()->setRegisters(registers); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleRegisterListValues(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record.resultClass != GdbResultDone) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QList<Register> registers = qq->registerHandler()->registers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 24^done,register-values=[{number="0",value="0xf423f"},...]
 | 
					
						
							|  |  |  |     foreach (const GdbMi &item, record.data.findChild("register-values").children()) { | 
					
						
							|  |  |  |         int index = item.findChild("number").data().toInt(); | 
					
						
							|  |  |  |         if (index < registers.size()) { | 
					
						
							|  |  |  |             Register ® = registers[index]; | 
					
						
							|  |  |  |             QString value = item.findChild("value").data(); | 
					
						
							|  |  |  |             reg.changed = (value != reg.value); | 
					
						
							|  |  |  |             if (reg.changed) | 
					
						
							|  |  |  |                 reg.value = value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qq->registerHandler()->setRegisters(registers); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Thread specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool GdbEngine::supportsThreads() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // 6.3 crashes happily on -thread-list-ids. So don't use it.
 | 
					
						
							|  |  |  |     // The test below is a semi-random pick, 6.8 works fine
 | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     return m_gdbVersion > 60500; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Tooltip specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static WatchData m_toolTip; | 
					
						
							|  |  |  | static QString m_toolTipExpression; | 
					
						
							|  |  |  | static QPoint m_toolTipPos; | 
					
						
							|  |  |  | static QHash<QString, WatchData> m_toolTipCache; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool hasLetterOrNumber(const QString &exp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     for (int i = exp.size(); --i >= 0; ) | 
					
						
							|  |  |  |         if (exp[i].isLetterOrNumber() || exp[i] == '_') | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool hasSideEffects(const QString &exp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: complete?
 | 
					
						
							|  |  |  |     return exp.contains("-=") | 
					
						
							|  |  |  |         || exp.contains("+=") | 
					
						
							|  |  |  |         || exp.contains("/=") | 
					
						
							|  |  |  |         || exp.contains("*=") | 
					
						
							|  |  |  |         || exp.contains("&=") | 
					
						
							|  |  |  |         || exp.contains("|=") | 
					
						
							|  |  |  |         || exp.contains("^=") | 
					
						
							|  |  |  |         || exp.contains("--") | 
					
						
							|  |  |  |         || exp.contains("++"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isKeyWord(const QString &exp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: incomplete
 | 
					
						
							|  |  |  |     return exp == QLatin1String("class") | 
					
						
							|  |  |  |         || exp == QLatin1String("const") | 
					
						
							|  |  |  |         || exp == QLatin1String("do") | 
					
						
							|  |  |  |         || exp == QLatin1String("if") | 
					
						
							|  |  |  |         || exp == QLatin1String("return") | 
					
						
							|  |  |  |         || exp == QLatin1String("struct") | 
					
						
							|  |  |  |         || exp == QLatin1String("template") | 
					
						
							|  |  |  |         || exp == QLatin1String("void") | 
					
						
							|  |  |  |         || exp == QLatin1String("volatile") | 
					
						
							|  |  |  |         || exp == QLatin1String("while"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
 | 
					
						
							|  |  |  |     if (q->status() != DebuggerInferiorStopped) { | 
					
						
							|  |  |  |         //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     if (q->settings()->m_debugDumpers) { | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         // minimize interference
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_toolTipPos = pos; | 
					
						
							|  |  |  |     m_toolTipExpression = exp0; | 
					
						
							|  |  |  |     QString exp = exp0; | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |     if (m_toolTip.isTypePending()) { | 
					
						
							|  |  |  |         qDebug() << "suppressing duplicated tooltip creation"; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  |     if (m_toolTipCache.contains(exp)) { | 
					
						
							|  |  |  |         const WatchData & data = m_toolTipCache[exp]; | 
					
						
							|  |  |  |         // FIXME: qq->watchHandler()->collapseChildren(data.iname);
 | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QToolTip::hideText(); | 
					
						
							|  |  |  |     if (exp.isEmpty() || exp.startsWith("#"))  { | 
					
						
							|  |  |  |         QToolTip::hideText(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!hasLetterOrNumber(exp)) { | 
					
						
							|  |  |  |         QToolTip::showText(m_toolTipPos, | 
					
						
							|  |  |  |             "'" + exp + "' contains no identifier"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (isKeyWord(exp)) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (exp.startsWith('"') && exp.endsWith('"'))  { | 
					
						
							|  |  |  |         QToolTip::showText(m_toolTipPos, "String literal " + exp); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (exp.startsWith("++") || exp.startsWith("--")) | 
					
						
							|  |  |  |         exp = exp.mid(2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (exp.endsWith("++") || exp.endsWith("--")) | 
					
						
							|  |  |  |         exp = exp.mid(2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (exp.startsWith("<") || exp.startsWith("[")) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (hasSideEffects(exp)) { | 
					
						
							|  |  |  |         QToolTip::showText(m_toolTipPos, | 
					
						
							|  |  |  |             "Cowardly refusing to evaluate expression '" + exp | 
					
						
							|  |  |  |                 + "' with potential side effects"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Gdb crashes when creating a variable object with the name
 | 
					
						
							|  |  |  |     // of the type of 'this'
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |     for (int i = 0; i != m_currentLocals.childCount(); ++i) { | 
					
						
							|  |  |  |         if (m_currentLocals.childAt(i).exp == "this") { | 
					
						
							|  |  |  |             qDebug() << "THIS IN ROW " << i; | 
					
						
							|  |  |  |             if (m_currentLocals.childAt(i).type.startsWith(exp)) { | 
					
						
							|  |  |  |                 QToolTip::showText(m_toolTipPos, | 
					
						
							|  |  |  |                     exp + ": type of current 'this'"); | 
					
						
							|  |  |  |                 qDebug() << " TOOLTIP CRASH SUPPRESSED"; | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //if (m_manager->status() != DebuggerInferiorStopped)
 | 
					
						
							|  |  |  |     //    return;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // FIXME: 'exp' can contain illegal characters
 | 
					
						
							|  |  |  |     m_toolTip = WatchData(); | 
					
						
							|  |  |  |     //m_toolTip.level = 0;
 | 
					
						
							|  |  |  |    // m_toolTip.row = 0;
 | 
					
						
							|  |  |  |    // m_toolTip.parentIndex = 2;
 | 
					
						
							|  |  |  |     m_toolTip.exp = exp; | 
					
						
							|  |  |  |     m_toolTip.name = exp; | 
					
						
							|  |  |  |     m_toolTip.iname = tooltipIName; | 
					
						
							|  |  |  |     insertData(m_toolTip); | 
					
						
							|  |  |  |     updateWatchModel2(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Watch specific stuff
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const QString strNotInScope = QLatin1String("<not in scope>"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isPointerType(const QString &type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return type.endsWith("*") || type.endsWith("* const"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isAccessSpecifier(const QString &str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static const QStringList items = | 
					
						
							|  |  |  |         QStringList() << "private" << "protected" << "public"; | 
					
						
							|  |  |  |     return items.contains(str); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool startsWithDigit(const QString &str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return !str.isEmpty() && str[0] >= '0' && str[0] <= '9'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString stripPointerType(QString type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (type.endsWith("*")) | 
					
						
							|  |  |  |         type.chop(1); | 
					
						
							|  |  |  |     if (type.endsWith("* const")) | 
					
						
							|  |  |  |         type.chop(7); | 
					
						
							|  |  |  |     if (type.endsWith(' ')) | 
					
						
							|  |  |  |         type.chop(1); | 
					
						
							|  |  |  |     return type; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static QString gdbQuoteTypes(const QString &type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // gdb does not understand sizeof(Core::IFile*).
 | 
					
						
							|  |  |  |     // "sizeof('Core::IFile*')" is also not acceptable,
 | 
					
						
							|  |  |  |     // it needs to be "sizeof('Core::IFile'*)"
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // We never will have a perfect solution here (even if we had a full blown
 | 
					
						
							|  |  |  |     // C++ parser as we do not have information on what is a type and what is
 | 
					
						
							|  |  |  |     // a vriable name. So "a<b>::c" could either be two comparisons of values
 | 
					
						
							|  |  |  |     // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
 | 
					
						
							|  |  |  |     // assume here it is the latter.
 | 
					
						
							|  |  |  |     //return type;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
 | 
					
						
							|  |  |  |     // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
 | 
					
						
							|  |  |  |     if (isPointerType(type)) | 
					
						
							|  |  |  |         return gdbQuoteTypes(stripPointerType(type)) + "*"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QString accu; | 
					
						
							|  |  |  |     QString result; | 
					
						
							|  |  |  |     int templateLevel = 0; | 
					
						
							|  |  |  |     for (int i = 0; i != type.size(); ++i) { | 
					
						
							|  |  |  |         QChar c = type.at(i); | 
					
						
							|  |  |  |         if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') { | 
					
						
							|  |  |  |             accu += c; | 
					
						
							|  |  |  |         } else if (c == '<') { | 
					
						
							|  |  |  |             ++templateLevel; | 
					
						
							|  |  |  |             accu += c; | 
					
						
							|  |  |  |         } else if (c == '<') { | 
					
						
							|  |  |  |             --templateLevel; | 
					
						
							|  |  |  |             accu += c; | 
					
						
							|  |  |  |         } else if (templateLevel > 0) { | 
					
						
							|  |  |  |             accu += c; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             if (accu.contains(':') || accu.contains('<')) | 
					
						
							|  |  |  |                 result += '\'' + accu + '\''; | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 result += accu; | 
					
						
							|  |  |  |             accu.clear(); | 
					
						
							|  |  |  |             result += c; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (accu.contains(':') || accu.contains('<')) | 
					
						
							|  |  |  |         result += '\'' + accu + '\''; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         result += accu; | 
					
						
							|  |  |  |     //qDebug() << "GDB_QUOTING" << type << " TO " << result;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataValue(WatchData &data, const GdbMi &mi, | 
					
						
							|  |  |  |     int encoding = 0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) { | 
					
						
							|  |  |  |         QByteArray ba; | 
					
						
							|  |  |  |         switch (encoding) { | 
					
						
							|  |  |  |             case 0: // unencoded 8 bit data
 | 
					
						
							|  |  |  |                 ba = mi.data(); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case 1: //  base64 encoded 8 bit data
 | 
					
						
							|  |  |  |                 ba = QByteArray::fromBase64(mi.data()); | 
					
						
							| 
									
										
										
										
											2009-01-28 15:17:55 +01:00
										 |  |  |                 ba = '"' + ba + '"'; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             case 2: //  base64 encoded 16 bit data
 | 
					
						
							|  |  |  |                 ba = QByteArray::fromBase64(mi.data()); | 
					
						
							|  |  |  |                 ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8(); | 
					
						
							| 
									
										
										
										
											2009-01-28 15:17:55 +01:00
										 |  |  |                 ba = '"' + ba + '"'; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             case 3: //  base64 encoded 32 bit data
 | 
					
						
							|  |  |  |                 ba = QByteArray::fromBase64(mi.data()); | 
					
						
							|  |  |  |                 ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8(); | 
					
						
							| 
									
										
										
										
											2009-01-28 15:17:55 +01:00
										 |  |  |                 ba = '"' + ba + '"'; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |        data.setValue(ba); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |        data.setValueNeeded(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataEditValue(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) | 
					
						
							|  |  |  |         data.editvalue = mi.data(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) | 
					
						
							|  |  |  |         data.setValueToolTip(mi.data()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataChildCount(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) { | 
					
						
							|  |  |  |         data.childCount = mi.data().toInt(); | 
					
						
							|  |  |  |         data.setChildCountUnneeded(); | 
					
						
							|  |  |  |         if (data.childCount == 0) | 
					
						
							|  |  |  |             data.setChildrenUnneeded(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         data.childCount = -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.data() == "true") | 
					
						
							|  |  |  |         data.valuedisabled = true; | 
					
						
							|  |  |  |     else if (mi.data() == "false") | 
					
						
							|  |  |  |         data.valuedisabled = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataExpression(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) | 
					
						
							|  |  |  |         data.exp = "(" + mi.data() + ")"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setWatchDataAddress(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) { | 
					
						
							|  |  |  |         data.addr = mi.data(); | 
					
						
							|  |  |  |         if (data.exp.isEmpty()) | 
					
						
							|  |  |  |             data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool extractTemplate(const QString &type, QString *tmplate, QString *inner) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
 | 
					
						
							|  |  |  |     // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
 | 
					
						
							|  |  |  |     // whether parsing was successful
 | 
					
						
							|  |  |  |     int level = 0; | 
					
						
							| 
									
										
										
										
											2009-01-28 16:55:09 +01:00
										 |  |  |     bool skipSpace = false; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     for (int i = 0; i != type.size(); ++i) { | 
					
						
							|  |  |  |         QChar c = type[i]; | 
					
						
							| 
									
										
										
										
											2009-01-28 16:55:09 +01:00
										 |  |  |         if (c == ' ' && skipSpace) { | 
					
						
							|  |  |  |             skipSpace = false; | 
					
						
							|  |  |  |         } else if (c == '<') { | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             *(level == 0 ? tmplate : inner) += c; | 
					
						
							|  |  |  |             ++level; | 
					
						
							|  |  |  |         } else if (c == '>') { | 
					
						
							|  |  |  |             --level; | 
					
						
							|  |  |  |             *(level == 0 ? tmplate : inner) += c; | 
					
						
							|  |  |  |         } else if (c == ',') { | 
					
						
							|  |  |  |             *inner += (level == 1) ? '@' : ','; | 
					
						
							| 
									
										
										
										
											2009-01-28 16:55:09 +01:00
										 |  |  |             skipSpace = true; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             *(level == 0 ? tmplate : inner) += c; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *tmplate = tmplate->trimmed(); | 
					
						
							|  |  |  |     *tmplate = tmplate->remove("<>"); | 
					
						
							|  |  |  |     *inner = inner->trimmed(); | 
					
						
							| 
									
										
										
										
											2009-01-28 17:34:23 +01:00
										 |  |  |     //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     return !inner->isEmpty(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static QString extractTypeFromPTypeOutput(const QString &str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int pos0 = str.indexOf('='); | 
					
						
							|  |  |  |     int pos1 = str.indexOf('{'); | 
					
						
							|  |  |  |     int pos2 = str.lastIndexOf('}'); | 
					
						
							|  |  |  |     QString res = str; | 
					
						
							|  |  |  |     if (pos0 != -1 && pos1 != -1 && pos2 != -1) | 
					
						
							|  |  |  |         res = str.mid(pos0 + 2, pos1 - 1 - pos0) | 
					
						
							|  |  |  |             + " ... " + str.right(str.size() - pos2); | 
					
						
							|  |  |  |     return res.simplified(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool isIntOrFloatType(const QString &type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static const QStringList types = QStringList() | 
					
						
							|  |  |  |         << "char" << "int" << "short" << "float" << "double" << "long" | 
					
						
							|  |  |  |         << "bool" << "signed char" << "unsigned" << "unsigned char" | 
					
						
							|  |  |  |         << "unsigned int" << "unsigned long" << "long long" | 
					
						
							|  |  |  |         << "unsigned long long"; | 
					
						
							|  |  |  |     return types.contains(type); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static QString sizeofTypeExpression(const QString &type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (type.endsWith('*')) | 
					
						
							|  |  |  |         return "sizeof(void*)"; | 
					
						
							|  |  |  |     if (type.endsWith('>')) | 
					
						
							|  |  |  |         return "sizeof(" + type + ")"; | 
					
						
							|  |  |  |     return "sizeof(" + gdbQuoteTypes(type) + ")"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-27 15:53:50 +01:00
										 |  |  | void GdbEngine::setUseCustomDumpers(bool on) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |     //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     Q_UNUSED(on); | 
					
						
							| 
									
										
										
										
											2009-01-27 15:53:50 +01:00
										 |  |  |     // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     //m_expandedINames.clear();
 | 
					
						
							|  |  |  |     updateLocals(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     DebuggerSettings *s = q->settings(); | 
					
						
							|  |  |  |     if (!s->m_useCustomDumpers) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |     if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers()) | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     if (m_dataDumperState != DataDumperAvailable) | 
					
						
							|  |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2008-12-18 10:11:10 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // simple types
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (m_availableSimpleDumpers.contains(type)) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-18 10:11:10 +01:00
										 |  |  |     // templates
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     QString tmplate; | 
					
						
							|  |  |  |     QString inner; | 
					
						
							| 
									
										
										
										
											2008-12-18 10:11:10 +01:00
										 |  |  |     if (!extractTemplate(type, &tmplate, &inner)) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     return m_availableSimpleDumpers.contains(tmplate); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(!data.exp.isEmpty(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     QString tmplate; | 
					
						
							|  |  |  |     QString inner; | 
					
						
							|  |  |  |     bool isTemplate = extractTemplate(data.type, &tmplate, &inner); | 
					
						
							|  |  |  |     QStringList inners = inner.split('@'); | 
					
						
							|  |  |  |     if (inners.at(0).isEmpty()) | 
					
						
							|  |  |  |         inners.clear(); | 
					
						
							| 
									
										
										
										
											2008-12-12 12:55:05 +01:00
										 |  |  |     for (int i = 0; i != inners.size(); ++i) | 
					
						
							|  |  |  |         inners[i] = inners[i].simplified(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     QString outertype = isTemplate ? tmplate : data.type; | 
					
						
							| 
									
										
										
										
											2008-12-18 10:11:10 +01:00
										 |  |  |     // adjust the data extract
 | 
					
						
							| 
									
										
										
										
											2009-01-07 15:03:49 +01:00
										 |  |  |     if (outertype == m_namespace + "QWidget") | 
					
						
							|  |  |  |         outertype = m_namespace + "QObject"; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     QString extraArgs[4]; | 
					
						
							|  |  |  |     extraArgs[0] = "0"; | 
					
						
							|  |  |  |     extraArgs[1] = "0"; | 
					
						
							|  |  |  |     extraArgs[2] = "0"; | 
					
						
							|  |  |  |     extraArgs[3] = "0"; | 
					
						
							|  |  |  |     int extraArgCount = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // "generic" template dumpers: passing sizeof(argument)
 | 
					
						
							|  |  |  |     // gives already most information the dumpers need
 | 
					
						
							|  |  |  |     foreach (const QString &arg, inners) | 
					
						
							|  |  |  |         extraArgs[extraArgCount++] = sizeofTypeExpression(arg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // in rare cases we need more or less:
 | 
					
						
							|  |  |  |     if (outertype == m_namespace + "QObject") { | 
					
						
							| 
									
										
										
										
											2008-12-19 09:15:02 +01:00
										 |  |  |         extraArgs[0] = "(char*)&((('" | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             + m_namespace + "QObjectPrivate'*)&" | 
					
						
							|  |  |  |             + data.exp + ")->children)-(char*)&" + data.exp; | 
					
						
							|  |  |  |     } else if (outertype == m_namespace + "QVector") { | 
					
						
							| 
									
										
										
										
											2008-12-19 09:15:02 +01:00
										 |  |  |         extraArgs[1] = "(char*)&((" | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             + data.exp + ").d->array)-(char*)" + data.exp + ".d"; | 
					
						
							|  |  |  |     } else if (outertype == m_namespace + "QObjectSlot" | 
					
						
							|  |  |  |             || outertype == m_namespace + "QObjectSignal") { | 
					
						
							|  |  |  |         // we need the number out of something like
 | 
					
						
							|  |  |  |         // iname="local.ob.slots.[2]deleteLater()"
 | 
					
						
							|  |  |  |         int lastOpened = data.iname.lastIndexOf('['); | 
					
						
							|  |  |  |         int lastClosed = data.iname.lastIndexOf(']'); | 
					
						
							|  |  |  |         QString slotNumber = "-1"; | 
					
						
							|  |  |  |         if (lastOpened != -1 && lastClosed != -1) | 
					
						
							|  |  |  |             slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1); | 
					
						
							| 
									
										
										
										
											2008-12-19 09:15:02 +01:00
										 |  |  |         extraArgs[0] = slotNumber; | 
					
						
							| 
									
										
										
										
											2009-01-07 16:29:14 +01:00
										 |  |  |     } else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") { | 
					
						
							| 
									
										
										
										
											2009-01-07 15:03:49 +01:00
										 |  |  |         QString nodetype; | 
					
						
							|  |  |  |         if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) { | 
					
						
							|  |  |  |             nodetype  = m_namespace + "QMapNode"; | 
					
						
							| 
									
										
										
										
											2009-01-07 16:29:14 +01:00
										 |  |  |             nodetype += data.type.mid(outertype.size()); | 
					
						
							| 
									
										
										
										
											2009-01-07 15:03:49 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2009-01-07 17:41:09 +01:00
										 |  |  |             // FIXME: doesn't work for QMultiMap
 | 
					
						
							| 
									
										
										
										
											2009-01-07 15:03:49 +01:00
										 |  |  |             nodetype  = data.type + "::Node";  | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
 | 
					
						
							|  |  |  |         //    << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
 | 
					
						
							| 
									
										
										
										
											2008-12-19 09:15:02 +01:00
										 |  |  |         extraArgs[2] = sizeofTypeExpression(nodetype); | 
					
						
							|  |  |  |         extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value"; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } else if (outertype == m_namespace + "QMapNode") { | 
					
						
							| 
									
										
										
										
											2008-12-19 09:15:02 +01:00
										 |  |  |         extraArgs[2] = sizeofTypeExpression(data.type); | 
					
						
							|  |  |  |         extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value"; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } else if (outertype == "std::vector") { | 
					
						
							|  |  |  |         //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
 | 
					
						
							|  |  |  |         if (inners.at(0) == "bool") { | 
					
						
							|  |  |  |             outertype = "std::vector::bool"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
 | 
					
						
							|  |  |  |             //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
 | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-01-16 13:10:42 +01:00
										 |  |  |     } else if (outertype == "std::deque") { | 
					
						
							|  |  |  |         // remove 'std::allocator<...>':
 | 
					
						
							|  |  |  |         extraArgs[1] = "0"; | 
					
						
							|  |  |  |     } else if (outertype == "std::stack") { | 
					
						
							|  |  |  |         // remove 'std::allocator<...>':
 | 
					
						
							|  |  |  |         extraArgs[1] = "0"; | 
					
						
							| 
									
										
										
										
											2008-12-12 12:55:05 +01:00
										 |  |  |     } else if (outertype == "std::map") { | 
					
						
							|  |  |  |         // We don't want the comparator and the allocator confuse gdb.
 | 
					
						
							|  |  |  |         // But we need the offset of the second item in the value pair.
 | 
					
						
							| 
									
										
										
										
											2008-12-17 15:46:27 +01:00
										 |  |  |         // We read the type of the pair from the allocator argument because
 | 
					
						
							|  |  |  |         // that gets the constness "right" (in the sense that gdb can
 | 
					
						
							|  |  |  |         // read it back;
 | 
					
						
							|  |  |  |         QString pairType = inners.at(3); | 
					
						
							|  |  |  |         // remove 'std::allocator<...>':
 | 
					
						
							|  |  |  |         pairType = pairType.mid(15, pairType.size() - 15 - 2); | 
					
						
							|  |  |  |         extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second"; | 
					
						
							| 
									
										
										
										
											2008-12-12 12:55:05 +01:00
										 |  |  |         extraArgs[3] = "0"; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } else if (outertype == "std::basic_string") { | 
					
						
							|  |  |  |         //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
 | 
					
						
							|  |  |  |         if (inners.at(0) == "char") { | 
					
						
							|  |  |  |             outertype = "std::string"; | 
					
						
							|  |  |  |         } else if (inners.at(0) == "wchar_t") { | 
					
						
							|  |  |  |             outertype = "std::wstring"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         extraArgs[0] = "0"; | 
					
						
							|  |  |  |         extraArgs[1] = "0"; | 
					
						
							|  |  |  |         extraArgs[2] = "0"; | 
					
						
							|  |  |  |         extraArgs[3] = "0"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
 | 
					
						
							|  |  |  |     //int protocol = data.iname.startsWith("watch") ? 3 : 2;
 | 
					
						
							|  |  |  |     int protocol = 2; | 
					
						
							|  |  |  |     //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QString addr; | 
					
						
							|  |  |  |     if (data.addr.startsWith("0x")) | 
					
						
							|  |  |  |         addr = "(void*)" + data.addr; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         addr = "&(" + data.exp + ")"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QByteArray params; | 
					
						
							|  |  |  |     params.append(outertype); | 
					
						
							|  |  |  |     params.append('\0'); | 
					
						
							|  |  |  |     params.append(data.iname); | 
					
						
							|  |  |  |     params.append('\0'); | 
					
						
							|  |  |  |     params.append(data.exp); | 
					
						
							|  |  |  |     params.append('\0'); | 
					
						
							|  |  |  |     params.append(inner); | 
					
						
							|  |  |  |     params.append('\0'); | 
					
						
							|  |  |  |     params.append(data.iname); | 
					
						
							|  |  |  |     params.append('\0'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sendWatchParameters(params); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QString cmd ="call " | 
					
						
							|  |  |  |             + QString("qDumpObjectData440(") | 
					
						
							|  |  |  |             + QString::number(protocol) | 
					
						
							|  |  |  |             + ',' + "%1+1"                // placeholder for token
 | 
					
						
							|  |  |  |             + ',' + addr | 
					
						
							|  |  |  |             + ',' + (dumpChildren ? "1" : "0") | 
					
						
							|  |  |  |             + ',' + extraArgs[0] | 
					
						
							|  |  |  |             + ',' + extraArgs[1] | 
					
						
							|  |  |  |             + ',' + extraArgs[2] | 
					
						
							|  |  |  |             + ',' + extraArgs[3] + ')'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-12 12:55:05 +01:00
										 |  |  |     //qDebug() << "CMD: " << cmd;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |     QVariant var; | 
					
						
							|  |  |  |     var.setValue(data); | 
					
						
							|  |  |  |     sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     q->showStatusMessage( | 
					
						
							|  |  |  |         tr("Retrieving data for watch view (%1 requests pending)...") | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |             .arg(m_pendingRequests + 1), 10000); | 
					
						
							| 
									
										
										
										
											2009-01-21 11:41:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // retrieve response
 | 
					
						
							|  |  |  |     sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::createGdbVariable(const WatchData &data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     sendSynchronizedCommand("-var-delete \"" + data.iname + '"'); | 
					
						
							|  |  |  |     QString exp = data.exp; | 
					
						
							|  |  |  |     if (exp.isEmpty() && data.addr.startsWith("0x")) | 
					
						
							|  |  |  |         exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr; | 
					
						
							|  |  |  |     QVariant val = QVariant::fromValue<WatchData>(data); | 
					
						
							|  |  |  |     sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * " | 
					
						
							|  |  |  |         + '"' + exp + '"', WatchVarCreate, val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::updateSubItem(const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							|  |  |  |     #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |     qDebug() << "UPDATE SUBITEM: " << data.toString(); | 
					
						
							|  |  |  |     #endif
 | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(data.isValid(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // in any case we need the type first
 | 
					
						
							|  |  |  |     if (data.isTypeNeeded()) { | 
					
						
							|  |  |  |         // This should only happen if we don't have a variable yet.
 | 
					
						
							|  |  |  |         // Let's play safe, though.
 | 
					
						
							|  |  |  |         if (!data.variable.isEmpty()) { | 
					
						
							|  |  |  |             // Update: It does so for out-of-scope watchers.
 | 
					
						
							|  |  |  |             #if 1
 | 
					
						
							|  |  |  |             qDebug() << "FIXME: GdbEngine::updateSubItem: " | 
					
						
							|  |  |  |                  << data.toString() << "should not happen"; | 
					
						
							|  |  |  |             #else
 | 
					
						
							|  |  |  |             data.setType("<out of scope>"); | 
					
						
							|  |  |  |             data.setValue("<out of scope>"); | 
					
						
							|  |  |  |             data.setChildCount(0); | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |             #endif
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // The WatchVarCreate handler will receive type information
 | 
					
						
							|  |  |  |         // and re-insert a WatchData item with correct type, so
 | 
					
						
							|  |  |  |         // we will not re-enter this bit.
 | 
					
						
							|  |  |  |         // FIXME: Concurrency issues?
 | 
					
						
							|  |  |  |         createGdbVariable(data); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // we should have a type now. this is relied upon further below
 | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(!data.type.isEmpty(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // a common case that can be easily solved
 | 
					
						
							|  |  |  |     if (data.isChildrenNeeded() && isPointerType(data.type) | 
					
						
							|  |  |  |         && !isCustomValueDumperAvailable(data.type)) { | 
					
						
							|  |  |  |         // We sometimes know what kind of children pointers have
 | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "IT'S A POINTER"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  | #if 1
 | 
					
						
							|  |  |  |         WatchData data1; | 
					
						
							|  |  |  |         data1.iname = data.iname + ".*"; | 
					
						
							|  |  |  |         data1.name = "*" + data.name; | 
					
						
							|  |  |  |         data1.exp = "(*(" + data.exp + "))"; | 
					
						
							|  |  |  |         data1.type = stripPointerType(data.type); | 
					
						
							|  |  |  |         data1.setValueNeeded(); | 
					
						
							|  |  |  |         insertData(data1); | 
					
						
							|  |  |  |         data.setChildrenUnneeded(); | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |         // Try automatic dereferentiation
 | 
					
						
							|  |  |  |         data.exp = "*(" + data.exp + ")"; | 
					
						
							|  |  |  |         data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
 | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |     if (data.isValueNeeded() && data.exp.isEmpty()) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: NO EXPRESSION?"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         data.setError("<no expression given>"); | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isValueNeeded() && data.variable.isEmpty()) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         createGdbVariable(data); | 
					
						
							|  |  |  |         // the WatchVarCreate handler will re-insert a WatchData
 | 
					
						
							|  |  |  |         // item, with valueNeeded() set.
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isValueNeeded()) { | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: VALUE"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         QString cmd = "-var-evaluate-expression \"" + data.iname + "\""; | 
					
						
							|  |  |  |         sendSynchronizedCommand(cmd, WatchEvaluateExpression, | 
					
						
							|  |  |  |             QVariant::fromValue(data)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         runCustomDumper(data, true); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isChildrenNeeded() && data.variable.isEmpty()) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         createGdbVariable(data); | 
					
						
							|  |  |  |         // the WatchVarCreate handler will re-insert a WatchData
 | 
					
						
							|  |  |  |         // item, with childrenNeeded() set.
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isChildrenNeeded()) { | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; | 
					
						
							|  |  |  |         sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isChildCountNeeded() && data.variable.isEmpty()) { | 
					
						
							|  |  |  |         #if DEBUG_SUBITEM
 | 
					
						
							|  |  |  |         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT"; | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         createGdbVariable(data); | 
					
						
							|  |  |  |         // the WatchVarCreate handler will re-insert a WatchData
 | 
					
						
							|  |  |  |         // item, with childrenNeeded() set.
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.isChildCountNeeded()) { | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; | 
					
						
							|  |  |  |         sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString(); | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(false, return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::updateWatchModel() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_pendingRequests = 0; | 
					
						
							|  |  |  |     PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL"); | 
					
						
							|  |  |  |     updateWatchModel2(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::updateWatchModel2() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PENDING_DEBUG("UPDATE WATCH MODEL"); | 
					
						
							|  |  |  |     QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes(); | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     //QTC_ASSERT(incomplete.isEmpty(), /**/);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (!incomplete.isEmpty()) { | 
					
						
							|  |  |  |         #if DEBUG_PENDING
 | 
					
						
							|  |  |  |         qDebug() << "##############################################"; | 
					
						
							|  |  |  |         qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:"; | 
					
						
							|  |  |  |         foreach (const WatchData &data, incomplete) | 
					
						
							|  |  |  |             qDebug() << data.toString(); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Bump requests to avoid model rebuilding during the nested
 | 
					
						
							|  |  |  |         // updateWatchModel runs.
 | 
					
						
							|  |  |  |         ++m_pendingRequests; | 
					
						
							|  |  |  |         foreach (const WatchData &data, incomplete) | 
					
						
							|  |  |  |             updateSubItem(data); | 
					
						
							|  |  |  |         PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL"); | 
					
						
							|  |  |  |         updateWatchModel2(); | 
					
						
							|  |  |  |         --m_pendingRequests; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (m_pendingRequests > 0) { | 
					
						
							|  |  |  |         PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |     PENDING_DEBUG("REBUILDING MODEL"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     emit gdbInputAvailable(QString(), | 
					
						
							|  |  |  |         "[" + currentTime() + "]    <Rebuild Watchmodel>"); | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |     q->showStatusMessage(tr("Finished retrieving data."), 400); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     qq->watchHandler()->rebuildModel(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!m_toolTipExpression.isEmpty()) { | 
					
						
							|  |  |  |         WatchData *data = qq->watchHandler()->findData(tooltipIName); | 
					
						
							|  |  |  |         if (data) { | 
					
						
							|  |  |  |             //m_toolTipCache[data->exp] = *data;
 | 
					
						
							|  |  |  |             QToolTip::showText(m_toolTipPos, | 
					
						
							|  |  |  |                     "(" + data->type + ") " + data->exp + " = " + data->value); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             QToolTip::showText(m_toolTipPos, | 
					
						
							|  |  |  |                 "Cannot evaluate expression: " + m_toolTipExpression); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Q_UNUSED(record); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |     //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
 | 
					
						
							|  |  |  |     GdbMi output = record.data.findChild("consolestreamoutput"); | 
					
						
							|  |  |  |     QByteArray out = output.data(); | 
					
						
							| 
									
										
										
										
											2009-01-21 11:57:14 +01:00
										 |  |  |     out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
 | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |     out = out.left(out.lastIndexOf('"')); | 
					
						
							| 
									
										
										
										
											2009-01-27 17:41:31 +01:00
										 |  |  |     //out.replace('\'', '"');
 | 
					
						
							|  |  |  |     out.replace("\\", ""); | 
					
						
							| 
									
										
										
										
											2009-01-21 12:16:27 +01:00
										 |  |  |     out = "dummy={" + out + "}"; | 
					
						
							| 
									
										
										
										
											2009-01-21 11:57:14 +01:00
										 |  |  |     //qDebug() << "OUTPUT: " << out;
 | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     GdbMi contents; | 
					
						
							|  |  |  |     contents.fromString(out); | 
					
						
							|  |  |  |     GdbMi simple = contents.findChild("dumpers"); | 
					
						
							|  |  |  |     m_namespace = contents.findChild("namespace").data(); | 
					
						
							|  |  |  |     GdbMi qtversion = contents.findChild("qtversion"); | 
					
						
							|  |  |  |     if (qtversion.children().size() == 3) { | 
					
						
							|  |  |  |         m_qtVersion = (qtversion.childAt(0).data().toInt() << 16) | 
					
						
							|  |  |  |                     + (qtversion.childAt(1).data().toInt() << 8) | 
					
						
							|  |  |  |                     + qtversion.childAt(2).data().toInt(); | 
					
						
							|  |  |  |         //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
 | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         m_qtVersion = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     | 
					
						
							| 
									
										
										
										
											2009-01-21 11:57:14 +01:00
										 |  |  |     //qDebug() << "CONTENTS: " << contents.toString();
 | 
					
						
							|  |  |  |     //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
 | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |     m_availableSimpleDumpers.clear(); | 
					
						
							|  |  |  |     foreach (const GdbMi &item, simple.children()) | 
					
						
							|  |  |  |         m_availableSimpleDumpers.append(item.data()); | 
					
						
							|  |  |  |     if (m_availableSimpleDumpers.isEmpty()) { | 
					
						
							|  |  |  |         m_dataDumperState = DataDumperUnavailable; | 
					
						
							|  |  |  |         QMessageBox::warning(q->mainWindow(), | 
					
						
							|  |  |  |             tr("Cannot find special data dumpers"), | 
					
						
							|  |  |  |             tr("The debugged binary does not contain information needed for " | 
					
						
							|  |  |  |                     "nice display of Qt data types.\n\n" | 
					
						
							| 
									
										
										
										
											2009-02-06 11:20:32 +01:00
										 |  |  |                     "You might want to try including the file\n\n" | 
					
						
							| 
									
										
										
										
											2009-02-17 17:34:41 +01:00
										 |  |  |                     ".../share/qtcreator/gdbmacros/gdbmacros.cpp\n\n" | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |                     "into your project directly.") | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         m_dataDumperState = DataDumperAvailable; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-01-21 11:57:14 +01:00
										 |  |  |     //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
 | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | void GdbEngine::sendWatchParameters(const QByteArray ¶ms0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QByteArray params = params0; | 
					
						
							|  |  |  |     params.append('\0'); | 
					
						
							|  |  |  |     char buf[50]; | 
					
						
							|  |  |  |     sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size()); | 
					
						
							|  |  |  |     QByteArray encoded; | 
					
						
							|  |  |  |     encoded.append(buf); | 
					
						
							|  |  |  |     for (int i = 0; i != params.size(); ++i) { | 
					
						
							|  |  |  |         sprintf(buf, "%d,", int(params[i])); | 
					
						
							|  |  |  |         encoded.append(buf); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     encoded[encoded.size() - 1] = '}'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sendCommand(encoded); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleVarAssign() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // everything might have changed, force re-evaluation
 | 
					
						
							|  |  |  |     // FIXME: Speed this up by re-using variables and only
 | 
					
						
							|  |  |  |     // marking values as 'unknown'
 | 
					
						
							|  |  |  |     updateLocals(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (mi.isValid()) { | 
					
						
							|  |  |  |         if (!data.framekey.isEmpty()) | 
					
						
							|  |  |  |             m_varToType[data.framekey] = mi.data(); | 
					
						
							|  |  |  |         data.setType(mi.data()); | 
					
						
							|  |  |  |     } else if (data.type.isEmpty()) { | 
					
						
							|  |  |  |         data.setTypeNeeded(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleVarCreate(const GdbResultRecord &record, | 
					
						
							|  |  |  |     const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							|  |  |  |     // happens e.g. when we already issued a var-evaluate command
 | 
					
						
							|  |  |  |     if (!data.isValid()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
 | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         data.variable = data.iname; | 
					
						
							|  |  |  |         setWatchDataType(data, record.data.findChild("type")); | 
					
						
							|  |  |  |         if (isCustomValueDumperAvailable(data.type)) { | 
					
						
							|  |  |  |             // we do not trust gdb if we have a custom dumper
 | 
					
						
							|  |  |  |             if (record.data.findChild("children").isValid()) | 
					
						
							|  |  |  |                 data.setChildrenUnneeded(); | 
					
						
							|  |  |  |             else if (qq->watchHandler()->isExpandedIName(data.iname)) | 
					
						
							|  |  |  |                 data.setChildrenNeeded(); | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             if (record.data.findChild("children").isValid()) | 
					
						
							|  |  |  |                 data.setChildrenUnneeded(); | 
					
						
							|  |  |  |             else if (qq->watchHandler()->isExpandedIName(data.iname)) | 
					
						
							|  |  |  |                 data.setChildrenNeeded(); | 
					
						
							|  |  |  |             setWatchDataChildCount(data, record.data.findChild("numchild")); | 
					
						
							|  |  |  |             //if (data.isValueNeeded() && data.childCount > 0)
 | 
					
						
							|  |  |  |             //    data.setValue(QByteArray());
 | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         data.setError(record.data.findChild("msg").data()); | 
					
						
							|  |  |  |         if (data.isWatcher()) { | 
					
						
							|  |  |  |             data.value = strNotInScope; | 
					
						
							|  |  |  |             data.type = " "; | 
					
						
							|  |  |  |             data.setAllUnneeded(); | 
					
						
							|  |  |  |             data.setChildCount(0); | 
					
						
							|  |  |  |             data.valuedisabled = true; | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record, | 
					
						
							|  |  |  |     const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(data.isValid(), qDebug() << "HUH?"); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         //if (col == 0)
 | 
					
						
							|  |  |  |         //    data.name = record.data.findChild("value").data();
 | 
					
						
							|  |  |  |         //else
 | 
					
						
							|  |  |  |             setWatchDataValue(data, record.data.findChild("value")); | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         data.setError(record.data.findChild("msg").data()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
 | 
					
						
							|  |  |  |     insertData(data); | 
					
						
							|  |  |  |     //updateWatchModel2();
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |     //qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         QString msg = record.data.findChild("msg").data(); | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |         //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
 | 
					
						
							|  |  |  |         q->showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record, | 
					
						
							|  |  |  |     const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(data.isValid(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         // ignore this case, data will follow
 | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         // Record an extra result, as the socket result will be lost
 | 
					
						
							|  |  |  |         // in transmission
 | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |         //--m_pendingRequests;
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         QString msg = record.data.findChild("msg").data(); | 
					
						
							|  |  |  |         //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
 | 
					
						
							|  |  |  | #ifdef QT_DEBUG
 | 
					
						
							| 
									
										
										
										
											2009-01-21 11:41:32 +01:00
										 |  |  |         // Make debugging of dumpers easier
 | 
					
						
							| 
									
										
										
										
											2009-01-14 18:09:02 +01:00
										 |  |  |         if (q->settings()->m_debugDumpers | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 && msg.startsWith("The program being debugged stopped while") | 
					
						
							|  |  |  |                 && msg.contains("qDumpObjectData440")) { | 
					
						
							|  |  |  |             // Fake full stop
 | 
					
						
							|  |  |  |             sendCommand("p 0", GdbAsyncOutput2);  // dummy
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |         //if (msg.startsWith("The program being debugged was sig"))
 | 
					
						
							|  |  |  |         //    msg = strNotInScope;
 | 
					
						
							|  |  |  |         //if (msg.startsWith("The program being debugged stopped while"))
 | 
					
						
							|  |  |  |         //    msg = strNotInScope;
 | 
					
						
							|  |  |  |         //data.setError(msg);
 | 
					
						
							|  |  |  |         //insertData(data);
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record, | 
					
						
							|  |  |  |     const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							| 
									
										
										
										
											2008-12-09 12:08:56 +01:00
										 |  |  |     QTC_ASSERT(data.isValid(), return); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
 | 
					
						
							|  |  |  |     //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
 | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |     if (record.resultClass != GdbResultDone) { | 
					
						
							|  |  |  |         qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |     GdbMi output = record.data.findChild("consolestreamoutput"); | 
					
						
							|  |  |  |     QByteArray out = output.data(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int markerPos = out.indexOf('"') + 1; // position of 'success marker'
 | 
					
						
							|  |  |  |     if (markerPos == -1 || out.at(markerPos) == 'f') {  // 't' or 'f'
 | 
					
						
							|  |  |  |         // custom dumper produced no output
 | 
					
						
							|  |  |  |         data.setError(strNotInScope); | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |         insertData(data); | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     out = out.mid(markerPos +  1); | 
					
						
							|  |  |  |     out = out.left(out.lastIndexOf('"')); | 
					
						
							|  |  |  |     out.replace("\\", ""); | 
					
						
							|  |  |  |     out = "dummy={" + out + "}"; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     GdbMi contents; | 
					
						
							|  |  |  |     contents.fromString(out); | 
					
						
							|  |  |  |     //qDebug() << "CONTENTS" << contents.toString(true);
 | 
					
						
							|  |  |  |     if (!contents.isValid()) { | 
					
						
							|  |  |  |         data.setError(strNotInScope); | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setWatchDataType(data, contents.findChild("type")); | 
					
						
							|  |  |  |     setWatchDataValue(data, contents.findChild("value"), | 
					
						
							|  |  |  |         contents.findChild("valueencoded").data().toInt()); | 
					
						
							|  |  |  |     setWatchDataAddress(data, contents.findChild("addr")); | 
					
						
							|  |  |  |     setWatchDataChildCount(data, contents.findChild("numchild")); | 
					
						
							|  |  |  |     setWatchDataValueToolTip(data, contents.findChild("valuetooltip")); | 
					
						
							|  |  |  |     setWatchDataValueDisabled(data, contents.findChild("valuedisabled")); | 
					
						
							|  |  |  |     setWatchDataEditValue(data, contents.findChild("editvalue")); | 
					
						
							|  |  |  |     if (qq->watchHandler()->isDisplayedIName(data.iname)) { | 
					
						
							|  |  |  |         GdbMi editvalue = contents.findChild("editvalue"); | 
					
						
							|  |  |  |         if (editvalue.isValid()) { | 
					
						
							|  |  |  |             setWatchDataEditValue(data, editvalue); | 
					
						
							|  |  |  |             qq->watchHandler()->showEditValue(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!qq->watchHandler()->isExpandedIName(data.iname)) | 
					
						
							|  |  |  |         data.setChildrenUnneeded(); | 
					
						
							|  |  |  |     GdbMi children = contents.findChild("children"); | 
					
						
							|  |  |  |     if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname)) | 
					
						
							|  |  |  |         data.setChildrenUnneeded(); | 
					
						
							|  |  |  |     data.setValueUnneeded(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // try not to repeat data too often
 | 
					
						
							|  |  |  |     WatchData childtemplate; | 
					
						
							|  |  |  |     setWatchDataType(childtemplate, contents.findChild("childtype")); | 
					
						
							|  |  |  |     setWatchDataChildCount(childtemplate, contents.findChild("childnumchild")); | 
					
						
							|  |  |  |     //qDebug() << "DATA: " << data.toString();
 | 
					
						
							|  |  |  |     insertData(data); | 
					
						
							|  |  |  |     foreach (GdbMi item, children.children()) { | 
					
						
							|  |  |  |         WatchData data1 = childtemplate; | 
					
						
							|  |  |  |         data1.name = item.findChild("name").data(); | 
					
						
							|  |  |  |         data1.iname = data.iname + "." + data1.name; | 
					
						
							| 
									
										
										
										
											2009-01-28 12:15:40 +01:00
										 |  |  |         if (!data1.name.isEmpty() && data1.name.at(0).isDigit()) | 
					
						
							|  |  |  |             data1.name = '[' + data1.name + ']'; | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |         QString key = item.findChild("key").data(); | 
					
						
							| 
									
										
										
										
											2009-01-28 15:06:05 +01:00
										 |  |  |         if (!key.isEmpty()) { | 
					
						
							| 
									
										
										
										
											2009-01-28 16:55:09 +01:00
										 |  |  |             if (item.findChild("keyencoded").data()[0] == '1') { | 
					
						
							| 
									
										
										
										
											2009-01-28 15:17:55 +01:00
										 |  |  |                 key = '"' + QByteArray::fromBase64(key.toUtf8()) + '"'; | 
					
						
							| 
									
										
										
										
											2009-01-28 16:55:09 +01:00
										 |  |  |                 if (key.size() > 13) { | 
					
						
							|  |  |  |                     key = key.left(12); | 
					
						
							|  |  |  |                     key += "..."; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             //data1.name += " (" + key + ")";
 | 
					
						
							|  |  |  |             data1.name = key; | 
					
						
							| 
									
										
										
										
											2009-01-28 15:06:05 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-01-28 10:57:23 +01:00
										 |  |  |         setWatchDataType(data1, item.findChild("type")); | 
					
						
							|  |  |  |         setWatchDataExpression(data1, item.findChild("exp")); | 
					
						
							|  |  |  |         setWatchDataChildCount(data1, item.findChild("numchild")); | 
					
						
							|  |  |  |         setWatchDataValue(data1, item.findChild("value"), | 
					
						
							|  |  |  |             item.findChild("valueencoded").data().toInt()); | 
					
						
							|  |  |  |         setWatchDataAddress(data1, item.findChild("addr")); | 
					
						
							|  |  |  |         setWatchDataValueToolTip(data1, item.findChild("valuetooltip")); | 
					
						
							|  |  |  |         setWatchDataValueDisabled(data1, item.findChild("valuedisabled")); | 
					
						
							|  |  |  |         if (!qq->watchHandler()->isExpandedIName(data1.iname)) | 
					
						
							|  |  |  |             data1.setChildrenUnneeded(); | 
					
						
							|  |  |  |         //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
 | 
					
						
							|  |  |  |         insertData(data1); | 
					
						
							| 
									
										
										
										
											2009-01-21 11:12:12 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | void GdbEngine::updateLocals() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     setTokenBarrier(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     m_pendingRequests = 0; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:38:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     PENDING_DEBUG("\nRESET PENDING"); | 
					
						
							|  |  |  |     m_toolTipCache.clear(); | 
					
						
							|  |  |  |     m_toolTipExpression.clear(); | 
					
						
							|  |  |  |     qq->watchHandler()->reinitializeWatchers(); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     int level = currentFrame(); | 
					
						
							|  |  |  |     // '2' is 'list with type and value'
 | 
					
						
							|  |  |  |     QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level); | 
					
						
							|  |  |  |     sendSynchronizedCommand(cmd, StackListArguments);                 // stage 1/2
 | 
					
						
							|  |  |  |     // '2' is 'list with type and value'
 | 
					
						
							|  |  |  |     sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:38:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-12 09:55:59 +01:00
										 |  |  |     //tryLoadCustomDumpers();
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleStackListArguments(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     // stage 1/2
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Linux:
 | 
					
						
							|  |  |  |     // 12^done,stack-args=
 | 
					
						
							|  |  |  |     //   [frame={level="0",args=[
 | 
					
						
							|  |  |  |     //     {name="argc",type="int",value="1"},
 | 
					
						
							|  |  |  |     //     {name="argv",type="char **",value="(char **) 0x7..."}]}]
 | 
					
						
							|  |  |  |     // Mac:
 | 
					
						
							|  |  |  |     // 78^done,stack-args=
 | 
					
						
							|  |  |  |     //    {frame={level="0",args={
 | 
					
						
							|  |  |  |     //      varobj=
 | 
					
						
							|  |  |  |     //        {exp="this",value="0x38a2fab0",name="var21",numchild="3",
 | 
					
						
							|  |  |  |     //             type="CurrentDocumentFind *  const",typecode="PTR",
 | 
					
						
							|  |  |  |     //             dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
 | 
					
						
							|  |  |  |     //             block_end_addr="0x3938eb2d"},
 | 
					
						
							|  |  |  |     //      varobj=
 | 
					
						
							|  |  |  |     //         {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
 | 
					
						
							|  |  |  |     //              name="var22",numchild="1",type="const QString  ...} }}}
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // In both cases, iterating over the children of stack-args/frame/args
 | 
					
						
							|  |  |  |     // is ok.
 | 
					
						
							|  |  |  |     m_currentFunctionArgs.clear(); | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         const GdbMi list = record.data.findChild("stack-args"); | 
					
						
							|  |  |  |         const GdbMi frame = list.findChild("frame"); | 
					
						
							|  |  |  |         const GdbMi args = frame.findChild("args"); | 
					
						
							|  |  |  |         m_currentFunctionArgs = args.children(); | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen"; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleStackListLocals(const GdbResultRecord &record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     // stage 2/2
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     // There could be shadowed variables
 | 
					
						
							|  |  |  |     QList<GdbMi> locals = record.data.findChild("locals").children(); | 
					
						
							|  |  |  |     locals += m_currentFunctionArgs; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     setLocals(locals); | 
					
						
							| 
									
										
										
										
											2008-12-03 16:51:56 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::setLocals(const QList<GdbMi> &locals)  | 
					
						
							|  |  |  | {  | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |     //qDebug() << m_varToType;
 | 
					
						
							|  |  |  |     QHash<QString, int> seen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     foreach (const GdbMi &item, locals) { | 
					
						
							|  |  |  |         // Local variables of inlined code are reported as 
 | 
					
						
							|  |  |  |         // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
 | 
					
						
							|  |  |  |         // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."
 | 
					
						
							|  |  |  |         // We do not want these at all. Current hypotheses is that those
 | 
					
						
							|  |  |  |         // "spurious" locals have _two_ "exp" field. Try to filter them:
 | 
					
						
							|  |  |  |         #ifdef Q_OS_MAC
 | 
					
						
							|  |  |  |         int numExps = 0; | 
					
						
							|  |  |  |         foreach (const GdbMi &child, item.children()) | 
					
						
							|  |  |  |             numExps += int(child.name() == "exp"); | 
					
						
							|  |  |  |         if (numExps > 1) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         QString name = item.findChild("exp").data(); | 
					
						
							|  |  |  |         #else
 | 
					
						
							|  |  |  |         QString name = item.findChild("name").data(); | 
					
						
							|  |  |  |         #endif
 | 
					
						
							|  |  |  |         int n = seen.value(name); | 
					
						
							|  |  |  |         if (n) { | 
					
						
							|  |  |  |             seen[name] = n + 1; | 
					
						
							|  |  |  |             WatchData data; | 
					
						
							|  |  |  |             data.iname = "local." + name + QString::number(n + 1); | 
					
						
							|  |  |  |             data.name = name + QString(" <shadowed %1>").arg(n); | 
					
						
							|  |  |  |             //data.setValue("<shadowed>");
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |             setWatchDataValue(data, item.findChild("value")); | 
					
						
							| 
									
										
										
										
											2009-02-11 14:17:25 +01:00
										 |  |  |             data.setType("<shadowed>"); | 
					
						
							|  |  |  |             data.setChildCount(0); | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             seen[name] = 1; | 
					
						
							|  |  |  |             WatchData data; | 
					
						
							|  |  |  |             data.iname = "local." + name; | 
					
						
							|  |  |  |             data.name = name; | 
					
						
							|  |  |  |             data.exp = name; | 
					
						
							|  |  |  |             data.framekey = m_currentFrame + data.name; | 
					
						
							|  |  |  |             setWatchDataType(data, item.findChild("type")); | 
					
						
							|  |  |  |             // set value only directly if it is simple enough, otherwise
 | 
					
						
							|  |  |  |             // pass through the insertData() machinery
 | 
					
						
							|  |  |  |             if (isIntOrFloatType(data.type) || isPointerType(data.type)) | 
					
						
							|  |  |  |                 setWatchDataValue(data, item.findChild("value")); | 
					
						
							|  |  |  |             if (!qq->watchHandler()->isExpandedIName(data.iname)) | 
					
						
							|  |  |  |                 data.setChildrenUnneeded(); | 
					
						
							|  |  |  |             if (isPointerType(data.type) || data.name == "this") | 
					
						
							|  |  |  |                 data.setChildCount(1); | 
					
						
							|  |  |  |             if (0 && m_varToType.contains(data.framekey)) { | 
					
						
							|  |  |  |                 qDebug() << "RE-USING " << m_varToType.value(data.framekey); | 
					
						
							|  |  |  |                 data.setType(m_varToType.value(data.framekey)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::insertData(const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //qDebug() << "INSERT DATA" << data0.toString();
 | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							|  |  |  |     if (data.value.startsWith("mi_cmd_var_create:")) { | 
					
						
							|  |  |  |         qDebug() << "BOGUS VALUE: " << data.toString(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qq->watchHandler()->insertData(data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleTypeContents(const QString &output) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // output.startsWith("type = ") == true
 | 
					
						
							|  |  |  |     // "type = int"
 | 
					
						
							|  |  |  |     // "type = class QString {"
 | 
					
						
							|  |  |  |     // "type = class QStringList : public QList<QString> {"
 | 
					
						
							|  |  |  |     QString tip; | 
					
						
							|  |  |  |     QString className; | 
					
						
							|  |  |  |     if (output.startsWith("type = class")) { | 
					
						
							|  |  |  |         int posBrace = output.indexOf('{'); | 
					
						
							|  |  |  |         QString head = output.mid(13, posBrace - 13 - 1); | 
					
						
							|  |  |  |         int posColon = head.indexOf(": public"); | 
					
						
							|  |  |  |         if (posColon == -1) | 
					
						
							|  |  |  |             posColon = head.indexOf(": protected"); | 
					
						
							|  |  |  |         if (posColon == -1) | 
					
						
							|  |  |  |             posColon = head.indexOf(": private"); | 
					
						
							|  |  |  |         if (posColon == -1) { | 
					
						
							|  |  |  |             className = head; | 
					
						
							|  |  |  |             tip = "class " + className + " { ... }"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             className = head.left(posColon - 1); | 
					
						
							|  |  |  |             tip = "class " + head + " { ... }"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         //qDebug() << "posColon: " << posColon;
 | 
					
						
							|  |  |  |         //qDebug() << "posBrace: " << posBrace;
 | 
					
						
							|  |  |  |         //qDebug() << "head: " << head;
 | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         className = output.mid(7); | 
					
						
							|  |  |  |         tip = className; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     //qDebug() << "output: " << output.left(100) + "...";
 | 
					
						
							|  |  |  |     //qDebug() << "className: " << className;
 | 
					
						
							|  |  |  |     //qDebug() << "tip: " << tip;
 | 
					
						
							|  |  |  |     //m_toolTip.type = className;
 | 
					
						
							|  |  |  |     m_toolTip.type.clear(); | 
					
						
							|  |  |  |     m_toolTip.value = tip; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleVarListChildrenHelper(const GdbMi &item, | 
					
						
							|  |  |  |     const WatchData &parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
 | 
					
						
							|  |  |  |     //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
 | 
					
						
							|  |  |  |     QByteArray exp = item.findChild("exp").data(); | 
					
						
							|  |  |  |     QByteArray name = item.findChild("name").data(); | 
					
						
							|  |  |  |     if (isAccessSpecifier(exp)) { | 
					
						
							|  |  |  |         // suppress 'private'/'protected'/'public' level
 | 
					
						
							|  |  |  |         WatchData data; | 
					
						
							|  |  |  |         data.variable = name; | 
					
						
							|  |  |  |         data.iname = parent.iname; | 
					
						
							|  |  |  |         //data.iname = data.variable;
 | 
					
						
							|  |  |  |         data.exp = parent.exp; | 
					
						
							|  |  |  |         data.setTypeUnneeded(); | 
					
						
							|  |  |  |         data.setValueUnneeded(); | 
					
						
							|  |  |  |         data.setChildCountUnneeded(); | 
					
						
							|  |  |  |         data.setChildrenUnneeded(); | 
					
						
							|  |  |  |         //qDebug() << "DATA" << data.toString();
 | 
					
						
							|  |  |  |         QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; | 
					
						
							|  |  |  |         //iname += '.' + exp;
 | 
					
						
							|  |  |  |         sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); | 
					
						
							|  |  |  |     } else if (item.findChild("numchild").data() == "0") { | 
					
						
							|  |  |  |         // happens for structs without data, e.g. interfaces.
 | 
					
						
							|  |  |  |         WatchData data; | 
					
						
							|  |  |  |         data.iname = parent.iname + '.' + exp; | 
					
						
							|  |  |  |         data.name = exp; | 
					
						
							|  |  |  |         data.variable = name; | 
					
						
							|  |  |  |         setWatchDataType(data, item.findChild("type")); | 
					
						
							|  |  |  |         setWatchDataValue(data, item.findChild("value")); | 
					
						
							|  |  |  |         setWatchDataAddress(data, item.findChild("addr")); | 
					
						
							|  |  |  |         data.setChildCount(0); | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  |     } else if (parent.iname.endsWith('.')) { | 
					
						
							|  |  |  |         // Happens with anonymous unions
 | 
					
						
							|  |  |  |         WatchData data; | 
					
						
							|  |  |  |         data.iname = name; | 
					
						
							|  |  |  |         QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; | 
					
						
							|  |  |  |         sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); | 
					
						
							|  |  |  |     } else if (exp == "staticMetaObject") { | 
					
						
							|  |  |  |         //    && item.findChild("type").data() == "const QMetaObject")
 | 
					
						
							|  |  |  |         // FIXME: Namespaces?
 | 
					
						
							|  |  |  |         // { do nothing }    FIXME: make coinfigurable?
 | 
					
						
							|  |  |  |         // special "clever" hack to avoid clutter in the GUI.
 | 
					
						
							|  |  |  |         // I am not sure this is a good idea...
 | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         WatchData data; | 
					
						
							|  |  |  |         data.iname = parent.iname + '.' + exp; | 
					
						
							|  |  |  |         data.variable = name; | 
					
						
							|  |  |  |         setWatchDataType(data, item.findChild("type")); | 
					
						
							|  |  |  |         setWatchDataValue(data, item.findChild("value")); | 
					
						
							|  |  |  |         setWatchDataAddress(data, item.findChild("addr")); | 
					
						
							|  |  |  |         setWatchDataChildCount(data, item.findChild("numchild")); | 
					
						
							|  |  |  |         if (!qq->watchHandler()->isExpandedIName(data.iname)) | 
					
						
							|  |  |  |             data.setChildrenUnneeded(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         data.name = exp; | 
					
						
							|  |  |  |         if (isPointerType(parent.type) && data.type == exp) { | 
					
						
							|  |  |  |             data.exp = "*(" + parent.exp + ")"; | 
					
						
							|  |  |  |             data.name = "*" + parent.name; | 
					
						
							|  |  |  |         } else if (data.type == exp) { | 
					
						
							|  |  |  |             // A type we derive from? gdb crashes when creating variables here
 | 
					
						
							|  |  |  |             data.exp = parent.exp; | 
					
						
							|  |  |  |         } else if (exp.startsWith("*")) { | 
					
						
							|  |  |  |             // A pointer
 | 
					
						
							|  |  |  |             data.exp = "*(" + parent.exp + ")"; | 
					
						
							|  |  |  |         } else if (startsWithDigit(exp)) { | 
					
						
							|  |  |  |             // An array. No variables needed?
 | 
					
						
							|  |  |  |             data.name = "[" + data.name + "]"; | 
					
						
							|  |  |  |             data.exp = parent.exp + "[" + exp + "]"; | 
					
						
							|  |  |  |         } else if (0 && parent.name.endsWith('.')) { | 
					
						
							|  |  |  |             // Happens with anonymous unions
 | 
					
						
							|  |  |  |             data.exp = parent.exp + exp; | 
					
						
							|  |  |  |             //data.name = "<anonymous union>";
 | 
					
						
							|  |  |  |         } else if (exp.isEmpty()) { | 
					
						
							|  |  |  |             // Happens with anonymous unions
 | 
					
						
							|  |  |  |             data.exp = parent.exp; | 
					
						
							|  |  |  |             data.name = "<n/a>"; | 
					
						
							|  |  |  |             data.iname = parent.iname + ".@"; | 
					
						
							|  |  |  |             data.type = "<anonymous union>"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             // A structure. Hope there's nothing else...
 | 
					
						
							|  |  |  |             data.exp = parent.exp + '.' + exp; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isCustomValueDumperAvailable(data.type)) { | 
					
						
							|  |  |  |             // we do not trust gdb if we have a custom dumper
 | 
					
						
							|  |  |  |             data.setValueNeeded(); | 
					
						
							|  |  |  |             data.setChildCountNeeded(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
 | 
					
						
							|  |  |  |         //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
 | 
					
						
							|  |  |  |         insertData(data); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleVarListChildren(const GdbResultRecord &record, | 
					
						
							|  |  |  |     const WatchData &data0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //WatchResultCounter dummy(this, WatchVarListChildren);
 | 
					
						
							|  |  |  |     WatchData data = data0; | 
					
						
							|  |  |  |     if (!data.isValid()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT " << data.toString();
 | 
					
						
							|  |  |  |         GdbMi children = record.data.findChild("children"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         foreach (const GdbMi &child, children.children()) | 
					
						
							|  |  |  |             handleVarListChildrenHelper(child, data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!isAccessSpecifier(data.variable.split('.').takeLast())) { | 
					
						
							|  |  |  |             data.setChildrenUnneeded(); | 
					
						
							|  |  |  |             insertData(data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         data.setError(record.data.findChild("msg").data()); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         data.setError("Unknown error: " + record.toString()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::handleToolTip(const GdbResultRecord &record, | 
					
						
							|  |  |  |         const QString &what) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
 | 
					
						
							|  |  |  |     //    << "record: " << record.toString();
 | 
					
						
							|  |  |  |     if (record.resultClass == GdbResultError) { | 
					
						
							|  |  |  |         QString msg = record.data.findChild("msg").data(); | 
					
						
							|  |  |  |         if (what == "create") { | 
					
						
							|  |  |  |             sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (what == "evaluate") { | 
					
						
							|  |  |  |             if (msg.startsWith("Cannot look up value of a typedef")) { | 
					
						
							|  |  |  |                 m_toolTip.value = m_toolTip.exp + " is a typedef."; | 
					
						
							|  |  |  |                 //return;
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else if (record.resultClass == GdbResultDone) { | 
					
						
							|  |  |  |         if (what == "create") { | 
					
						
							|  |  |  |             setWatchDataType(m_toolTip, record.data.findChild("type")); | 
					
						
							|  |  |  |             setWatchDataChildCount(m_toolTip, record.data.findChild("numchild")); | 
					
						
							|  |  |  |             if (isCustomValueDumperAvailable(m_toolTip.type)) | 
					
						
							|  |  |  |                 runCustomDumper(m_toolTip, false); | 
					
						
							|  |  |  |             else | 
					
						
							| 
									
										
										
										
											2008-12-08 14:26:11 +01:00
										 |  |  |                 q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |                 sendCommand("-data-evaluate-expression " + m_toolTip.exp, | 
					
						
							|  |  |  |                     WatchToolTip, "evaluate"); | 
					
						
							|  |  |  |                 //sendToolTipCommand("-var-evaluate-expression tooltip")
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (what == "evaluate") { | 
					
						
							|  |  |  |             m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp | 
					
						
							|  |  |  |                    + " = " + record.data.findChild("value").data(); | 
					
						
							|  |  |  |             //return;
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (what == "ptype") { | 
					
						
							|  |  |  |             GdbMi mi = record.data.findChild("consolestreamoutput"); | 
					
						
							|  |  |  |             m_toolTip.value = extractTypeFromPTypeOutput(mi.data()); | 
					
						
							|  |  |  |             //return;
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_toolTip.iname = tooltipIName; | 
					
						
							|  |  |  |     m_toolTip.setChildrenUnneeded(); | 
					
						
							|  |  |  |     m_toolTip.setChildCountUnneeded(); | 
					
						
							|  |  |  |     insertData(m_toolTip); | 
					
						
							|  |  |  |     qDebug() << "DATA INSERTED"; | 
					
						
							|  |  |  |     QTimer::singleShot(0, this, SLOT(updateWatchModel2())); | 
					
						
							|  |  |  |     qDebug() << "HANDLE TOOLTIP END"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | void GdbEngine::handleChangedItem(QStandardItem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // HACK: Just store the item for the slot
 | 
					
						
							|  |  |  |     //  handleChangedItem(QWidget *widget) below.
 | 
					
						
							|  |  |  |     QModelIndex index = item->index().sibling(item->index().row(), 0); | 
					
						
							|  |  |  |     //WatchData data = m_currentSet.takeData(iname);
 | 
					
						
							|  |  |  |     //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
 | 
					
						
							|  |  |  |     //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     sendCommand("-var-delete assign"); | 
					
						
							|  |  |  |     sendCommand("-var-create assign * " + expression); | 
					
						
							|  |  |  |     sendCommand("-var-assign assign " + value, WatchVarAssign); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void GdbEngine::tryLoadCustomDumpers() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_dataDumperState != DataDumperUninitialized) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS"); | 
					
						
							| 
									
										
										
										
											2009-02-13 14:45:46 +01:00
										 |  |  |     m_dataDumperState = DataDumperUnavailable;  | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #if defined(Q_OS_LINUX)
 | 
					
						
							|  |  |  |     QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so"; | 
					
						
							| 
									
										
										
										
											2009-02-13 14:45:46 +01:00
										 |  |  |     if (QFileInfo(lib).exists()) { | 
					
						
							|  |  |  |         m_dataDumperState = DataDumperLoadTried; | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         //sendCommand("p dlopen");
 | 
					
						
							|  |  |  |         QString flag = QString::number(RTLD_NOW); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("sharedlibrary libc"); // for malloc
 | 
					
						
							|  |  |  |         sendCommand("sharedlibrary libdl"); // for dlopen
 | 
					
						
							|  |  |  |         sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")", | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |             WatchDumpCustomSetup); | 
					
						
							| 
									
										
										
										
											2009-01-05 12:54:52 +01:00
										 |  |  |         // some older systems like CentOS 4.6 prefer this:
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("call (void)__dlopen(\"" + lib + "\", " + flag + ")", | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |             WatchDumpCustomSetup); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("sharedlibrary " + dotEscape(lib)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #if defined(Q_OS_MAC)
 | 
					
						
							|  |  |  |     QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib"; | 
					
						
							| 
									
										
										
										
											2009-02-13 14:45:46 +01:00
										 |  |  |     if (QFileInfo(lib).exists()) { | 
					
						
							|  |  |  |         m_dataDumperState = DataDumperLoadTried; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("sharedlibrary libc"); // for malloc
 | 
					
						
							|  |  |  |         sendCommand("sharedlibrary libdl"); // for dlopen
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         QString flag = QString::number(RTLD_NOW); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")", | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |             WatchDumpCustomSetup); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("sharedlibrary " + dotEscape(lib)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #if defined(Q_OS_WIN)
 | 
					
						
							|  |  |  |     QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll"; | 
					
						
							|  |  |  |     if (QFileInfo(lib).exists()) { | 
					
						
							| 
									
										
										
										
											2009-02-13 14:45:46 +01:00
										 |  |  |         m_dataDumperState = DataDumperLoadTried; | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("sharedlibrary .*"); // for LoadLibraryA
 | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |         //sendCommand("handle SIGSEGV pass stop print");
 | 
					
						
							|  |  |  |         //sendCommand("set unwindonsignal off");
 | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("call LoadLibraryA(\"" + lib + "\")", | 
					
						
							| 
									
										
										
										
											2009-02-11 14:39:31 +01:00
										 |  |  |             WatchDumpCustomSetup); | 
					
						
							| 
									
										
										
										
											2009-02-17 17:40:13 +01:00
										 |  |  |         sendCommand("sharedlibrary " + dotEscape(lib)); | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-13 14:45:46 +01:00
										 |  |  |     if (m_dataDumperState == DataDumperLoadTried) { | 
					
						
							|  |  |  |         // retreive list of dumpable classes
 | 
					
						
							|  |  |  |         sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)", | 
					
						
							|  |  |  |             GdbQueryDataDumper1); | 
					
						
							|  |  |  |         sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper2); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         gdbOutputAvailable("", QString("DEBUG HELPER LIBRARY IS NOT USABLE: " | 
					
						
							|  |  |  |             " %1  EXISTS: %2, EXECUTABLE: %3").arg(lib) | 
					
						
							|  |  |  |             .arg(QFileInfo(lib).exists()) | 
					
						
							|  |  |  |             .arg(QFileInfo(lib).isExecutable())); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-02 12:01:29 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | IDebuggerEngine *createGdbEngine(DebuggerManager *parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return new GdbEngine(parent); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 |