{
switch (sm) {
case NoStartMode:
- case AttachCore:
case StartRemoteGdb:
return false;
default:
#endif
}
-static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
+static inline QString msgNoCdbBinaryForToolChain(const Abi &tc)
{
return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
}
+static inline bool isMsvcFlavor(Abi::OSFlavor osf)
+{
+ return osf == Abi::WindowsMsvc2005Flavor
+ || osf == Abi::WindowsMsvc2008Flavor
+ || osf == Abi::WindowsMsvc2010Flavor;
+}
+
static QString cdbBinary(const DebuggerStartParameters &sp)
{
if (!sp.debuggerCommand.isEmpty()) {
// Do not use a GDB binary if we got started for a project with MinGW runtime.
- const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
- && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
- || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
- || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
+ const bool abiMatch = sp.toolChainAbi.os() == Abi::WindowsOS
+ && isMsvcFlavor(sp.toolChainAbi.osFlavor());
if (abiMatch)
return sp.debuggerCommand;
}
return false;
}
+ if (sp.startMode == AttachCore && !isMsvcFlavor(sp.toolChainAbi.osFlavor())) {
+ check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine cannot debug gdb core files."));
+ return false;
+ }
+
if (cdbBinary(sp).isEmpty()) {
check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
- check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
- check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
+ check->settingsCategory = QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
+ check->settingsPage = QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
return false;
}
CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
DebuggerEngine *masterEngine, const OptionsPtr &options) :
- DebuggerEngine(sp, masterEngine),
+ DebuggerEngine(sp, CppLanguage, masterEngine),
m_creatorExtPrefix("<qtcreatorcdbext>|"),
m_tokenPrefix("<token>"),
m_options(options),
m_extensionMessageBuffer.clear();
m_pendingBreakpointMap.clear();
m_customSpecialStopData.clear();
+ m_symbolAddressCache.clear();
+ m_coreStopReason.reset();
// Create local list of mappings in native separators
m_sourcePathMappings.clear();
const QString extensionFileName = extensionFi.fileName();
// Prepare arguments
QStringList arguments;
- const bool isRemote = sp.startMode == AttachToRemote;
+ const bool isRemote = sp.startMode == AttachToRemoteServer;
if (isRemote) { // Must be first
arguments << QLatin1String("-remote") << sp.remoteChannel;
} else {
nativeArguments.push_back(blank);
nativeArguments += QDir::toNativeSeparators(sp.executable);
break;
- case AttachToRemote:
+ case AttachToRemoteServer:
break;
case AttachExternal:
case AttachCrashedExternal:
arguments << QLatin1String("-pr") << QLatin1String("-pb");
}
break;
+ case AttachCore:
+ arguments << QLatin1String("-z") << sp.coreFile;
+ break;
default:
*errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
return false;
{
if (debug)
qDebug("setupInferior");
+ // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
+ // (attemptBreakpointSynchronization() will be directly called then)
attemptBreakpointSynchronization();
-
if (startParameters().breakOnMain) {
const BreakpointParameters bp(BreakpointAtMain);
postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings,
qDebug("runEngine");
foreach (const QString &breakEvent, m_options->breakEvents)
postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
- postCommand("g", 0);
+ if (startParameters().startMode == AttachCore) {
+ QTC_ASSERT(!m_coreStopReason.isNull(), return; );
+ notifyInferiorUnrunnable();
+ processStop(*m_coreStopReason, false);
+ } else {
+ postCommand("g", 0);
+ }
}
bool CdbEngine::commandsPending() const
if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
detachDebugger();
// Remote requires a bit more force to quit.
- if (m_effectiveStartMode == AttachToRemote) {
+ if (m_effectiveStartMode == AttachToRemoteServer) {
postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
postCommand("qq", 0);
} else {
unsigned CdbEngine::debuggerCapabilities() const
{
return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
- |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability
+ |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability|WatchWidgetsCapability
|ReloadModuleCapability
|BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
|BreakConditionCapability|TracePointCapability
bool CdbEngine::canInterruptInferior() const
{
- return m_effectiveStartMode != AttachToRemote && inferiorPid();
+ return m_effectiveStartMode != AttachToRemoteServer && inferiorPid();
}
void CdbEngine::interruptInferior()
postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
}
+// Default address range for showing disassembly.
+enum { DisassemblerRange = 512 };
+
+/* Try to emulate gdb's behaviour: When passed an address, display
+ * the disassembled function. CDB's 'u' (disassemble) command takes a symbol,
+ * but does not display the whole function, only 10 lines per default.
+ * So, to ensure the agent's
+ * address is in that range, resolve the function symbol, cache it and
+ * request the disassembly for a range that contains the agent's address. */
+
void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
{
QTC_ASSERT(m_accessible, return;)
+ const QString function = agent->location().functionName();
+ const QString module = agent->location().from();
+ const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
+ if (function.isEmpty() || module.isEmpty()) {
+ // No function, display a default range.
+ postDisassemblerCommand(agent->address(), cookie);
+ } else {
+ postResolveSymbol(module, function, cookie);
+ }
+}
+
+void CdbEngine::postDisassemblerCommand(quint64 address, const QVariant &cookie)
+{
+ postDisassemblerCommand(address - DisassemblerRange / 2,
+ address + DisassemblerRange / 2, cookie);
+}
+
+void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress,
+ const QVariant &cookie)
+{
QByteArray cmd;
ByteArrayInputStream str(cmd);
- str << "u " << hex << hexPrefixOn << agent->address() << " L40";
- const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
+ str << "u " << hex <<hexPrefixOn << address << ' ' << endAddress;
postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
}
+void CdbEngine::postResolveSymbol(const QString &module, const QString &function,
+ const QVariant &cookie)
+{
+ const QString symbol = module + QLatin1Char('!') + function;
+ const QList<quint64> addresses = m_symbolAddressCache.values(symbol);
+ if (addresses.isEmpty()) {
+ QVariantList cookieList;
+ cookieList << QVariant(symbol) << cookie;
+ showMessage(QLatin1String("Resolving symbol: ") + symbol, LogMisc);
+ postBuiltinCommand(QByteArray("x ") + symbol.toLatin1(), 0,
+ &CdbEngine::handleResolveSymbol, 0,
+ QVariant(cookieList));
+ } else {
+ showMessage(QString::fromLatin1("Using cached addresses for %1.").
+ arg(symbol), LogMisc);
+ handleResolveSymbol(addresses, cookie);
+ }
+}
+
+// Parse address from 'x' response.
+// "00000001`3f7ebe80 module!foo (void)"
+static inline quint64 resolvedAddress(const QByteArray &line)
+{
+ const int blankPos = line.indexOf(' ');
+ if (blankPos >= 0) {
+ QByteArray addressBA = line.left(blankPos);
+ if (addressBA.size() > 9 && addressBA.at(8) == '`')
+ addressBA.remove(8, 1);
+ bool ok;
+ const quint64 address = addressBA.toULongLong(&ok, 16);
+ if (ok)
+ return address;
+ }
+ return 0;
+}
+
+void CdbEngine::handleResolveSymbol(const CdbBuiltinCommandPtr &command)
+{
+ QTC_ASSERT(command->cookie.type() == QVariant::List, return; );
+ const QVariantList cookieList = command->cookie.toList();
+ const QString symbol = cookieList.front().toString();
+ // Insert all matches of (potentially) ambiguous symbols
+ if (const int size = command->reply.size()) {
+ for (int i = 0; i < size; i++) {
+ if (const quint64 address = resolvedAddress(command->reply.at(i))) {
+ m_symbolAddressCache.insert(symbol, address);
+ showMessage(QString::fromLatin1("Obtained 0x%1 for %2 (#%3)").
+ arg(address, 0, 16).arg(symbol).arg(i + 1), LogMisc);
+ }
+ }
+ } else {
+ showMessage(QLatin1String("Symbol resolution failed: ")
+ + QString::fromLatin1(command->joinedReply()),
+ LogError);
+ }
+ handleResolveSymbol(m_symbolAddressCache.values(symbol), cookieList.back());
+}
+
+// Find the function address matching needle in a list of function
+// addresses obtained from the 'x' command. Check for the
+// mimimum POSITIVE offset (needle >= function address.)
+static inline quint64 findClosestFunctionAddress(const QList<quint64> &addresses,
+ quint64 needle)
+{
+ const int size = addresses.size();
+ if (!size)
+ return 0;
+ if (size == 1)
+ return addresses.front();
+ int closestIndex = 0;
+ quint64 closestOffset = 0xFFFFFFFF;
+ for (int i = 0; i < size; i++) {
+ if (addresses.at(i) <= needle) {
+ const quint64 offset = needle - addresses.at(i);
+ if (offset < closestOffset) {
+ closestOffset = offset;
+ closestIndex = i;
+ }
+ }
+ }
+ return addresses.at(closestIndex);
+}
+
+void CdbEngine::handleResolveSymbol(const QList<quint64> &addresses, const QVariant &cookie)
+{
+ // Disassembly mode: Determine suitable range containing the
+ // agent's address within the function to display.
+ if (qVariantCanConvert<DisassemblerAgent*>(cookie)) {
+ DisassemblerAgent *agent = cookie.value<DisassemblerAgent *>();
+ const quint64 agentAddress = agent->address();
+ const quint64 functionAddress
+ = findClosestFunctionAddress(addresses, agentAddress);
+ if (functionAddress > 0 && functionAddress <= agentAddress) {
+ quint64 endAddress = agentAddress + DisassemblerRange / 2;
+ if (const quint64 remainder = endAddress % 8)
+ endAddress += 8 - remainder;
+ postDisassemblerCommand(functionAddress, endAddress, cookie);
+ } else {
+ postDisassemblerCommand(agentAddress, cookie);
+ }
+ return;
+ }
+}
+
// Parse: "00000000`77606060 cc int 3"
void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
{
void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
{
- if (reply->success) {
+ // Fails for core dumps.
+ if (reply->success)
notifyInferiorPid(reply->reply.toULongLong());
+ if (reply->success || startParameters().startMode == AttachCore) {
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
notifyInferiorSetupOk();
} else {
}
}
-// Parse CDB gdbmi register syntax
-static inline Register parseRegister(const GdbMi &gdbmiReg)
+// Parse CDB gdbmi register syntax.
+static Register parseRegister(const GdbMi &gdbmiReg)
{
Register reg;
reg.name = gdbmiReg.findChild("name").data();
reg.name += description.data();
reg.name += ')';
}
- reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
+ reg.value = gdbmiReg.findChild("value").data();
return reg;
}
if (state() == EngineSetupRequested) { // Temporary stop at beginning
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
notifyEngineSetupOk();
+ // Store stop reason to be handled in runEngine().
+ if (startParameters().startMode == AttachCore) {
+ m_coreStopReason.reset(new GdbMi);
+ m_coreStopReason->fromString(messageBA);
+ }
return;
}
+
GdbMi stopReason;
stopReason.fromString(messageBA);
processStop(stopReason, false);
}
// Notify about state and send off command sequence to get stack, etc.
if (stopFlags & StopNotifyStop) {
- if (state() == InferiorStopRequested) {
- STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
- notifyInferiorStopOk();
- } else {
- STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
- notifyInferiorSpontaneousStop();
+ if (startParameters().startMode != AttachCore) {
+ if (state() == InferiorStopRequested) {
+ STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
+ notifyInferiorStopOk();
+ } else {
+ STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
+ notifyInferiorSpontaneousStop();
+ }
}
// Prevent further commands from being sent if shutdown is in progress
if (stopFlags & StopShutdownInProgress) {
case BreakpointAtFork:
case WatchpointAtExpression:
case BreakpointAtSysCall:
+ case BreakpointOnQmlSignalHandler:
+ case BreakpointAtJavaScriptThrow:
return false;
case WatchpointAtAddress:
case BreakpointByFileAndLine:
}
switch (handler->state(id)) {
case BreakpointInsertRequested:
- if (parameters.type == BreakpointByFileAndLine) {
+ if (parameters.type == BreakpointByFileAndLine
+ && m_options->breakpointCorrection) {
if (lineCorrection.isNull())
lineCorrection.reset(new BreakpointCorrectionContext(debuggerCore()->cppCodeModelSnapshot(),
CPlusPlus::CppModelManagerInterface::instance()->workingCopy()));