PageRenderTime 45ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

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

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