PageRenderTime 36ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/src/plugins/debugger/cdb/cdbengine.cpp

https://bitbucket.org/kpozn/qt-creator-py-reborn
C++ | 3133 lines | 2646 code | 212 blank | 275 comment | 476 complexity | cd301d975e19f4cd921a1aa3b104caab MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**************************************************************************
  2. **
  3. ** This file is part of Qt Creator
  4. **
  5. ** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
  6. **
  7. ** Contact: Nokia Corporation (qt-info@nokia.com)
  8. **
  9. **
  10. ** GNU Lesser General Public License Usage
  11. **
  12. ** This file may be used under the terms of the GNU Lesser General Public
  13. ** License version 2.1 as published by the Free Software Foundation and
  14. ** appearing in the file LICENSE.LGPL included in the packaging of this file.
  15. ** Please review the following information to ensure the GNU Lesser General
  16. ** Public License version 2.1 requirements will be met:
  17. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  18. **
  19. ** In addition, as a special exception, Nokia gives you certain additional
  20. ** rights. These rights are described in the Nokia Qt LGPL Exception
  21. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  22. **
  23. ** Other Usage
  24. **
  25. ** Alternatively, this file may be used in accordance with the terms and
  26. ** conditions contained in a signed written agreement between you and Nokia.
  27. **
  28. ** If you have questions regarding the use of this file, please contact
  29. ** Nokia at qt-info@nokia.com.
  30. **
  31. **************************************************************************/
  32. #include "cdbengine.h"
  33. #include "breakhandler.h"
  34. #include "breakpoint.h"
  35. #include "bytearrayinputstream.h"
  36. #include "cdboptions.h"
  37. #include "cdboptionspage.h"
  38. #include "cdbparsehelpers.h"
  39. #include "debuggeractions.h"
  40. #include "debuggercore.h"
  41. #include "debuggerinternalconstants.h"
  42. #include "debuggerrunner.h"
  43. #include "debuggerstartparameters.h"
  44. #include "debuggertooltipmanager.h"
  45. #include "disassembleragent.h"
  46. #include "disassemblerlines.h"
  47. #include "memoryagent.h"
  48. #include "moduleshandler.h"
  49. #include "registerhandler.h"
  50. #include "stackframe.h"
  51. #include "stackhandler.h"
  52. #include "threadshandler.h"
  53. #include "watchhandler.h"
  54. #include "watchutils.h"
  55. #include "gdb/gdbmi.h"
  56. #include "shared/cdbsymbolpathlisteditor.h"
  57. #include "shared/hostutils.h"
  58. #include "procinterrupt.h"
  59. #include <TranslationUnit.h>
  60. #include <coreplugin/icore.h>
  61. #include <texteditor/itexteditor.h>
  62. #include <projectexplorer/abi.h>
  63. #include <projectexplorer/projectexplorerconstants.h>
  64. #include <utils/synchronousprocess.h>
  65. #include <utils/winutils.h>
  66. #include <utils/qtcassert.h>
  67. #include <utils/savedaction.h>
  68. #include <utils/consoleprocess.h>
  69. #include <utils/fileutils.h>
  70. #include <cplusplus/findcdbbreakpoint.h>
  71. #include <cplusplus/CppDocument.h>
  72. #include <cpptools/ModelManagerInterface.h>
  73. #include <QCoreApplication>
  74. #include <QFileInfo>
  75. #include <QDir>
  76. #include <QDebug>
  77. #include <QTextStream>
  78. #include <QDateTime>
  79. #include <QToolTip>
  80. #include <QMainWindow>
  81. #include <QMessageBox>
  82. #include <cctype>
  83. Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
  84. Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
  85. enum { debug = 0 };
  86. enum { debugLocals = 0 };
  87. enum { debugSourceMapping = 0 };
  88. enum { debugWatches = 0 };
  89. enum { debugBreakpoints = 0 };
  90. enum HandleLocalsFlags
  91. {
  92. PartialLocalsUpdate = 0x1,
  93. LocalsUpdateForNewFrame = 0x2
  94. };
  95. #if 0
  96. # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
  97. #else
  98. # define STATE_DEBUG(state, func, line, notifyFunc)
  99. #endif
  100. /*!
  101. \class Debugger::Internal::CdbEngine
  102. Cdb engine version 2: Run the CDB process on pipes and parse its output.
  103. The engine relies on a CDB extension Qt Creator provides as an extension
  104. library (32/64bit), which is loaded into cdb.exe. It serves to:
  105. \list
  106. \o Notify the engine about the state of the debugging session:
  107. \list
  108. \o idle: (hooked up with .idle_cmd) debuggee stopped
  109. \o accessible: Debuggee stopped, cdb.exe accepts commands
  110. \o inaccessible: Debuggee runs, no way to post commands
  111. \o session active/inactive: Lost debuggee, terminating.
  112. \endlist
  113. \o Hook up with output/event callbacks and produce formatted output to be able
  114. to catch application output and exceptions.
  115. \o Provide some extension commands that produce output in a standardized (GDBMI)
  116. format that ends up in handleExtensionMessage(), for example:
  117. \list
  118. \o pid Return debuggee pid for interrupting.
  119. \o locals Print locals from SymbolGroup
  120. \o expandLocals Expand locals in symbol group
  121. \o registers, modules, threads
  122. \endlist
  123. \endlist
  124. Debugger commands can be posted by calling:
  125. \list
  126. \o postCommand(): Does not expect a reply
  127. \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
  128. that is captured by enclosing it in special tokens using the 'echo' command and
  129. then invokes a callback with a CdbBuiltinCommand structure.
  130. \o postExtensionCommand(): Run a command provided by the extension producing
  131. one-line output and invoke a callback with a CdbExtensionCommand structure
  132. (output is potentially split up in chunks).
  133. \endlist
  134. Startup sequence:
  135. [Console: The console stub launches the process. On process startup,
  136. launchCDB() is called with AttachExternal].
  137. setupEngine() calls launchCDB() with the startparameters. The debuggee
  138. runs into the initial breakpoint (session idle). EngineSetupOk is
  139. notified (inferior still stopped). setupInferior() is then called
  140. which does breakpoint synchronization and issues the extension 'pid'
  141. command to obtain the inferior pid (which also hooks up the output callbacks).
  142. handlePid() notifies notifyInferiorSetupOk.
  143. runEngine() is then called which issues 'g' to continue the inferior.
  144. Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
  145. when the inferior exits (except attach modes).
  146. */
  147. using namespace ProjectExplorer;
  148. namespace Debugger {
  149. namespace Internal {
  150. static const char localsPrefixC[] = "local.";
  151. struct MemoryViewCookie
  152. {
  153. explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
  154. quint64 addr = 0, quint64 l = 0) :
  155. agent(a), editorToken(e), address(addr), length(l)
  156. {}
  157. MemoryAgent *agent;
  158. QObject *editorToken;
  159. quint64 address;
  160. quint64 length;
  161. };
  162. struct MemoryChangeCookie
  163. {
  164. explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
  165. address(addr), data(d) {}
  166. quint64 address;
  167. QByteArray data;
  168. };
  169. struct ConditionalBreakPointCookie
  170. {
  171. ConditionalBreakPointCookie(BreakpointModelId i = BreakpointModelId()) : id(i) {}
  172. BreakpointModelId id;
  173. GdbMi stopReason;
  174. };
  175. } // namespace Internal
  176. } // namespace Debugger
  177. Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
  178. Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
  179. Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
  180. namespace Debugger {
  181. namespace Internal {
  182. static inline bool isCreatorConsole(const DebuggerStartParameters &sp, const CdbOptions &o)
  183. {
  184. return !o.cdbConsole && sp.useTerminal
  185. && (sp.startMode == StartInternal || sp.startMode == StartExternal);
  186. }
  187. static QMessageBox *
  188. nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
  189. {
  190. QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
  191. debuggerCore()->mainWindow());
  192. mb->setAttribute(Qt::WA_DeleteOnClose);
  193. mb->show();
  194. return mb;
  195. }
  196. // Base data structure for command queue entries with callback
  197. struct CdbCommandBase
  198. {
  199. typedef CdbEngine::BuiltinCommandHandler CommandHandler;
  200. CdbCommandBase();
  201. CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
  202. unsigned nc, const QVariant &cookie);
  203. int token;
  204. unsigned flags;
  205. QByteArray command;
  206. QVariant cookie;
  207. // Continue with another commands as specified in CommandSequenceFlags
  208. unsigned commandSequence;
  209. };
  210. CdbCommandBase::CdbCommandBase() :
  211. token(0), flags(0), commandSequence(0)
  212. {
  213. }
  214. CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
  215. unsigned nc, const QVariant &c) :
  216. token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
  217. {
  218. }
  219. // Queue entry for builtin commands producing free-format
  220. // line-by-line output.
  221. struct CdbBuiltinCommand : public CdbCommandBase
  222. {
  223. typedef CdbEngine::BuiltinCommandHandler CommandHandler;
  224. CdbBuiltinCommand() {}
  225. CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
  226. CommandHandler h,
  227. unsigned nc, const QVariant &cookie) :
  228. CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
  229. {}
  230. QByteArray joinedReply() const;
  231. CommandHandler handler;
  232. QList<QByteArray> reply;
  233. };
  234. QByteArray CdbBuiltinCommand::joinedReply() const
  235. {
  236. if (reply.isEmpty())
  237. return QByteArray();
  238. QByteArray answer;
  239. answer.reserve(120 * reply.size());
  240. foreach (const QByteArray &l, reply) {
  241. answer += l;
  242. answer += '\n';
  243. }
  244. return answer;
  245. }
  246. // Queue entry for Qt Creator extension commands producing one-line
  247. // output with success flag and error message.
  248. struct CdbExtensionCommand : public CdbCommandBase
  249. {
  250. typedef CdbEngine::ExtensionCommandHandler CommandHandler;
  251. CdbExtensionCommand() : success(false) {}
  252. CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
  253. CommandHandler h,
  254. unsigned nc, const QVariant &cookie) :
  255. CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
  256. CommandHandler handler;
  257. QByteArray reply;
  258. QByteArray errorMessage;
  259. bool success;
  260. };
  261. template <class CommandPtrType>
  262. int indexOfCommand(const QList<CommandPtrType> &l, int token)
  263. {
  264. const int count = l.size();
  265. for (int i = 0; i < count; i++)
  266. if (l.at(i)->token == token)
  267. return i;
  268. return -1;
  269. }
  270. static inline bool validMode(DebuggerStartMode sm)
  271. {
  272. switch (sm) {
  273. case NoStartMode:
  274. case StartRemoteGdb:
  275. return false;
  276. default:
  277. break;
  278. }
  279. return true;
  280. }
  281. // Accessed by RunControlFactory
  282. DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
  283. DebuggerEngine *masterEngine, QString *errorMessage)
  284. {
  285. #ifdef Q_OS_WIN
  286. CdbOptionsPage *op = CdbOptionsPage::instance();
  287. if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
  288. *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
  289. return 0;
  290. }
  291. return new CdbEngine(sp, masterEngine, op->options());
  292. #else
  293. Q_UNUSED(masterEngine)
  294. Q_UNUSED(sp)
  295. #endif
  296. *errorMessage = QString::fromLatin1("Unsupported debug mode");
  297. return 0;
  298. }
  299. bool isCdbEngineEnabled()
  300. {
  301. #ifdef Q_OS_WIN
  302. return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
  303. #else
  304. return false;
  305. #endif
  306. }
  307. static inline QString msgNoCdbBinaryForToolChain(const Abi &tc)
  308. {
  309. return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
  310. }
  311. static inline bool isMsvcFlavor(Abi::OSFlavor osf)
  312. {
  313. return osf == Abi::WindowsMsvc2005Flavor
  314. || osf == Abi::WindowsMsvc2008Flavor
  315. || osf == Abi::WindowsMsvc2010Flavor;
  316. }
  317. bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
  318. {
  319. #ifdef Q_OS_WIN
  320. const Abi abi = sp.toolChainAbi;
  321. if (!isCdbEngineEnabled()) {
  322. check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
  323. arg(abi.toString()));
  324. check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
  325. check->settingsPage = CdbOptionsPage::settingsId();
  326. return false;
  327. }
  328. if (!validMode(sp.startMode)) {
  329. check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
  330. return false;
  331. }
  332. if (abi.binaryFormat() != Abi::PEFormat || abi.os() != Abi::WindowsOS) {
  333. check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
  334. arg(abi.toString()));
  335. return false;
  336. }
  337. if (sp.startMode == AttachCore && !isMsvcFlavor(abi.osFlavor())) {
  338. check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine cannot debug gdb core files."));
  339. return false;
  340. }
  341. if (sp.debuggerCommand.isEmpty()) {
  342. check->errorDetails.push_back(msgNoCdbBinaryForToolChain(abi));
  343. check->settingsCategory = QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
  344. check->settingsPage = QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
  345. return false;
  346. }
  347. return true;
  348. #else
  349. Q_UNUSED(sp);
  350. check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
  351. return false;
  352. #endif
  353. }
  354. void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
  355. {
  356. #ifdef Q_OS_WIN
  357. opts->push_back(new CdbOptionsPage);
  358. #else
  359. Q_UNUSED(opts);
  360. #endif
  361. }
  362. #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
  363. static inline Utils::SavedAction *theAssemblerAction()
  364. {
  365. return debuggerCore()->action(OperateByInstruction);
  366. }
  367. CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
  368. DebuggerEngine *masterEngine, const OptionsPtr &options) :
  369. DebuggerEngine(sp, CppLanguage, masterEngine),
  370. m_creatorExtPrefix("<qtcreatorcdbext>|"),
  371. m_tokenPrefix("<token>"),
  372. m_options(options),
  373. m_effectiveStartMode(NoStartMode),
  374. m_accessible(false),
  375. m_specialStopMode(NoSpecialStop),
  376. m_nextCommandToken(0),
  377. m_currentBuiltinCommandIndex(-1),
  378. m_extensionCommandPrefixBA("!" QT_CREATOR_CDB_EXT "."),
  379. m_operateByInstructionPending(true),
  380. m_operateByInstruction(true), // Default CDB setting
  381. m_notifyEngineShutdownOnTermination(false),
  382. m_hasDebuggee(false),
  383. m_elapsedLogTime(0),
  384. m_sourceStepInto(false),
  385. m_watchPointX(0),
  386. m_watchPointY(0),
  387. m_ignoreCdbOutput(false)
  388. {
  389. connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
  390. setObjectName(QLatin1String("CdbEngine"));
  391. connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
  392. connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
  393. connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
  394. connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
  395. }
  396. void CdbEngine::init()
  397. {
  398. m_effectiveStartMode = NoStartMode;
  399. notifyInferiorPid(0);
  400. m_accessible = false;
  401. m_specialStopMode = NoSpecialStop;
  402. m_nextCommandToken = 0;
  403. m_currentBuiltinCommandIndex = -1;
  404. m_operateByInstructionPending = theAssemblerAction()->isChecked();
  405. m_operateByInstruction = true; // Default CDB setting
  406. m_notifyEngineShutdownOnTermination = false;
  407. m_hasDebuggee = false;
  408. m_sourceStepInto = false;
  409. m_watchPointX = m_watchPointY = 0;
  410. m_ignoreCdbOutput = false;
  411. m_outputBuffer.clear();
  412. m_builtinCommandQueue.clear();
  413. m_extensionCommandQueue.clear();
  414. m_extensionMessageBuffer.clear();
  415. m_pendingBreakpointMap.clear();
  416. m_customSpecialStopData.clear();
  417. m_symbolAddressCache.clear();
  418. m_coreStopReason.reset();
  419. // Create local list of mappings in native separators
  420. m_sourcePathMappings.clear();
  421. const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
  422. if (!globalOptions->sourcePathMap.isEmpty()) {
  423. typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
  424. m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
  425. const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
  426. for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
  427. m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
  428. QDir::toNativeSeparators(it.value())));
  429. }
  430. }
  431. QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process));
  432. }
  433. CdbEngine::~CdbEngine()
  434. {
  435. }
  436. void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
  437. {
  438. // To be set next time session becomes accessible
  439. m_operateByInstructionPending = operateByInstruction;
  440. if (state() == InferiorStopOk)
  441. syncOperateByInstruction(operateByInstruction);
  442. }
  443. void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
  444. {
  445. if (debug)
  446. qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
  447. if (m_operateByInstruction == operateByInstruction)
  448. return;
  449. QTC_ASSERT(m_accessible, return);
  450. m_operateByInstruction = operateByInstruction;
  451. postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
  452. postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
  453. }
  454. bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
  455. TextEditor::ITextEditor *editor,
  456. const DebuggerToolTipContext &contextIn)
  457. {
  458. if (debug)
  459. qDebug() << Q_FUNC_INFO;
  460. // Need a stopped debuggee and a cpp file in a valid frame
  461. if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
  462. return false;
  463. // Determine expression and function
  464. int line;
  465. int column;
  466. DebuggerToolTipContext context = contextIn;
  467. QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
  468. // Are we in the current stack frame
  469. if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
  470. return false;
  471. // No numerical or any other expressions [yet]
  472. if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
  473. return false;
  474. // Can this be found as a local variable?
  475. const QByteArray localsPrefix(localsPrefixC);
  476. QByteArray iname = localsPrefix + exp.toAscii();
  477. if (!watchHandler()->hasItem(iname)) {
  478. // Nope, try a 'local.this.m_foo'.
  479. exp.prepend(QLatin1String("this."));
  480. iname.insert(localsPrefix.size(), "this.");
  481. if (!watchHandler()->hasItem(iname))
  482. return false;
  483. }
  484. DebuggerToolTipWidget *tw = new DebuggerToolTipWidget;
  485. tw->setContext(context);
  486. tw->setDebuggerModel(LocalsType);
  487. tw->setExpression(exp);
  488. tw->acquireEngine(this);
  489. DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
  490. return true;
  491. }
  492. // Determine full path to the CDB extension library.
  493. QString CdbEngine::extensionLibraryName(bool is64Bit)
  494. {
  495. // Determine extension lib name and path to use
  496. QString rc;
  497. QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
  498. << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT "64" : QT_CREATOR_CDB_EXT "32")
  499. << '/' << QT_CREATOR_CDB_EXT << ".dll";
  500. return rc;
  501. }
  502. // Determine environment for CDB.exe, start out with run config and
  503. // add CDB extension path merged with system value should there be one.
  504. static QStringList mergeEnvironment(QStringList runConfigEnvironment,
  505. QString cdbExtensionPath)
  506. {
  507. // Determine CDB extension path from Qt Creator
  508. static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
  509. const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
  510. if (!oldCdbExtensionPath.isEmpty()) {
  511. cdbExtensionPath.append(QLatin1Char(';'));
  512. cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
  513. }
  514. // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
  515. // config, just to make sure, delete any existing entries
  516. const QString cdbExtensionPathVariableAssign =
  517. QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
  518. for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
  519. if (it->startsWith(cdbExtensionPathVariableAssign)) {
  520. it = runConfigEnvironment.erase(it);
  521. break;
  522. } else {
  523. ++it;
  524. }
  525. }
  526. runConfigEnvironment.append(cdbExtensionPathVariableAssign +
  527. QDir::toNativeSeparators(cdbExtensionPath));
  528. return runConfigEnvironment;
  529. }
  530. int CdbEngine::elapsedLogTime() const
  531. {
  532. const int elapsed = m_logTime.elapsed();
  533. const int delta = elapsed - m_elapsedLogTime;
  534. m_elapsedLogTime = elapsed;
  535. return delta;
  536. }
  537. // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
  538. bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
  539. {
  540. if (debug)
  541. qDebug("startConsole %s", qPrintable(sp.executable));
  542. m_consoleStub.reset(new Utils::ConsoleProcess);
  543. m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
  544. connect(m_consoleStub.data(), SIGNAL(processError(QString)),
  545. SLOT(consoleStubError(QString)));
  546. connect(m_consoleStub.data(), SIGNAL(processStarted()),
  547. SLOT(consoleStubProcessStarted()));
  548. connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
  549. SLOT(consoleStubExited()));
  550. m_consoleStub->setWorkingDirectory(sp.workingDirectory);
  551. if (sp.environment.size())
  552. m_consoleStub->setEnvironment(sp.environment);
  553. if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
  554. *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
  555. return false;
  556. }
  557. return true;
  558. }
  559. void CdbEngine::consoleStubError(const QString &msg)
  560. {
  561. if (debug)
  562. qDebug("consoleStubProcessMessage() in %s %s", stateName(state()), qPrintable(msg));
  563. if (state() == EngineSetupRequested) {
  564. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
  565. notifyEngineSetupFailed();
  566. } else {
  567. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
  568. notifyEngineIll();
  569. }
  570. nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
  571. }
  572. void CdbEngine::consoleStubProcessStarted()
  573. {
  574. if (debug)
  575. qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
  576. // Attach to console process.
  577. DebuggerStartParameters attachParameters = startParameters();
  578. attachParameters.executable.clear();
  579. attachParameters.processArgs.clear();
  580. attachParameters.attachPID = m_consoleStub->applicationPID();
  581. attachParameters.startMode = AttachExternal;
  582. attachParameters.useTerminal = false;
  583. showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
  584. QString errorMessage;
  585. if (!launchCDB(attachParameters, &errorMessage)) {
  586. showMessage(errorMessage, LogError);
  587. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
  588. notifyEngineSetupFailed();
  589. }
  590. }
  591. void CdbEngine::consoleStubExited()
  592. {
  593. }
  594. void CdbEngine::setupEngine()
  595. {
  596. if (debug)
  597. qDebug(">setupEngine");
  598. // Nag to add symbol server
  599. if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
  600. &(m_options->symbolPaths)))
  601. m_options->toSettings(Core::ICore::settings());
  602. init();
  603. if (!m_logTime.elapsed())
  604. m_logTime.start();
  605. QString errorMessage;
  606. // Console: Launch the stub with the suspended application and attach to it
  607. // CDB in theory has a command line option '-2' that launches a
  608. // console, too, but that immediately closes when the debuggee quits.
  609. // Use the Creator stub instead.
  610. const DebuggerStartParameters &sp = startParameters();
  611. const bool launchConsole = isCreatorConsole(sp, *m_options);
  612. m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
  613. const bool ok = launchConsole ?
  614. startConsole(startParameters(), &errorMessage) :
  615. launchCDB(startParameters(), &errorMessage);
  616. if (debug)
  617. qDebug("<setupEngine ok=%d", ok);
  618. if (!ok) {
  619. showMessage(errorMessage, LogError);
  620. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
  621. notifyEngineSetupFailed();
  622. }
  623. }
  624. bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
  625. {
  626. if (debug)
  627. qDebug("launchCDB startMode=%d", sp.startMode);
  628. const QChar blank(QLatin1Char(' '));
  629. // Start engine which will run until initial breakpoint:
  630. // Determine binary (force MSVC), extension lib name and path to use
  631. // The extension is passed as relative name with the path variable set
  632. //(does not work with absolute path names)
  633. const QString executable = sp.debuggerCommand;
  634. if (executable.isEmpty()) {
  635. *errorMessage = tr("There is no CDB executable specified.");
  636. return false;
  637. }
  638. const bool is64bit =
  639. #ifdef Q_OS_WIN
  640. Utils::winIs64BitBinary(executable);
  641. #else
  642. false;
  643. #endif
  644. const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
  645. if (!extensionFi.isFile()) {
  646. *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
  647. arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
  648. return false;
  649. }
  650. const QString extensionFileName = extensionFi.fileName();
  651. // Prepare arguments
  652. QStringList arguments;
  653. const bool isRemote = sp.startMode == AttachToRemoteServer;
  654. if (isRemote) { // Must be first
  655. arguments << QLatin1String("-remote") << sp.remoteChannel;
  656. } else {
  657. arguments << (QLatin1String("-a") + extensionFileName);
  658. }
  659. // Source line info/No terminal breakpoint / Pull extension
  660. arguments << QLatin1String("-lines") << QLatin1String("-G")
  661. // register idle (debuggee stop) notification
  662. << QLatin1String("-c")
  663. << QLatin1String(".idle_cmd ") + QString::fromLatin1(m_extensionCommandPrefixBA) + QLatin1String("idle");
  664. if (sp.useTerminal) // Separate console
  665. arguments << QLatin1String("-2");
  666. if (!m_options->symbolPaths.isEmpty())
  667. arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
  668. if (!m_options->sourcePaths.isEmpty())
  669. arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
  670. // Compile argument string preserving quotes
  671. QString nativeArguments = m_options->additionalArguments;
  672. switch (sp.startMode) {
  673. case StartInternal:
  674. case StartExternal:
  675. if (!nativeArguments.isEmpty())
  676. nativeArguments.push_back(blank);
  677. nativeArguments += QDir::toNativeSeparators(sp.executable);
  678. break;
  679. case AttachToRemoteServer:
  680. break;
  681. case AttachExternal:
  682. case AttachCrashedExternal:
  683. arguments << QLatin1String("-p") << QString::number(sp.attachPID);
  684. if (sp.startMode == AttachCrashedExternal) {
  685. arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
  686. } else {
  687. if (isCreatorConsole(startParameters(), *m_options))
  688. arguments << QLatin1String("-pr") << QLatin1String("-pb");
  689. }
  690. break;
  691. case AttachCore:
  692. arguments << QLatin1String("-z") << sp.coreFile;
  693. break;
  694. default:
  695. *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
  696. return false;
  697. }
  698. if (!sp.processArgs.isEmpty()) { // Complete native argument string.
  699. if (!nativeArguments.isEmpty())
  700. nativeArguments.push_back(blank);
  701. nativeArguments += sp.processArgs;
  702. }
  703. const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
  704. arg(QDir::toNativeSeparators(executable),
  705. arguments.join(QString(blank)) + blank + nativeArguments,
  706. QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
  707. extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
  708. showMessage(msg, LogMisc);
  709. m_outputBuffer.clear();
  710. const QStringList environment = sp.environment.size() == 0 ?
  711. QProcessEnvironment::systemEnvironment().toStringList() :
  712. sp.environment.toStringList();
  713. m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
  714. if (!sp.workingDirectory.isEmpty())
  715. m_process.setWorkingDirectory(sp.workingDirectory);
  716. #ifdef Q_OS_WIN
  717. if (!nativeArguments.isEmpty()) // Appends
  718. m_process.setNativeArguments(nativeArguments);
  719. #endif
  720. m_process.start(executable, arguments);
  721. if (!m_process.waitForStarted()) {
  722. *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
  723. arg(QDir::toNativeSeparators(executable), m_process.errorString());
  724. return false;
  725. }
  726. #ifdef Q_OS_WIN
  727. const unsigned long pid = Utils::winQPidToPid(m_process.pid());
  728. #else
  729. const unsigned long pid = 0;
  730. #endif
  731. showMessage(QString::fromLatin1("%1 running as %2").
  732. arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
  733. m_hasDebuggee = true;
  734. if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
  735. m_accessible = true;
  736. const QByteArray loadCommand = QByteArray(".load ")
  737. + extensionFileName.toLocal8Bit();
  738. postCommand(loadCommand, 0);
  739. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
  740. notifyEngineSetupOk();
  741. }
  742. return true;
  743. }
  744. void CdbEngine::setupInferior()
  745. {
  746. if (debug)
  747. qDebug("setupInferior");
  748. // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
  749. // (attemptBreakpointSynchronization() will be directly called then)
  750. attemptBreakpointSynchronization();
  751. if (startParameters().breakOnMain) {
  752. const BreakpointParameters bp(BreakpointAtMain);
  753. postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings,
  754. BreakpointModelId(quint16(-1)), true), 0);
  755. }
  756. postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
  757. postCommand(".asm source_line", 0); // Source line in assembly
  758. postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
  759. }
  760. void CdbEngine::runEngine()
  761. {
  762. if (debug)
  763. qDebug("runEngine");
  764. foreach (const QString &breakEvent, m_options->breakEvents)
  765. postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
  766. if (startParameters().startMode == AttachCore) {
  767. QTC_ASSERT(!m_coreStopReason.isNull(), return; );
  768. notifyInferiorUnrunnable();
  769. processStop(*m_coreStopReason, false);
  770. } else {
  771. postCommand("g", 0);
  772. }
  773. }
  774. bool CdbEngine::commandsPending() const
  775. {
  776. return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
  777. }
  778. void CdbEngine::shutdownInferior()
  779. {
  780. if (debug)
  781. qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
  782. isCdbProcessRunning());
  783. if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
  784. if (debug)
  785. qDebug("notifyInferiorShutdownOk");
  786. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
  787. notifyInferiorShutdownOk();
  788. return;
  789. }
  790. if (m_accessible) { // except console.
  791. if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
  792. detachDebugger();
  793. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
  794. notifyInferiorShutdownOk();
  795. } else {
  796. // A command got stuck.
  797. if (commandsPending()) {
  798. showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
  799. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
  800. notifyInferiorShutdownFailed();
  801. return;
  802. }
  803. if (!canInterruptInferior()) {
  804. showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
  805. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
  806. notifyInferiorShutdownFailed();
  807. return;
  808. }
  809. interruptInferior(); // Calls us again
  810. }
  811. }
  812. /* shutdownEngine/processFinished:
  813. * Note that in the case of launching a process by the debugger, the debugger
  814. * automatically quits a short time after reporting the session becoming
  815. * inaccessible without debuggee (notifyInferiorExited). In that case,
  816. * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
  817. * as not to confuse the state engine.
  818. */
  819. void CdbEngine::shutdownEngine()
  820. {
  821. if (debug)
  822. qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
  823. "accessible=%d,commands pending=%d",
  824. stateName(state()), isCdbProcessRunning(), m_accessible,
  825. commandsPending());
  826. if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
  827. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
  828. notifyEngineShutdownOk();
  829. return;
  830. }
  831. // No longer trigger anything from messages
  832. m_ignoreCdbOutput = true;
  833. // Go for kill if there are commands pending.
  834. if (m_accessible && !commandsPending()) {
  835. // detach (except console): Wait for debugger to finish.
  836. if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
  837. detachDebugger();
  838. // Remote requires a bit more force to quit.
  839. if (m_effectiveStartMode == AttachToRemoteServer) {
  840. postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
  841. postCommand("qq", 0);
  842. } else {
  843. postCommand("q", 0);
  844. }
  845. m_notifyEngineShutdownOnTermination = true;
  846. return;
  847. } else {
  848. // Remote process. No can do, currently
  849. m_notifyEngineShutdownOnTermination = true;
  850. Utils::SynchronousProcess::stopProcess(m_process);
  851. return;
  852. }
  853. // Lost debuggee, debugger should quit anytime now
  854. if (!m_hasDebuggee) {
  855. m_notifyEngineShutdownOnTermination = true;
  856. return;
  857. }
  858. interruptInferior();
  859. }
  860. void CdbEngine::processFinished()
  861. {
  862. if (debug)
  863. qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
  864. elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
  865. m_process.exitStatus(), m_process.exitCode());
  866. const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
  867. if (crashed) {
  868. showMessage(tr("CDB crashed"), LogError); // not in your life.
  869. } else {
  870. showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
  871. }
  872. if (m_notifyEngineShutdownOnTermination) {
  873. if (crashed) {
  874. if (debug)
  875. qDebug("notifyEngineIll");
  876. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
  877. notifyEngineIll();
  878. } else {
  879. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
  880. notifyEngineShutdownOk();
  881. }
  882. } else {
  883. // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
  884. // Otherwise, we take a shortcut.
  885. if (isSlaveEngine()) {
  886. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
  887. notifyInferiorExited();
  888. } else {
  889. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
  890. notifyEngineSpontaneousShutdown();
  891. }
  892. }
  893. }
  894. void CdbEngine::detachDebugger()
  895. {
  896. postCommand(".detach", 0);
  897. }
  898. static inline bool isWatchIName(const QByteArray &iname)
  899. {
  900. return iname.startsWith("watch");
  901. }
  902. void CdbEngine::updateWatchData(const WatchData &dataIn,
  903. const WatchUpdateFlags & flags)
  904. {
  905. if (debug || debugLocals || debugWatches)
  906. qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
  907. elapsedLogTime(), m_accessible, stateName(state()),
  908. flags.tryIncremental,
  909. qPrintable(dataIn.toString()));
  910. if (!m_accessible) // Add watch data while running?
  911. return;
  912. // New watch item?
  913. if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
  914. QByteArray args;
  915. ByteArrayInputStream str(args);
  916. str << dataIn.iname << " \"" << dataIn.exp << '"';
  917. postExtensionCommand("addwatch", args, 0,
  918. &CdbEngine::handleAddWatch, 0,
  919. qVariantFromValue(dataIn));
  920. return;
  921. }
  922. if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
  923. WatchData data = dataIn;
  924. data.setAllUnneeded();
  925. watchHandler()->insertData(data);
  926. return;
  927. }
  928. updateLocalVariable(dataIn.iname);
  929. }
  930. void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
  931. {
  932. WatchData item = qvariant_cast<WatchData>(reply->cookie);
  933. if (debugWatches)
  934. qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
  935. if (reply->success) {
  936. updateLocalVariable(item.iname);
  937. } else {
  938. item.setError(tr("Unable to add expression"));
  939. watchHandler()->insertIncompleteData(item);
  940. showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
  941. arg(QString::fromLatin1(item.iname), QString::fromLatin1(item.exp),
  942. QString::fromLocal8Bit(reply->errorMessage)), LogError);
  943. }
  944. }
  945. void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
  946. {
  947. if (debuggerCore()->boolSetting(VerboseLog))
  948. str << blankSeparator << "-v";
  949. if (debuggerCore()->boolSetting(UseDebuggingHelpers))
  950. str << blankSeparator << "-c";
  951. const QByteArray typeFormats = watchHandler()->typeFormatRequests();
  952. if (!typeFormats.isEmpty())
  953. str << blankSeparator << "-T " << typeFormats;
  954. const QByteArray individualFormats = watchHandler()->individualFormatRequests();
  955. if (!individualFormats.isEmpty())
  956. str << blankSeparator << "-I " << individualFormats;
  957. }
  958. void CdbEngine::updateLocalVariable(const QByteArray &iname)
  959. {
  960. const bool isWatch = isWatchIName(iname);
  961. if (debugWatches)
  962. qDebug() << "updateLocalVariable watch=" << isWatch << iname;
  963. QByteArray localsArguments;
  964. ByteArrayInputStream str(localsArguments);
  965. addLocalsOptions(str);
  966. if (!isWatch) {
  967. const int stackFrame = stackHandler()->currentIndex();
  968. if (stackFrame < 0) {
  969. qWarning("Internal error; no stack frame in updateLocalVariable");
  970. return;
  971. }
  972. str << blankSeparator << stackFrame;
  973. }
  974. str << blankSeparator << iname;
  975. postExtensionCommand(isWatch ? "watches" : "locals",
  976. localsArguments, 0,
  977. &CdbEngine::handleLocals,
  978. 0, QVariant(int(PartialLocalsUpdate)));
  979. }
  980. bool CdbEngine::hasCapability(unsigned cap) const
  981. {
  982. return cap & (DisassemblerCapability | RegisterCapability
  983. | ShowMemoryCapability
  984. |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability|WatchWidgetsCapability
  985. |ReloadModuleCapability
  986. |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
  987. |BreakConditionCapability|TracePointCapability
  988. |BreakModuleCapability
  989. |OperateByInstructionCapability
  990. |RunToLineCapability
  991. |MemoryAddressCapability);
  992. }
  993. void CdbEngine::executeStep()
  994. {
  995. if (!m_operateByInstruction)
  996. m_sourceStepInto = true; // See explanation at handleStackTrace().
  997. postCommand(QByteArray("t"), 0); // Step into-> t (trace)
  998. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
  999. notifyInferiorRunRequested();
  1000. }
  1001. void CdbEngine::executeStepOut()
  1002. {
  1003. postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
  1004. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
  1005. notifyInferiorRunRequested();
  1006. }
  1007. void CdbEngine::executeNext()
  1008. {
  1009. postCommand(QByteArray("p"), 0); // Step over -> p
  1010. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
  1011. notifyInferiorRunRequested();
  1012. }
  1013. void CdbEngine::executeStepI()
  1014. {
  1015. executeStep();
  1016. }
  1017. void CdbEngine::executeNextI()
  1018. {
  1019. executeNext();
  1020. }
  1021. void CdbEngine::continueInferior()
  1022. {
  1023. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
  1024. notifyInferiorRunRequested();
  1025. doContinueInferior();
  1026. }
  1027. void CdbEngine::doContinueInferior()
  1028. {
  1029. postCommand(QByteArray("g"), 0);
  1030. }
  1031. bool CdbEngine::canInterruptInferior() const
  1032. {
  1033. return m_effectiveStartMode != AttachToRemoteServer && inferiorPid();
  1034. }
  1035. void CdbEngine::interruptInferior()
  1036. {
  1037. if (debug)
  1038. qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
  1039. bool ok = false;
  1040. if (!canInterruptInferior()) {
  1041. showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
  1042. } else {
  1043. ok = doInterruptInferior(NoSpecialStop);
  1044. }
  1045. // Restore running state if stop failed.
  1046. if (!ok) {
  1047. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
  1048. notifyInferiorStopOk();
  1049. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
  1050. notifyInferiorRunRequested();
  1051. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
  1052. notifyInferiorRunOk();
  1053. }
  1054. }
  1055. void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
  1056. {
  1057. if (m_specialStopMode == NoSpecialStop)
  1058. doInterruptInferior(CustomSpecialStop);
  1059. m_customSpecialStopData.push_back(v);
  1060. }
  1061. bool CdbEngine::doInterruptInferior(SpecialStopMode sm)
  1062. {
  1063. const SpecialStopMode oldSpecialMode = m_specialStopMode;
  1064. m_specialStopMode = sm;
  1065. showMessage(QString::fromLatin1("Interrupting process %1...").arg(inferiorPid()), LogMisc);
  1066. QString errorMessage;
  1067. const bool ok = interruptProcess(inferiorPid(), CdbEngineType, &errorMessage);
  1068. if (!ok) {
  1069. m_specialStopMode = oldSpecialMode;
  1070. showMessage(errorMessage, LogError);
  1071. }
  1072. return ok;
  1073. }
  1074. void CdbEngine::executeRunToLine(const ContextData &data)
  1075. {
  1076. // Add one-shot breakpoint
  1077. BreakpointParameters bp;
  1078. if (data.address) {
  1079. bp.type =BreakpointByAddress;
  1080. bp.address = data.address;
  1081. } else {
  1082. bp.type =BreakpointByFileAndLine;
  1083. bp.fileName = data.fileName;
  1084. bp.lineNumber = data.lineNumber;
  1085. }
  1086. postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(quint16(-1)), true), 0);
  1087. continueInferior();
  1088. }
  1089. void CdbEngine::executeRunToFunction(const QString &functionName)
  1090. {
  1091. // Add one-shot breakpoint
  1092. BreakpointParameters bp(BreakpointByFunction);
  1093. bp.functionName = functionName;
  1094. postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(quint16(-1)), true), 0);
  1095. continueInferior();
  1096. }
  1097. void CdbEngine::setRegisterValue(int regnr, const QString &value)
  1098. {
  1099. const Registers registers = registerHandler()->registers();
  1100. QTC_ASSERT(regnr < registers.size(), return);
  1101. // Value is decimal or 0x-hex-prefixed
  1102. QByteArray cmd;
  1103. ByteArrayInputStream str(cmd);
  1104. str << "r " << registers.at(regnr).name << '=' << value;
  1105. postCommand(cmd, 0);
  1106. reloadRegisters();
  1107. }
  1108. void CdbEngine::executeJumpToLine(const ContextData &data)
  1109. {
  1110. if (data.address) {
  1111. // Goto address directly.
  1112. jumpToAddress(data.address);
  1113. gotoLocation(Location(data.address));
  1114. } else {
  1115. // Jump to source line: Resolve source line address and go to that location
  1116. QByteArray cmd;
  1117. ByteArrayInputStream str(cmd);
  1118. str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
  1119. const QVariant cookie = qVariantFromValue(data);
  1120. postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
  1121. }
  1122. }
  1123. void CdbEngine::jumpToAddress(quint64 address)
  1124. {
  1125. // Fake a jump to address by setting the PC register.
  1126. QByteArray registerCmd;
  1127. ByteArrayInputStream str(registerCmd);
  1128. // PC-register depending on 64/32bit.
  1129. str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
  1130. str.setHexPrefix(true);
  1131. str.setIntegerBase(16);
  1132. str << address;
  1133. postCommand(registerCmd, 0);
  1134. }
  1135. void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
  1136. {
  1137. if (cmd->reply.isEmpty())
  1138. return;
  1139. // Evaluate expression: 5365511549 = 00000001`3fcf357d
  1140. // Set register 'rip' to hex address and goto lcoation
  1141. QByteArray answer = cmd->reply.front().trimmed();
  1142. const int equalPos = answer.indexOf(" = ");
  1143. if (equalPos == -1)
  1144. return;
  1145. answer.remove(0, equalPos + 3);
  1146. const int apPos = answer.indexOf('`');
  1147. if (apPos != -1)
  1148. answer.remove(apPos, 1);
  1149. bool ok;
  1150. const quint64 address = answer.toLongLong(&ok, 16);
  1151. if (ok && address) {
  1152. QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
  1153. const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
  1154. jumpToAddress(address);
  1155. gotoLocation(Location(cookie.fileName, cookie.lineNumber));
  1156. }
  1157. }
  1158. static inline bool isAsciiWord(const QString &s)
  1159. {
  1160. foreach (const QChar &c, s) {
  1161. if (!c.isLetterOrNumber() || c.toAscii() == 0)
  1162. return false;
  1163. }
  1164. return true;
  1165. }
  1166. void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
  1167. {
  1168. if (debug)
  1169. qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
  1170. if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
  1171. qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
  1172. return;
  1173. }
  1174. QByteArray cmd;
  1175. ByteArrayInputStream str(cmd);
  1176. switch (value.type()) {
  1177. case QVariant::String: {
  1178. // Convert qstring to Utf16 data not considering endianness for Windows.
  1179. const QString s = value.toString();
  1180. if (isAsciiWord(s)) {
  1181. str << m_extensionCommandPrefixBA << "assign \"" << w->iname << '='
  1182. << s.toLatin1() << '"';
  1183. } else {
  1184. const QByteArray utf16(reinterpret_cast<const char *>(s.utf16()), 2 * s.size());
  1185. str << m_extensionCommandPrefixBA << "assign -u " << w->iname << '='
  1186. << utf16.toHex();
  1187. }
  1188. }
  1189. break;
  1190. default:
  1191. str << m_extensionCommandPrefixBA << "assign " << w->iname << '='
  1192. << value.toString();
  1193. break;
  1194. }
  1195. postCommand(cmd, 0);
  1196. // Update all locals in case we change a union or something pointed to
  1197. // that affects other variables, too.
  1198. updateLocals();
  1199. }
  1200. void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
  1201. {
  1202. int currentThreadId;
  1203. Threads threads = ThreadsHandler::parseGdbmiThreads(data, &currentThreadId);
  1204. threadsHandler()->setThreads(threads);
  1205. threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
  1206. forceCurrentThreadId : currentThreadId);
  1207. }
  1208. void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
  1209. {
  1210. if (debug)
  1211. qDebug("CdbEngine::handleThreads success=%d", reply->success);
  1212. if (reply->success) {
  1213. GdbMi data;
  1214. data.fromString(reply->reply);
  1215. parseThreads(data);
  1216. // Continue sequence
  1217. postCommandSequence(reply->commandSequence);
  1218. } else {
  1219. showMessage(QString::fromLatin1(reply->errorMessage), LogError);
  1220. }
  1221. }
  1222. void CdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
  1223. {
  1224. if (languages & CppLanguage)
  1225. postCommand(command.toLocal8Bit(), QuietCommand);
  1226. }
  1227. // Post command without callback
  1228. void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
  1229. {
  1230. if (debug)
  1231. qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
  1232. elapsedLogTime(), cmd.constData(), flags, stateName(state()));
  1233. if (!(flags & QuietCommand))
  1234. showMessage(QString::fromLocal8Bit(cmd), LogInput);
  1235. m_process.write(cmd + '\n');
  1236. }
  1237. // Post a built-in-command producing free-format output with a callback.
  1238. // In order to catch the output, it is enclosed in 'echo' commands
  1239. // printing a specially formatted token to be identifiable in the output.
  1240. void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
  1241. BuiltinCommandHandler handler,
  1242. unsigned nextCommandFlag,
  1243. const QVariant &cookie)
  1244. {
  1245. if (!m_accessible) {
  1246. const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
  1247. .arg(QString::fromLocal8Bit(cmd), QString::fromLatin1(stateName(state())));
  1248. showMessage(msg, LogError);
  1249. return;
  1250. }
  1251. if (!flags & QuietCommand)
  1252. showMessage(QString::fromLocal8Bit(cmd), LogInput);
  1253. const int token = m_nextCommandToken++;
  1254. CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
  1255. m_builtinCommandQueue.push_back(pendingCommand);
  1256. // Enclose command in echo-commands for token
  1257. QByteArray fullCmd;
  1258. ByteArrayInputStream str(fullCmd);
  1259. str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
  1260. << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
  1261. if (debug)
  1262. qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
  1263. elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
  1264. m_builtinCommandQueue.size(), nextCommandFlag);
  1265. if (debug > 1)
  1266. qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
  1267. fullCmd.constData());
  1268. m_process.write(fullCmd);
  1269. }
  1270. // Post an extension command producing one-line output with a callback,
  1271. // pass along token for identification in queue.
  1272. void CdbEngine::postExtensionCommand(const QByteArray &cmd,
  1273. const QByteArray &arguments,
  1274. unsigned flags,
  1275. ExtensionCommandHandler handler,
  1276. unsigned nextCommandFlag,
  1277. const QVariant &cookie)
  1278. {
  1279. if (!m_accessible) {
  1280. const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
  1281. .arg(QString::fromLocal8Bit(cmd), QString::fromLatin1(stateName(state())));
  1282. showMessage(msg, LogError);
  1283. return;
  1284. }
  1285. const int token = m_nextCommandToken++;
  1286. // Format full command with token to be recognizeable in the output
  1287. QByteArray fullCmd;
  1288. ByteArrayInputStream str(fullCmd);
  1289. str << m_extensionCommandPrefixBA << cmd << " -t " << token;
  1290. if (!arguments.isEmpty())
  1291. str << ' ' << arguments;
  1292. if (!flags & QuietCommand)
  1293. showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
  1294. CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
  1295. m_extensionCommandQueue.push_back(pendingCommand);
  1296. // Enclose command in echo-commands for token
  1297. if (debug)
  1298. qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
  1299. elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
  1300. m_extensionCommandQueue.size(), nextCommandFlag);
  1301. m_process.write(fullCmd + '\n');
  1302. }
  1303. void CdbEngine::activateFrame(int index)
  1304. {
  1305. // TODO: assembler,etc
  1306. if (index < 0)
  1307. return;
  1308. const StackFrames &frames = stackHandler()->frames();
  1309. QTC_ASSERT(index < frames.size(), return);
  1310. const StackFrame frame = frames.at(index);
  1311. if (debug || debugLocals)
  1312. qDebug("activateFrame idx=%d '%s' %d", index,
  1313. qPrintable(frame.file), frame.line);
  1314. stackHandler()->setCurrentIndex(index);
  1315. const bool showAssembler = !frames.at(index).isUsable();
  1316. if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
  1317. watchHandler()->removeAllData();
  1318. QAction *assemblerAction = theAssemblerAction();
  1319. if (assemblerAction->isChecked()) {
  1320. gotoLocation(frame);
  1321. } else {
  1322. assemblerAction->trigger(); // Seems to trigger update
  1323. }
  1324. } else {
  1325. gotoLocation(frame);
  1326. updateLocals(true);
  1327. }
  1328. }
  1329. void CdbEngine::updateLocals(bool forNewStackFrame)
  1330. {
  1331. typedef QHash<QByteArray, int> WatcherHash;
  1332. const int frameIndex = stackHandler()->currentIndex();
  1333. if (frameIndex < 0) {
  1334. watchHandler()->removeAllData();
  1335. return;
  1336. }
  1337. const StackFrame frame = stackHandler()->currentFrame();
  1338. if (!frame.isUsable()) {
  1339. watchHandler()->removeAllData();
  1340. return;
  1341. }
  1342. /* Watchers: Forcibly discard old symbol group as switching from
  1343. * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
  1344. * and cause errors as it seems to go 'stale' when switching threads.
  1345. * Initial expand, get uninitialized and query */
  1346. QByteArray arguments;
  1347. ByteArrayInputStream str(arguments);
  1348. str << "-D";
  1349. // Pre-expand
  1350. const QSet<QByteArray> expanded = watchHandler()->expandedINames();
  1351. if (!expanded.isEmpty()) {
  1352. str << blankSeparator << "-e ";
  1353. int i = 0;
  1354. foreach(const QByteArray &e, expanded) {
  1355. if (i++)
  1356. str << ',';
  1357. str << e;
  1358. }
  1359. }
  1360. addLocalsOptions(str);
  1361. // Uninitialized variables if desired. Quote as safeguard against shadowed
  1362. // variables in case of errors in uninitializedVariables().
  1363. if (debuggerCore()->boolSetting(UseCodeModel)) {
  1364. QStringList uninitializedVariables;
  1365. getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
  1366. frame.function, frame.file, frame.line, &uninitializedVariables);
  1367. if (!uninitializedVariables.isEmpty()) {
  1368. str << blankSeparator << "-u \"";
  1369. int i = 0;
  1370. foreach(const QString &u, uninitializedVariables) {
  1371. if (i++)
  1372. str << ',';
  1373. str << localsPrefixC << u;
  1374. }
  1375. str << '"';
  1376. }
  1377. }
  1378. // Perform watches synchronization
  1379. str << blankSeparator << "-W";
  1380. const WatcherHash watcherHash = WatchHandler::watcherNames();
  1381. if (!watcherHash.isEmpty()) {
  1382. const WatcherHash::const_iterator cend = watcherHash.constEnd();
  1383. for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
  1384. str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
  1385. }
  1386. }
  1387. // Required arguments: frame
  1388. const int flags = forNewStackFrame ? LocalsUpdateForNewFrame : 0;
  1389. str << blankSeparator << frameIndex;
  1390. postExtensionCommand("locals", arguments, 0,
  1391. &CdbEngine::handleLocals, 0,
  1392. QVariant(flags));
  1393. }
  1394. void CdbEngine::selectThread(int index)
  1395. {
  1396. if (index < 0 || index == threadsHandler()->currentThread())
  1397. return;
  1398. const int newThreadId = threadsHandler()->threads().at(index).id;
  1399. threadsHandler()->setCurrentThread(index);
  1400. const QByteArray cmd = '~' + QByteArray::number(newThreadId) + " s";
  1401. postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
  1402. }
  1403. // Default address range for showing disassembly.
  1404. enum { DisassemblerRange = 512 };
  1405. /* Called with a stack frame (address and function) or just a function
  1406. * name from the context menu. When address and function are
  1407. * passed, try to emulate gdb's behaviour to display the whole function.
  1408. * CDB's 'u' (disassemble) command takes a symbol,
  1409. * but displays only 10 lines per default. So, to ensure the agent's
  1410. * address is in that range, resolve the function symbol, cache it and
  1411. * request the disassembly for a range that contains the agent's address. */
  1412. void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
  1413. {
  1414. QTC_ASSERT(m_accessible, return);
  1415. const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
  1416. const Location location = agent->location();
  1417. if (debug)
  1418. qDebug() << "CdbEngine::fetchDisassembler 0x"
  1419. << QString::number(location.address(), 16)
  1420. << location.from() << '!' << location.functionName();
  1421. if (!location.functionName().isEmpty()) {
  1422. // Resolve function (from stack frame with function and address
  1423. // or just function from editor).
  1424. postResolveSymbol(location.from(), location.functionName(), cookie);
  1425. } else if (location.address()) {
  1426. // No function, display a default range.
  1427. postDisassemblerCommand(location.address(), cookie);
  1428. } else {
  1429. QTC_ASSERT(false, return);
  1430. }
  1431. }
  1432. void CdbEngine::postDisassemblerCommand(quint64 address, const QVariant &cookie)
  1433. {
  1434. postDisassemblerCommand(address - DisassemblerRange / 2,
  1435. address + DisassemblerRange / 2, cookie);
  1436. }
  1437. void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress,
  1438. const QVariant &cookie)
  1439. {
  1440. QByteArray cmd;
  1441. ByteArrayInputStream str(cmd);
  1442. str << "u " << hex <<hexPrefixOn << address << ' ' << endAddress;
  1443. postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
  1444. }
  1445. void CdbEngine::postResolveSymbol(const QString &module, const QString &function,
  1446. const QVariant &cookie)
  1447. {
  1448. QString symbol = module.isEmpty() ? QString(QLatin1Char('*')) : module;
  1449. symbol += QLatin1Char('!');
  1450. symbol += function;
  1451. const QList<quint64> addresses = m_symbolAddressCache.values(symbol);
  1452. if (addresses.isEmpty()) {
  1453. QVariantList cookieList;
  1454. cookieList << QVariant(symbol) << cookie;
  1455. showMessage(QLatin1String("Resolving symbol: ") + symbol + QLatin1String("..."), LogMisc);
  1456. postBuiltinCommand(QByteArray("x ") + symbol.toLatin1(), 0,
  1457. &CdbEngine::handleResolveSymbol, 0,
  1458. QVariant(cookieList));
  1459. } else {
  1460. showMessage(QString::fromLatin1("Using cached addresses for %1.").
  1461. arg(symbol), LogMisc);
  1462. handleResolveSymbol(addresses, cookie);
  1463. }
  1464. }
  1465. // Parse address from 'x' response.
  1466. // "00000001`3f7ebe80 module!foo (void)"
  1467. static inline quint64 resolvedAddress(const QByteArray &line)
  1468. {
  1469. const int blankPos = line.indexOf(' ');
  1470. if (blankPos >= 0) {
  1471. QByteArray addressBA = line.left(blankPos);
  1472. if (addressBA.size() > 9 && addressBA.at(8) == '`')
  1473. addressBA.remove(8, 1);
  1474. bool ok;
  1475. const quint64 address = addressBA.toULongLong(&ok, 16);
  1476. if (ok)
  1477. return address;
  1478. }
  1479. return 0;
  1480. }
  1481. void CdbEngine::handleResolveSymbol(const CdbBuiltinCommandPtr &command)
  1482. {
  1483. QTC_ASSERT(command->cookie.type() == QVariant::List, return; );
  1484. const QVariantList cookieList = command->cookie.toList();
  1485. const QString symbol = cookieList.front().toString();
  1486. // Insert all matches of (potentially) ambiguous symbols
  1487. if (const int size = command->reply.size()) {
  1488. for (int i = 0; i < size; i++) {
  1489. if (const quint64 address = resolvedAddress(command->reply.at(i))) {
  1490. m_symbolAddressCache.insert(symbol, address);
  1491. showMessage(QString::fromLatin1("Obtained 0x%1 for %2 (#%3)").
  1492. arg(address, 0, 16).arg(symbol).arg(i + 1), LogMisc);
  1493. }
  1494. }
  1495. } else {
  1496. showMessage(QLatin1String("Symbol resolution failed: ")
  1497. + QString::fromLatin1(command->joinedReply()),
  1498. LogError);
  1499. }
  1500. handleResolveSymbol(m_symbolAddressCache.values(symbol), cookieList.back());
  1501. }
  1502. // Find the function address matching needle in a list of function
  1503. // addresses obtained from the 'x' command. Check for the
  1504. // mimimum POSITIVE offset (needle >= function address.)
  1505. static inline quint64 findClosestFunctionAddress(const QList<quint64> &addresses,
  1506. quint64 needle)
  1507. {
  1508. const int size = addresses.size();
  1509. if (!size)
  1510. return 0;
  1511. if (size == 1)
  1512. return addresses.front();
  1513. int closestIndex = 0;
  1514. quint64 closestOffset = 0xFFFFFFFF;
  1515. for (int i = 0; i < size; i++) {
  1516. if (addresses.at(i) <= needle) {
  1517. const quint64 offset = needle - addresses.at(i);
  1518. if (offset < closestOffset) {
  1519. closestOffset = offset;
  1520. closestIndex = i;
  1521. }
  1522. }
  1523. }
  1524. return addresses.at(closestIndex);
  1525. }
  1526. static inline QString msgAmbiguousFunction(const QString &functionName,
  1527. quint64 address,
  1528. const QList<quint64> &addresses)
  1529. {
  1530. QString result;
  1531. QTextStream str(&result);
  1532. str.setIntegerBase(16);
  1533. str.setNumberFlags(str.numberFlags() | QTextStream::ShowBase);
  1534. str << "Several overloads of function '" << functionName
  1535. << "()' were found (";
  1536. for (int i = 0; i < addresses.size(); ++i) {
  1537. if (i)
  1538. str << ", ";
  1539. str << addresses.at(i);
  1540. }
  1541. str << "), using " << address << '.';
  1542. return result;
  1543. }
  1544. void CdbEngine::handleResolveSymbol(const QList<quint64> &addresses, const QVariant &cookie)
  1545. {
  1546. // Disassembly mode: Determine suitable range containing the
  1547. // agent's address within the function to display.
  1548. if (qVariantCanConvert<DisassemblerAgent*>(cookie)) {
  1549. DisassemblerAgent *agent = cookie.value<DisassemblerAgent *>();
  1550. const quint64 agentAddress = agent->address();
  1551. quint64 functionAddress = 0;
  1552. quint64 endAddress = 0;
  1553. if (agentAddress) {
  1554. // We have an address from the agent, find closest.
  1555. if (const quint64 closest = findClosestFunctionAddress(addresses, agentAddress)) {
  1556. if (closest <= agentAddress) {
  1557. functionAddress = closest;
  1558. endAddress = agentAddress + DisassemblerRange / 2;
  1559. }
  1560. }
  1561. } else {
  1562. // No agent address, disassembly was started with a function name only.
  1563. if (!addresses.isEmpty()) {
  1564. functionAddress = addresses.first();
  1565. endAddress = functionAddress + DisassemblerRange / 2;
  1566. if (addresses.size() > 1)
  1567. showMessage(msgAmbiguousFunction(agent->location().functionName(), functionAddress, addresses), LogMisc);
  1568. }
  1569. }
  1570. // Disassemble a function, else use default range around agent address
  1571. if (functionAddress) {
  1572. if (const quint64 remainder = endAddress % 8)
  1573. endAddress += 8 - remainder;
  1574. postDisassemblerCommand(functionAddress, endAddress, cookie);
  1575. } else if (agentAddress) {
  1576. postDisassemblerCommand(agentAddress, cookie);
  1577. } else {
  1578. QTC_ASSERT(false, return);
  1579. }
  1580. return;
  1581. } // DisassemblerAgent
  1582. }
  1583. // Parse: "00000000`77606060 cc int 3"
  1584. void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
  1585. {
  1586. QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return);
  1587. DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
  1588. agent->setContents(parseCdbDisassembler(command->reply));
  1589. }
  1590. void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
  1591. {
  1592. if (debug)
  1593. qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
  1594. const MemoryViewCookie cookie(agent, editor, addr, length);
  1595. if (m_accessible) {
  1596. postFetchMemory(cookie);
  1597. } else {
  1598. doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
  1599. }
  1600. }
  1601. void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
  1602. {
  1603. QByteArray args;
  1604. ByteArrayInputStream str(args);
  1605. str << cookie.address << ' ' << cookie.length;
  1606. postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
  1607. qVariantFromValue(cookie));
  1608. }
  1609. void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
  1610. {
  1611. QTC_ASSERT(!data.isEmpty(), return);
  1612. if (!m_accessible) {
  1613. const MemoryChangeCookie cookie(addr, data);
  1614. doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
  1615. } else {
  1616. postCommand(cdbWriteMemoryCommand(addr, data), 0);
  1617. }
  1618. }
  1619. void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
  1620. {
  1621. QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return);
  1622. const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
  1623. if (command->success) {
  1624. const QByteArray data = QByteArray::fromBase64(command->reply);
  1625. if (unsigned(data.size()) == memViewCookie.length)
  1626. memViewCookie.agent->addLazyData(memViewCookie.editorToken,
  1627. memViewCookie.address, data);
  1628. } else {
  1629. showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
  1630. }
  1631. }
  1632. void CdbEngine::reloadModules()
  1633. {
  1634. postCommandSequence(CommandListModules);
  1635. }
  1636. void CdbEngine::loadSymbols(const QString & /* moduleName */)
  1637. {
  1638. }
  1639. void CdbEngine::loadAllSymbols()
  1640. {
  1641. }
  1642. void CdbEngine::requestModuleSymbols(const QString &moduleName)
  1643. {
  1644. Q_UNUSED(moduleName)
  1645. }
  1646. void CdbEngine::reloadRegisters()
  1647. {
  1648. postCommandSequence(CommandListRegisters);
  1649. }
  1650. void CdbEngine::reloadSourceFiles()
  1651. {
  1652. }
  1653. void CdbEngine::reloadFullStack()
  1654. {
  1655. if (debug)
  1656. qDebug("%s", Q_FUNC_INFO);
  1657. postCommandSequence(CommandListStack);
  1658. }
  1659. void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
  1660. {
  1661. // Fails for core dumps.
  1662. if (reply->success)
  1663. notifyInferiorPid(reply->reply.toULongLong());
  1664. if (reply->success || startParameters().startMode == AttachCore) {
  1665. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
  1666. notifyInferiorSetupOk();
  1667. } else {
  1668. showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
  1669. arg(QLatin1String(reply->errorMessage)), LogError);
  1670. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
  1671. notifyInferiorSetupFailed();
  1672. }
  1673. }
  1674. // Parse CDB gdbmi register syntax.
  1675. static Register parseRegister(const GdbMi &gdbmiReg)
  1676. {
  1677. Register reg;
  1678. reg.name = gdbmiReg.findChild("name").data();
  1679. const GdbMi description = gdbmiReg.findChild("description");
  1680. if (description.type() != GdbMi::Invalid) {
  1681. reg.name += " (";
  1682. reg.name += description.data();
  1683. reg.name += ')';
  1684. }
  1685. reg.value = gdbmiReg.findChild("value").data();
  1686. return reg;
  1687. }
  1688. void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
  1689. {
  1690. if (reply->success) {
  1691. GdbMi value;
  1692. value.fromString(reply->reply);
  1693. if (value.type() == GdbMi::List) {
  1694. Modules modules;
  1695. modules.reserve(value.childCount());
  1696. foreach (const GdbMi &gdbmiModule, value.children()) {
  1697. Module module;
  1698. module.moduleName = QString::fromLatin1(gdbmiModule.findChild("name").data());
  1699. module.modulePath = QString::fromLatin1(gdbmiModule.findChild("image").data());
  1700. module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
  1701. module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
  1702. if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
  1703. module.symbolsRead = Module::ReadOk;
  1704. modules.push_back(module);
  1705. }
  1706. modulesHandler()->setModules(modules);
  1707. } else {
  1708. showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
  1709. qWarning("Parse error in modules response:\n%s", reply->reply.constData());
  1710. }
  1711. } else {
  1712. showMessage(QString::fromLatin1("Failed to determine modules: %1").
  1713. arg(QLatin1String(reply->errorMessage)), LogError);
  1714. }
  1715. postCommandSequence(reply->commandSequence);
  1716. }
  1717. void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
  1718. {
  1719. if (reply->success) {
  1720. GdbMi value;
  1721. value.fromString(reply->reply);
  1722. if (value.type() == GdbMi::List) {
  1723. Registers registers;
  1724. registers.reserve(value.childCount());
  1725. foreach (const GdbMi &gdbmiReg, value.children())
  1726. registers.push_back(parseRegister(gdbmiReg));
  1727. registerHandler()->setAndMarkRegisters(registers);
  1728. } else {
  1729. showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
  1730. qWarning("Parse error in registers response:\n%s", reply->reply.constData());
  1731. }
  1732. } else {
  1733. showMessage(QString::fromLatin1("Failed to determine registers: %1").
  1734. arg(QLatin1String(reply->errorMessage)), LogError);
  1735. }
  1736. postCommandSequence(reply->commandSequence);
  1737. }
  1738. void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
  1739. {
  1740. const int flags = reply->cookie.toInt();
  1741. if (!(flags & PartialLocalsUpdate))
  1742. watchHandler()->removeAllData();
  1743. if (reply->success) {
  1744. QList<WatchData> watchData;
  1745. GdbMi root;
  1746. root.fromString(reply->reply);
  1747. QTC_ASSERT(root.isList(), return);
  1748. if (debugLocals) {
  1749. qDebug() << root.toString(true, 4);
  1750. }
  1751. // Courtesy of GDB engine
  1752. foreach (const GdbMi &child, root.children()) {
  1753. WatchData dummy;
  1754. dummy.iname = child.findChild("iname").data();
  1755. dummy.name = QLatin1String(child.findChild("name").data());
  1756. parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
  1757. }
  1758. watchHandler()->insertData(watchData);
  1759. if (debugLocals) {
  1760. QDebug nsp = qDebug().nospace();
  1761. nsp << "Obtained " << watchData.size() << " items:\n";
  1762. foreach (const WatchData &wd, watchData)
  1763. nsp << wd.toString() <<'\n';
  1764. }
  1765. if (flags & LocalsUpdateForNewFrame)
  1766. emit stackFrameCompleted();
  1767. } else {
  1768. showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
  1769. }
  1770. }
  1771. void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
  1772. {
  1773. if (!reply->success)
  1774. showMessage(QString::fromLatin1(reply->errorMessage), LogError);
  1775. }
  1776. enum CdbExecutionStatus {
  1777. CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
  1778. CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
  1779. CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
  1780. CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
  1781. CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
  1782. CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
  1783. CDB_STATUS_REVERSE_STEP_INTO = 14 };
  1784. static const char *cdbStatusName(unsigned long s)
  1785. {
  1786. switch (s) {
  1787. case CDB_STATUS_NO_CHANGE:
  1788. return "No change";
  1789. case CDB_STATUS_GO:
  1790. return "go";
  1791. case CDB_STATUS_GO_HANDLED:
  1792. return "go_handled";
  1793. case CDB_STATUS_GO_NOT_HANDLED:
  1794. return "go_not_handled";
  1795. case CDB_STATUS_STEP_OVER:
  1796. return "step_over";
  1797. case CDB_STATUS_STEP_INTO:
  1798. return "step_into";
  1799. case CDB_STATUS_BREAK:
  1800. return "break";
  1801. case CDB_STATUS_NO_DEBUGGEE:
  1802. return "no_debuggee";
  1803. case CDB_STATUS_STEP_BRANCH:
  1804. return "step_branch";
  1805. case CDB_STATUS_IGNORE_EVENT:
  1806. return "ignore_event";
  1807. case CDB_STATUS_RESTART_REQUESTED:
  1808. return "restart_requested";
  1809. case CDB_STATUS_REVERSE_GO:
  1810. return "reverse_go";
  1811. case CDB_STATUS_REVERSE_STEP_BRANCH:
  1812. return "reverse_step_branch";
  1813. case CDB_STATUS_REVERSE_STEP_OVER:
  1814. return "reverse_step_over";
  1815. case CDB_STATUS_REVERSE_STEP_INTO:
  1816. return "reverse_step_into";
  1817. }
  1818. return "unknown";
  1819. }
  1820. /* Examine how to react to a stop. */
  1821. enum StopActionFlags
  1822. {
  1823. // Report options
  1824. StopReportLog = 0x1,
  1825. StopReportStatusMessage = 0x2,
  1826. StopReportParseError = 0x4,
  1827. StopShowExceptionMessageBox = 0x8,
  1828. // Notify stop or just continue
  1829. StopNotifyStop = 0x10,
  1830. StopIgnoreContinue = 0x20,
  1831. // Hit on break in artificial stop thread (created by DebugBreak()).
  1832. StopInArtificialThread = 0x40,
  1833. StopShutdownInProgress = 0x80 // Shutdown in progress
  1834. };
  1835. static inline QString msgTracePointTriggered(BreakpointModelId id, const int number,
  1836. const QString &threadId)
  1837. {
  1838. return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
  1839. .arg(id.toString()).arg(number).arg(threadId);
  1840. }
  1841. static inline QString msgCheckingConditionalBreakPoint(BreakpointModelId id, const int number,
  1842. const QByteArray &condition,
  1843. const QString &threadId)
  1844. {
  1845. return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
  1846. .arg(id.toString()).arg(number).arg(threadId, QString::fromLatin1(condition));
  1847. }
  1848. unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
  1849. QString *message,
  1850. QString *exceptionBoxMessage,
  1851. bool conditionalBreakPointTriggered)
  1852. {
  1853. // Report stop reason (GDBMI)
  1854. unsigned rc = 0;
  1855. if (targetState() == DebuggerFinished)
  1856. rc |= StopShutdownInProgress;
  1857. if (debug)
  1858. qDebug("%s", stopReason.toString(true, 4).constData());
  1859. const QByteArray reason = stopReason.findChild("reason").data();
  1860. if (reason.isEmpty()) {
  1861. *message = tr("Malformed stop response received.");
  1862. rc |= StopReportParseError|StopNotifyStop;
  1863. return rc;
  1864. }
  1865. // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
  1866. if (state() == InferiorStopOk) {
  1867. *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
  1868. arg(QString::fromLatin1(reason));
  1869. rc |= StopReportLog;
  1870. return rc;
  1871. }
  1872. const int threadId = stopReason.findChild("threadId").data().toInt();
  1873. if (reason == "breakpoint") {
  1874. // Note: Internal breakpoints (run to line) are reported with id=0.
  1875. // Step out creates temporary breakpoints with id 10000.
  1876. BreakpointModelId id;
  1877. int number = 0;
  1878. const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
  1879. if (breakpointIdG.isValid()) {
  1880. id = BreakpointModelId(breakpointIdG.data().toInt());
  1881. if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
  1882. const BreakpointResponse parameters = breakHandler()->response(id);
  1883. if (!parameters.message.isEmpty()) {
  1884. showMessage(parameters.message + QLatin1Char('\n'), AppOutput);
  1885. showMessage(parameters.message, LogMisc);
  1886. }
  1887. // Trace point? Just report.
  1888. number = parameters.id.majorPart();
  1889. if (parameters.tracepoint) {
  1890. *message = msgTracePointTriggered(id, number, QString::number(threadId));
  1891. return StopReportLog|StopIgnoreContinue;
  1892. }
  1893. // Trigger evaluation of BP expression unless we are already in the response.
  1894. if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
  1895. *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
  1896. QString::number(threadId));
  1897. ConditionalBreakPointCookie cookie(id);
  1898. cookie.stopReason = stopReason;
  1899. evaluateExpression(parameters.condition, qVariantFromValue(cookie));
  1900. return StopReportLog;
  1901. }
  1902. } else {
  1903. id = BreakpointModelId();
  1904. }
  1905. }
  1906. QString tid = QString::number(threadId);
  1907. if (id && breakHandler()->type(id) == WatchpointAtAddress) {
  1908. *message = msgWatchpointByAddressTriggered(id, number, breakHandler()->address(id), tid);
  1909. } else if (id && breakHandler()->type(id) == WatchpointAtExpression) {
  1910. *message = msgWatchpointByExpressionTriggered(id, number, breakHandler()->expression(id), tid);
  1911. } else {
  1912. *message = msgBreakpointTriggered(id, number, tid);
  1913. }
  1914. rc |= StopReportStatusMessage|StopNotifyStop;
  1915. return rc;
  1916. }
  1917. if (reason == "exception") {
  1918. WinException exception;
  1919. exception.fromGdbMI(stopReason);
  1920. QString description = exception.toString();
  1921. #ifdef Q_OS_WIN
  1922. // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
  1923. // pulls DLLs. Avoid showing a 'stopped' Message box.
  1924. if (exception.exceptionCode == winExceptionStartupCompleteTrap
  1925. || exception.exceptionCode == winExceptionWX86Breakpoint)
  1926. return StopNotifyStop;
  1927. if (exception.exceptionCode == winExceptionCtrlPressed) {
  1928. // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
  1929. *message = msgInterrupted();
  1930. rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
  1931. return rc;
  1932. }
  1933. if (isDebuggerWinException(exception.exceptionCode)) {
  1934. rc |= StopReportStatusMessage|StopNotifyStop;
  1935. // Detect interruption by DebugBreak() and force a switch to thread 0.
  1936. if (exception.function == "ntdll!DbgBreakPoint")
  1937. rc |= StopInArtificialThread;
  1938. *message = msgInterrupted();
  1939. return rc;
  1940. }
  1941. #endif
  1942. *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
  1943. *message = description;
  1944. rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
  1945. return rc;
  1946. }
  1947. *message = msgStopped(QLatin1String(reason));
  1948. rc |= StopReportStatusMessage|StopNotifyStop;
  1949. return rc;
  1950. }
  1951. void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
  1952. {
  1953. if (!m_hasDebuggee)
  1954. return;
  1955. if (debug)
  1956. qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
  1957. elapsedLogTime(), messageBA.constData(),
  1958. stateName(state()), m_specialStopMode);
  1959. // Switch source level debugging
  1960. syncOperateByInstruction(m_operateByInstructionPending);
  1961. // Engine-special stop reasons: Breakpoints and setup
  1962. const SpecialStopMode specialStopMode = m_specialStopMode;
  1963. m_specialStopMode = NoSpecialStop;
  1964. switch(specialStopMode) {
  1965. case SpecialStopSynchronizeBreakpoints:
  1966. if (debug)
  1967. qDebug("attemptBreakpointSynchronization in special stop");
  1968. attemptBreakpointSynchronization();
  1969. doContinueInferior();
  1970. return;
  1971. case SpecialStopGetWidgetAt:
  1972. postWidgetAtCommand();
  1973. return;
  1974. case CustomSpecialStop:
  1975. foreach (const QVariant &data, m_customSpecialStopData)
  1976. handleCustomSpecialStop(data);
  1977. m_customSpecialStopData.clear();
  1978. doContinueInferior();
  1979. return;
  1980. case NoSpecialStop:
  1981. break;
  1982. }
  1983. if (state() == EngineSetupRequested) { // Temporary stop at beginning
  1984. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
  1985. notifyEngineSetupOk();
  1986. // Store stop reason to be handled in runEngine().
  1987. if (startParameters().startMode == AttachCore) {
  1988. m_coreStopReason.reset(new GdbMi);
  1989. m_coreStopReason->fromString(messageBA);
  1990. }
  1991. return;
  1992. }
  1993. GdbMi stopReason;
  1994. stopReason.fromString(messageBA);
  1995. processStop(stopReason, false);
  1996. }
  1997. void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
  1998. {
  1999. // Further examine stop and report to user
  2000. QString message;
  2001. QString exceptionBoxMessage;
  2002. int forcedThreadId = -1;
  2003. const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
  2004. conditionalBreakPointTriggered);
  2005. // Do the non-blocking log reporting
  2006. if (stopFlags & StopReportLog)
  2007. showMessage(message, LogMisc);
  2008. if (stopFlags & StopReportStatusMessage)
  2009. showStatusMessage(message);
  2010. if (stopFlags & StopReportParseError)
  2011. showMessage(message, LogError);
  2012. // Ignore things like WOW64, report tracepoints.
  2013. if (stopFlags & StopIgnoreContinue) {
  2014. postCommand("g", 0);
  2015. return;
  2016. }
  2017. // Notify about state and send off command sequence to get stack, etc.
  2018. if (stopFlags & StopNotifyStop) {
  2019. if (startParameters().startMode != AttachCore) {
  2020. if (state() == InferiorStopRequested) {
  2021. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
  2022. notifyInferiorStopOk();
  2023. } else {
  2024. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
  2025. notifyInferiorSpontaneousStop();
  2026. }
  2027. }
  2028. // Prevent further commands from being sent if shutdown is in progress
  2029. if (stopFlags & StopShutdownInProgress) {
  2030. showMessage(QString::fromLatin1("Shutdown request detected..."));
  2031. return;
  2032. }
  2033. const bool sourceStepInto = m_sourceStepInto;
  2034. m_sourceStepInto = false;
  2035. // Start sequence to get all relevant data.
  2036. if (stopFlags & StopInArtificialThread) {
  2037. showMessage(tr("Switching to main thread..."), LogMisc);
  2038. postCommand("~0 s", 0);
  2039. forcedThreadId = 0;
  2040. // Re-fetch stack again.
  2041. postCommandSequence(CommandListStack);
  2042. } else {
  2043. const GdbMi stack = stopReason.findChild("stack");
  2044. if (stack.isValid()) {
  2045. if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
  2046. executeStep(); // Hit on a frame while step into, see parseStackTrace().
  2047. return;
  2048. }
  2049. } else {
  2050. showMessage(QString::fromLatin1(stopReason.findChild("stackerror").data()), LogError);
  2051. }
  2052. }
  2053. const GdbMi threads = stopReason.findChild("threads");
  2054. if (threads.isValid()) {
  2055. parseThreads(threads, forcedThreadId);
  2056. } else {
  2057. showMessage(QString::fromLatin1(stopReason.findChild("threaderror").data()), LogError);
  2058. }
  2059. // Fire off remaining commands asynchronously
  2060. if (!m_pendingBreakpointMap.isEmpty())
  2061. postCommandSequence(CommandListBreakPoints);
  2062. if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
  2063. postCommandSequence(CommandListRegisters);
  2064. if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
  2065. postCommandSequence(CommandListModules);
  2066. }
  2067. // After the sequence has been sent off and CDB is pondering the commands,
  2068. // pop up a message box for exceptions.
  2069. if (stopFlags & StopShowExceptionMessageBox)
  2070. showStoppedByExceptionMessageBox(exceptionBoxMessage);
  2071. }
  2072. void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
  2073. {
  2074. const DebuggerState s = state();
  2075. if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
  2076. return;
  2077. if (debug)
  2078. qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
  2079. elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
  2080. switch(s) {
  2081. case EngineShutdownRequested:
  2082. shutdownEngine();
  2083. break;
  2084. case InferiorShutdownRequested:
  2085. shutdownInferior();
  2086. break;
  2087. default:
  2088. break;
  2089. }
  2090. }
  2091. void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
  2092. {
  2093. const DebuggerState s = state();
  2094. // suppress reports
  2095. if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
  2096. return;
  2097. if (debug)
  2098. qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
  2099. elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
  2100. switch (state()) {
  2101. case EngineSetupRequested:
  2102. break;
  2103. case EngineRunRequested:
  2104. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
  2105. notifyEngineRunAndInferiorRunOk();
  2106. break;
  2107. case InferiorRunOk:
  2108. case InferiorStopOk:
  2109. // Inaccessible without debuggee (exit breakpoint)
  2110. // We go for spontaneous engine shutdown instead.
  2111. if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
  2112. if (debug)
  2113. qDebug("Lost debuggeee");
  2114. m_hasDebuggee = false;
  2115. }
  2116. break;
  2117. case InferiorRunRequested:
  2118. STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
  2119. notifyInferiorRunOk();
  2120. resetLocation();
  2121. break;
  2122. case EngineShutdownRequested:
  2123. break;
  2124. default:
  2125. break;
  2126. }
  2127. }
  2128. void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
  2129. {
  2130. if (debug > 1) {
  2131. QDebug nospace = qDebug().nospace();
  2132. nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
  2133. << ' ' << stateName(state());
  2134. if (t == 'N' || debug > 1) {
  2135. nospace << ' ' << message;
  2136. } else {
  2137. nospace << ' ' << message.size() << " bytes";
  2138. }
  2139. }
  2140. // Is there a reply expected, some command queued?
  2141. if (t == 'R' || t == 'N') {
  2142. if (token == -1) { // Default token, user typed in extension command
  2143. showMessage(QString::fromLatin1(message), LogMisc);
  2144. return;
  2145. }
  2146. const int index = indexOfCommand(m_extensionCommandQueue, token);
  2147. if (index != -1) {
  2148. // Did the command finish? Take off queue and complete, invoke CB
  2149. const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
  2150. if (t == 'R') {
  2151. command->success = true;
  2152. command->reply = message;
  2153. } else {
  2154. command->success = false;
  2155. command->errorMessage = message;
  2156. }
  2157. if (debug)
  2158. qDebug("### Completed extension command '%s', token=%d, pending=%d",
  2159. command->command.constData(), command->token, m_extensionCommandQueue.size());
  2160. if (command->handler)
  2161. (this->*(command->handler))(command);
  2162. return;
  2163. }
  2164. }
  2165. if (what == "debuggee_output") {
  2166. showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
  2167. return;
  2168. }
  2169. if (what == "event") {
  2170. showStatusMessage(QString::fromLatin1(message), 5000);
  2171. return;
  2172. }
  2173. if (what == "session_accessible") {
  2174. if (!m_accessible) {
  2175. m_accessible = true;
  2176. handleSessionAccessible(message.toULong());
  2177. }
  2178. return;
  2179. }
  2180. if (what == "session_inaccessible") {
  2181. if (m_accessible) {
  2182. m_accessible = false;
  2183. handleSessionInaccessible(message.toULong());
  2184. }
  2185. return;
  2186. }
  2187. if (what == "session_idle") {
  2188. handleSessionIdle(message);
  2189. return;
  2190. }
  2191. if (what == "exception") {
  2192. WinException exception;
  2193. GdbMi gdbmi;
  2194. gdbmi.fromString(message);
  2195. exception.fromGdbMI(gdbmi);
  2196. const QString message = exception.toString(true);
  2197. showStatusMessage(message);
  2198. #ifdef Q_OS_WIN // Report C++ exception in application output as well.
  2199. if (exception.exceptionCode == winExceptionCppException)
  2200. showMessage(message + QLatin1Char('\n'), AppOutput);
  2201. #endif
  2202. return;
  2203. }
  2204. return;
  2205. }
  2206. // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
  2207. enum { CdbPromptLength = 7 };
  2208. static inline bool isCdbPrompt(const QByteArray &c)
  2209. {
  2210. return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
  2211. && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
  2212. && std::isdigit(c.at(4));
  2213. }
  2214. // Check for '<token>32>' or '<token>32<'
  2215. static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
  2216. int *token, bool *isStart)
  2217. {
  2218. *token = 0;
  2219. *isStart = false;
  2220. const int tokenPrefixSize = tokenPrefix.size();
  2221. const int size = c.size();
  2222. if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
  2223. return false;
  2224. switch (c.at(size - 1)) {
  2225. case '>':
  2226. *isStart = false;
  2227. break;
  2228. case '<':
  2229. *isStart = true;
  2230. break;
  2231. default:
  2232. return false;
  2233. }
  2234. if (!c.startsWith(tokenPrefix))
  2235. return false;
  2236. bool ok;
  2237. *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
  2238. return ok;
  2239. }
  2240. void CdbEngine::parseOutputLine(QByteArray line)
  2241. {
  2242. // The hooked output callback in the extension suppresses prompts,
  2243. // it should happen only in initial and exit stages. Note however that
  2244. // if the output is not hooked, sequences of prompts are possible which
  2245. // can mix things up.
  2246. while (isCdbPrompt(line))
  2247. line.remove(0, CdbPromptLength);
  2248. // An extension notification (potentially consisting of several chunks)
  2249. if (line.startsWith(m_creatorExtPrefix)) {
  2250. // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
  2251. const char type = line.at(m_creatorExtPrefix.size());
  2252. // integer token
  2253. const int tokenPos = m_creatorExtPrefix.size() + 2;
  2254. const int tokenEndPos = line.indexOf('|', tokenPos);
  2255. QTC_ASSERT(tokenEndPos != -1, return);
  2256. const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
  2257. // remainingChunks
  2258. const int remainingChunksPos = tokenEndPos + 1;
  2259. const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
  2260. QTC_ASSERT(remainingChunksEndPos != -1, return);
  2261. const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
  2262. // const char 'serviceName'
  2263. const int whatPos = remainingChunksEndPos + 1;
  2264. const int whatEndPos = line.indexOf('|', whatPos);
  2265. QTC_ASSERT(whatEndPos != -1, return);
  2266. const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
  2267. // Build up buffer, call handler once last chunk was encountered
  2268. m_extensionMessageBuffer += line.mid(whatEndPos + 1);
  2269. if (remainingChunks == 0) {
  2270. handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
  2271. m_extensionMessageBuffer.clear();
  2272. }
  2273. return;
  2274. }
  2275. // Check for command start/end tokens within which the builtin command
  2276. // output is enclosed
  2277. int token = 0;
  2278. bool isStartToken = false;
  2279. const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
  2280. if (debug > 1)
  2281. qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
  2282. line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
  2283. // If there is a current command, wait for end of output indicated by token,
  2284. // command, trigger handler and finish, else append to its output.
  2285. if (m_currentBuiltinCommandIndex != -1) {
  2286. QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
  2287. const CdbBuiltinCommandPtr &currentCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
  2288. if (isCommandToken) {
  2289. // Did the command finish? Invoke callback and remove from queue.
  2290. if (debug)
  2291. qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
  2292. currentCommand->command.constData(), currentCommand->token,
  2293. currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
  2294. QTC_ASSERT(token == currentCommand->token, return; );
  2295. if (currentCommand->handler)
  2296. (this->*(currentCommand->handler))(currentCommand);
  2297. m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
  2298. m_currentBuiltinCommandIndex = -1;
  2299. } else {
  2300. // Record output of current command
  2301. currentCommand->reply.push_back(line);
  2302. }
  2303. return;
  2304. } // m_currentCommandIndex
  2305. if (isCommandToken) {
  2306. // Beginning command token encountered, start to record output.
  2307. const int index = indexOfCommand(m_builtinCommandQueue, token);
  2308. QTC_ASSERT(isStartToken && index != -1, return; );
  2309. m_currentBuiltinCommandIndex = index;
  2310. const CdbBuiltinCommandPtr &currentCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
  2311. if (debug)
  2312. qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
  2313. return;
  2314. }
  2315. showMessage(QString::fromLocal8Bit(line), LogMisc);
  2316. }
  2317. void CdbEngine::readyReadStandardOut()
  2318. {
  2319. if (m_ignoreCdbOutput)
  2320. return;
  2321. m_outputBuffer += m_process.readAllStandardOutput();
  2322. // Split into lines and parse line by line.
  2323. while (true) {
  2324. const int endOfLinePos = m_outputBuffer.indexOf('\n');
  2325. if (endOfLinePos == -1) {
  2326. break;
  2327. } else {
  2328. // Check for '\r\n'
  2329. QByteArray line = m_outputBuffer.left(endOfLinePos);
  2330. if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
  2331. line.truncate(line.size() - 1);
  2332. parseOutputLine(line);
  2333. m_outputBuffer.remove(0, endOfLinePos + 1);
  2334. }
  2335. }
  2336. }
  2337. void CdbEngine::readyReadStandardError()
  2338. {
  2339. showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
  2340. }
  2341. void CdbEngine::processError()
  2342. {
  2343. showMessage(m_process.errorString(), LogError);
  2344. }
  2345. #if 0
  2346. // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
  2347. static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
  2348. {
  2349. QByteArray cmd(cmdC);
  2350. ByteArrayInputStream str(cmd);
  2351. foreach(const BreakpointData *bp, bps)
  2352. str << ' ' << bp->bpNumber;
  2353. return cmd;
  2354. }
  2355. #endif
  2356. bool CdbEngine::stateAcceptsBreakpointChanges() const
  2357. {
  2358. switch (state()) {
  2359. case InferiorRunOk:
  2360. case InferiorStopOk:
  2361. return true;
  2362. default:
  2363. break;
  2364. }
  2365. return false;
  2366. }
  2367. bool CdbEngine::acceptsBreakpoint(BreakpointModelId id) const
  2368. {
  2369. const BreakpointParameters &data = breakHandler()->breakpointData(id);
  2370. if (!data.isCppBreakpoint())
  2371. return false;
  2372. switch (data.type) {
  2373. case UnknownType:
  2374. case BreakpointAtFork:
  2375. case WatchpointAtExpression:
  2376. case BreakpointAtSysCall:
  2377. case BreakpointOnQmlSignalEmit:
  2378. case BreakpointAtJavaScriptThrow:
  2379. return false;
  2380. case WatchpointAtAddress:
  2381. case BreakpointByFileAndLine:
  2382. case BreakpointByFunction:
  2383. case BreakpointByAddress:
  2384. case BreakpointAtThrow:
  2385. case BreakpointAtCatch:
  2386. case BreakpointAtMain:
  2387. case BreakpointAtExec:
  2388. break;
  2389. }
  2390. return true;
  2391. }
  2392. // Context for fixing file/line-type breakpoints, for delayed creation.
  2393. class BreakpointCorrectionContext
  2394. {
  2395. public:
  2396. explicit BreakpointCorrectionContext(const CPlusPlus::Snapshot &s,
  2397. const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy) :
  2398. m_snapshot(s), m_workingCopy(workingCopy) {}
  2399. unsigned fixLineNumber(const QString &fileName, unsigned lineNumber) const;
  2400. private:
  2401. const CPlusPlus::Snapshot m_snapshot;
  2402. CPlusPlus::CppModelManagerInterface::WorkingCopy m_workingCopy;
  2403. };
  2404. static CPlusPlus::Document::Ptr getParsedDocument(const QString &fileName,
  2405. const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy,
  2406. const CPlusPlus::Snapshot &snapshot)
  2407. {
  2408. QString src;
  2409. if (workingCopy.contains(fileName)) {
  2410. src = workingCopy.source(fileName);
  2411. } else {
  2412. Utils::FileReader reader;
  2413. if (reader.fetch(fileName)) // ### FIXME error reporting
  2414. src = QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
  2415. }
  2416. QByteArray source = snapshot.preprocessedCode(src, fileName);
  2417. CPlusPlus::Document::Ptr doc = snapshot.documentFromSource(source, fileName);
  2418. doc->parse();
  2419. return doc;
  2420. }
  2421. unsigned BreakpointCorrectionContext::fixLineNumber(const QString &fileName,
  2422. unsigned lineNumber) const
  2423. {
  2424. CPlusPlus::Document::Ptr doc = m_snapshot.document(fileName);
  2425. if (!doc || !doc->translationUnit()->ast())
  2426. doc = getParsedDocument(fileName, m_workingCopy, m_snapshot);
  2427. CPlusPlus::FindCdbBreakpoint findVisitor(doc->translationUnit());
  2428. const unsigned correctedLine = findVisitor(lineNumber);
  2429. if (!correctedLine) {
  2430. qWarning("Unable to find breakpoint location for %s:%d",
  2431. qPrintable(QDir::toNativeSeparators(fileName)), lineNumber);
  2432. return lineNumber;
  2433. }
  2434. if (debug)
  2435. qDebug("Code model: Breakpoint line %u -> %u in %s",
  2436. lineNumber, correctedLine, qPrintable(fileName));
  2437. return correctedLine;
  2438. }
  2439. void CdbEngine::attemptBreakpointSynchronization()
  2440. {
  2441. if (debug)
  2442. qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
  2443. // Check if there is anything to be done at all.
  2444. BreakHandler *handler = breakHandler();
  2445. // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
  2446. foreach (BreakpointModelId id, handler->unclaimedBreakpointIds())
  2447. if (acceptsBreakpoint(id))
  2448. handler->setEngine(id, this);
  2449. // Quick check: is there a need to change something? - Populate module cache
  2450. bool changed = false;
  2451. const BreakpointModelIds ids = handler->engineBreakpointIds(this);
  2452. foreach (BreakpointModelId id, ids) {
  2453. switch (handler->state(id)) {
  2454. case BreakpointInsertRequested:
  2455. case BreakpointRemoveRequested:
  2456. case BreakpointChangeRequested:
  2457. changed = true;
  2458. break;
  2459. case BreakpointInserted: {
  2460. // Collect the new modules matching the files.
  2461. // In the future, that information should be obtained from the build system.
  2462. const BreakpointParameters &data = handler->breakpointData(id);
  2463. if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
  2464. m_fileNameModuleHash.insert(data.fileName, data.module);
  2465. }
  2466. break;
  2467. default:
  2468. break;
  2469. }
  2470. }
  2471. if (debugBreakpoints)
  2472. qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
  2473. elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
  2474. if (!changed)
  2475. return;
  2476. if (!m_accessible) {
  2477. // No nested calls.
  2478. if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
  2479. doInterruptInferior(SpecialStopSynchronizeBreakpoints);
  2480. return;
  2481. }
  2482. // Add/Change breakpoints and store pending ones in map, since
  2483. // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
  2484. // handleBreakPoints will the complete that information and set it on the break handler.
  2485. bool addedChanged = false;
  2486. QScopedPointer<BreakpointCorrectionContext> lineCorrection;
  2487. foreach (BreakpointModelId id, ids) {
  2488. BreakpointParameters parameters = handler->breakpointData(id);
  2489. BreakpointResponse response;
  2490. response.fromParameters(parameters);
  2491. response.id = BreakpointResponseId(id.majorPart(), id.minorPart());
  2492. // If we encountered that file and have a module for it: Add it.
  2493. if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
  2494. const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
  2495. if (it != m_fileNameModuleHash.constEnd())
  2496. parameters.module = it.value();
  2497. }
  2498. switch (handler->state(id)) {
  2499. case BreakpointInsertRequested:
  2500. if (parameters.type == BreakpointByFileAndLine
  2501. && m_options->breakpointCorrection) {
  2502. if (lineCorrection.isNull())
  2503. lineCorrection.reset(new BreakpointCorrectionContext(debuggerCore()->cppCodeModelSnapshot(),
  2504. CPlusPlus::CppModelManagerInterface::instance()->workingCopy()));
  2505. response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber);
  2506. postCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), 0);
  2507. } else {
  2508. postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
  2509. }
  2510. if (!parameters.enabled)
  2511. postCommand("bd " + QByteArray::number(id.majorPart()), 0);
  2512. handler->notifyBreakpointInsertProceeding(id);
  2513. handler->notifyBreakpointInsertOk(id);
  2514. m_pendingBreakpointMap.insert(id, response);
  2515. addedChanged = true;
  2516. // Ensure enabled/disabled is correct in handler and line number is there.
  2517. handler->setResponse(id, response);
  2518. if (debugBreakpoints)
  2519. qDebug("Adding %d %s\n", id.toInternalId(),
  2520. qPrintable(response.toString()));
  2521. break;
  2522. case BreakpointChangeRequested:
  2523. handler->notifyBreakpointChangeProceeding(id);
  2524. if (debugBreakpoints)
  2525. qDebug("Changing %d:\n %s\nTo %s\n", id.toInternalId(),
  2526. qPrintable(handler->response(id).toString()),
  2527. qPrintable(parameters.toString()));
  2528. if (parameters.enabled != handler->response(id).enabled) {
  2529. // Change enabled/disabled breakpoints without triggering update.
  2530. postCommand((parameters.enabled ? "be " : "bd ")
  2531. + QByteArray::number(id.majorPart()), 0);
  2532. response.pending = false;
  2533. response.enabled = parameters.enabled;
  2534. handler->setResponse(id, response);
  2535. } else {
  2536. // Delete and re-add, triggering update
  2537. addedChanged = true;
  2538. postCommand("bc " + QByteArray::number(id.majorPart()), 0);
  2539. postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
  2540. m_pendingBreakpointMap.insert(id, response);
  2541. }
  2542. handler->notifyBreakpointChangeOk(id);
  2543. break;
  2544. case BreakpointRemoveRequested:
  2545. postCommand("bc " + QByteArray::number(id.majorPart()), 0);
  2546. handler->notifyBreakpointRemoveProceeding(id);
  2547. handler->notifyBreakpointRemoveOk(id);
  2548. m_pendingBreakpointMap.remove(id);
  2549. break;
  2550. default:
  2551. break;
  2552. }
  2553. }
  2554. // List breakpoints and send responses
  2555. if (addedChanged)
  2556. postCommandSequence(CommandListBreakPoints);
  2557. }
  2558. // Pass a file name through source mapping and normalize upper/lower case (for the editor
  2559. // manager to correctly process it) and convert to clean path.
  2560. CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
  2561. {
  2562. // 1) Check cache.
  2563. QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
  2564. if (it != m_normalizedFileCache.constEnd())
  2565. return it.value();
  2566. if (debugSourceMapping)
  2567. qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
  2568. // Do we have source path mappings? ->Apply.
  2569. const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
  2570. DebuggerToSource);
  2571. // Up/lower case normalization according to Windows.
  2572. #ifdef Q_OS_WIN
  2573. QString normalized = Utils::normalizePathName(fileName);
  2574. #else
  2575. QString normalized = fileName;
  2576. #endif
  2577. if (debugSourceMapping)
  2578. qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
  2579. // Check if it really exists, that is normalize worked and QFileInfo confirms it.
  2580. const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
  2581. NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
  2582. if (!exists) {
  2583. // At least upper case drive letter if failed.
  2584. if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
  2585. result.fileName[0] = result.fileName.at(0).toUpper();
  2586. }
  2587. m_normalizedFileCache.insert(f, result);
  2588. if (debugSourceMapping)
  2589. qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
  2590. return result;
  2591. }
  2592. // Parse frame from GDBMI. Duplicate of the gdb code, but that
  2593. // has more processing.
  2594. static StackFrames parseFrames(const GdbMi &gdbmi)
  2595. {
  2596. StackFrames rc;
  2597. const int count = gdbmi.childCount();
  2598. rc.reserve(count);
  2599. for (int i = 0; i < count; i++) {
  2600. const GdbMi &frameMi = gdbmi.childAt(i);
  2601. StackFrame frame;
  2602. frame.level = i;
  2603. const GdbMi fullName = frameMi.findChild("fullname");
  2604. if (fullName.isValid()) {
  2605. frame.file = QFile::decodeName(fullName.data());
  2606. frame.line = frameMi.findChild("line").data().toInt();
  2607. frame.usable = false; // To be decided after source path mapping.
  2608. }
  2609. frame.function = QLatin1String(frameMi.findChild("func").data());
  2610. frame.from = QLatin1String(frameMi.findChild("from").data());
  2611. frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
  2612. rc.push_back(frame);
  2613. }
  2614. return rc;
  2615. }
  2616. unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
  2617. {
  2618. // Parse frames, find current. Special handling for step into:
  2619. // When stepping into on an actual function (source mode) by executing 't', an assembler
  2620. // frame pointing at the jmp instruction is hit (noticeable by top function being
  2621. // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
  2622. // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
  2623. int current = -1;
  2624. StackFrames frames = parseFrames(data);
  2625. const int count = frames.size();
  2626. for (int i = 0; i < count; i++) {
  2627. const bool hasFile = !frames.at(i).file.isEmpty();
  2628. // jmp-frame hit by step into, do another 't' and abort sequence.
  2629. if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
  2630. showMessage(QString::fromLatin1("Step into: Call instruction hit, performing additional step..."), LogMisc);
  2631. return ParseStackStepInto;
  2632. }
  2633. if (hasFile) {
  2634. const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
  2635. frames[i].file = fileName.fileName;
  2636. frames[i].usable = fileName.exists;
  2637. if (current == -1 && frames[i].usable)
  2638. current = i;
  2639. }
  2640. }
  2641. if (count && current == -1) // No usable frame, use assembly.
  2642. current = 0;
  2643. // Set
  2644. stackHandler()->setFrames(frames);
  2645. activateFrame(current);
  2646. return 0;
  2647. }
  2648. void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
  2649. {
  2650. if (command->success) {
  2651. GdbMi data;
  2652. data.fromString(command->reply);
  2653. parseStackTrace(data, false);
  2654. postCommandSequence(command->commandSequence);
  2655. } else {
  2656. showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
  2657. }
  2658. }
  2659. void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
  2660. {
  2661. int value = 0;
  2662. if (command->success) {
  2663. value = command->reply.toInt();
  2664. } else {
  2665. showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
  2666. }
  2667. // Is this a conditional breakpoint?
  2668. if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
  2669. const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
  2670. const QString message = value ?
  2671. tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
  2672. arg(value).arg(cookie.id.toString()) :
  2673. tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
  2674. arg(cookie.id.toString());
  2675. showMessage(message, LogMisc);
  2676. // Stop if evaluation is true, else continue
  2677. if (value) {
  2678. processStop(cookie.stopReason, true);
  2679. } else {
  2680. postCommand("g", 0);
  2681. }
  2682. }
  2683. }
  2684. void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
  2685. {
  2686. if (exp.contains(' ') && !exp.startsWith('"')) {
  2687. exp.prepend('"');
  2688. exp.append('"');
  2689. }
  2690. postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
  2691. }
  2692. void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
  2693. {
  2694. postCommandSequence(command->commandSequence);
  2695. }
  2696. // Post a sequence of standard commands: Trigger next once one completes successfully
  2697. void CdbEngine::postCommandSequence(unsigned mask)
  2698. {
  2699. if (debug)
  2700. qDebug("postCommandSequence 0x%x\n", mask);
  2701. if (!mask)
  2702. return;
  2703. if (mask & CommandListThreads) {
  2704. postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
  2705. return;
  2706. }
  2707. if (mask & CommandListStack) {
  2708. postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
  2709. return;
  2710. }
  2711. if (mask & CommandListRegisters) {
  2712. QTC_ASSERT(threadsHandler()->currentThread() >= 0, return);
  2713. postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
  2714. return;
  2715. }
  2716. if (mask & CommandListModules) {
  2717. postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
  2718. return;
  2719. }
  2720. if (mask & CommandListBreakPoints) {
  2721. postExtensionCommand("breakpoints", QByteArray("-v"), 0,
  2722. &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
  2723. return;
  2724. }
  2725. }
  2726. void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
  2727. {
  2728. bool success = false;
  2729. QString message;
  2730. do {
  2731. if (!reply->success) {
  2732. message = QString::fromLatin1(reply->errorMessage);
  2733. break;
  2734. }
  2735. // Should be "namespace::QWidget:0x555"
  2736. QString watchExp = QString::fromLatin1(reply->reply);
  2737. const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
  2738. if (sepPos == -1) {
  2739. message = QString::fromLatin1("Invalid output: %1").arg(watchExp);
  2740. break;
  2741. }
  2742. // 0x000 -> nothing found
  2743. if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
  2744. message = QString::fromLatin1("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
  2745. break;
  2746. }
  2747. // Turn into watch expression: "*(namespace::QWidget*)0x555"
  2748. watchExp.replace(sepPos, 1, QLatin1String("*)"));
  2749. watchExp.insert(0, QLatin1String("*("));
  2750. watchHandler()->watchExpression(watchExp);
  2751. success = true;
  2752. } while (false);
  2753. if (!success)
  2754. showMessage(message, LogWarning);
  2755. m_watchPointX = m_watchPointY = 0;
  2756. }
  2757. static inline void formatCdbBreakPointResponse(BreakpointModelId id, const BreakpointResponse &r,
  2758. QTextStream &str)
  2759. {
  2760. str << "Obtained breakpoint " << id << " (#" << r.id.majorPart() << ')';
  2761. if (r.pending) {
  2762. str << ", pending";
  2763. } else {
  2764. str.setIntegerBase(16);
  2765. str << ", at 0x" << r.address;
  2766. str.setIntegerBase(10);
  2767. }
  2768. if (!r.enabled)
  2769. str << ", disabled";
  2770. if (!r.module.isEmpty())
  2771. str << ", module: '" << r.module << '\'';
  2772. str << '\n';
  2773. }
  2774. void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
  2775. {
  2776. if (debugBreakpoints)
  2777. qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
  2778. if (!reply->success) {
  2779. showMessage(QString::fromLatin1(reply->errorMessage), LogError);
  2780. return;
  2781. }
  2782. GdbMi value;
  2783. value.fromString(reply->reply);
  2784. if (value.type() != GdbMi::List) {
  2785. showMessage(QString::fromLatin1("Unabled to parse breakpoints reply"), LogError);
  2786. return;
  2787. }
  2788. handleBreakPoints(value);
  2789. }
  2790. void CdbEngine::handleBreakPoints(const GdbMi &value)
  2791. {
  2792. // Report all obtained parameters back. Note that not all parameters are reported
  2793. // back, so, match by id and complete
  2794. if (debugBreakpoints)
  2795. qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
  2796. QString message;
  2797. QTextStream str(&message);
  2798. BreakHandler *handler = breakHandler();
  2799. foreach (const GdbMi &breakPointG, value.children()) {
  2800. BreakpointResponse reportedResponse;
  2801. parseBreakPoint(breakPointG, &reportedResponse);
  2802. if (debugBreakpoints)
  2803. qDebug(" Parsed %d: pending=%d %s\n", reportedResponse.id.majorPart(),
  2804. reportedResponse.pending,
  2805. qPrintable(reportedResponse.toString()));
  2806. if (reportedResponse.id.isValid() && !reportedResponse.pending) {
  2807. const BreakpointModelId mid = handler->findBreakpointByResponseId(reportedResponse.id);
  2808. QTC_ASSERT(mid.isValid(), continue);
  2809. const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(mid);
  2810. if (it != m_pendingBreakpointMap.end()) {
  2811. // Complete the response and set on handler.
  2812. BreakpointResponse &currentResponse = it.value();
  2813. currentResponse.id = reportedResponse.id;
  2814. currentResponse.address = reportedResponse.address;
  2815. currentResponse.module = reportedResponse.module;
  2816. currentResponse.pending = reportedResponse.pending;
  2817. currentResponse.enabled = reportedResponse.enabled;
  2818. formatCdbBreakPointResponse(mid, currentResponse, str);
  2819. if (debugBreakpoints)
  2820. qDebug(" Setting for %d: %s\n", currentResponse.id.majorPart(),
  2821. qPrintable(currentResponse.toString()));
  2822. handler->setResponse(mid, currentResponse);
  2823. m_pendingBreakpointMap.erase(it);
  2824. }
  2825. } // not pending reported
  2826. } // foreach
  2827. if (m_pendingBreakpointMap.empty()) {
  2828. str << QLatin1String("All breakpoints have been resolved.\n");
  2829. } else {
  2830. str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
  2831. }
  2832. showMessage(message, LogMisc);
  2833. }
  2834. void CdbEngine::watchPoint(const QPoint &p)
  2835. {
  2836. m_watchPointX = p.x();
  2837. m_watchPointY = p.y();
  2838. switch (state()) {
  2839. case InferiorStopOk:
  2840. postWidgetAtCommand();
  2841. break;
  2842. case InferiorRunOk:
  2843. // "Select Widget to Watch" from a running application is currently not
  2844. // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
  2845. // but requires some work as not to confuse the engine by state-change notifications
  2846. // emitted by the debuggee function call.
  2847. showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
  2848. break;
  2849. default:
  2850. showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
  2851. arg(QString::fromLatin1(stateName(state()))), LogWarning);
  2852. break;
  2853. }
  2854. }
  2855. void CdbEngine::postWidgetAtCommand()
  2856. {
  2857. QByteArray arguments = QByteArray::number(m_watchPointX);
  2858. arguments.append(' ');
  2859. arguments.append(QByteArray::number(m_watchPointY));
  2860. postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
  2861. }
  2862. void CdbEngine::handleCustomSpecialStop(const QVariant &v)
  2863. {
  2864. if (qVariantCanConvert<MemoryChangeCookie>(v)) {
  2865. const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
  2866. postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
  2867. return;
  2868. }
  2869. if (qVariantCanConvert<MemoryViewCookie>(v)) {
  2870. postFetchMemory(qVariantValue<MemoryViewCookie>(v));
  2871. return;
  2872. }
  2873. }
  2874. } // namespace Internal
  2875. } // namespace Debugger