OSDN Git Service

Debugger: Expose which languages (QML, C++, Any) an engine supports
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / cdb / cdbengine.cpp
index e8b464d..587662c 100644 (file)
@@ -316,7 +316,6 @@ static inline bool validMode(DebuggerStartMode sm)
 {
     switch (sm) {
     case NoStartMode:
-    case AttachCore:
     case StartRemoteGdb:
         return false;
     default:
@@ -353,19 +352,24 @@ bool isCdbEngineEnabled()
 #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;
     }
@@ -394,10 +398,15 @@ bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck
         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;
     }
 
@@ -427,7 +436,7 @@ static inline Utils::SavedAction *theAssemblerAction()
 
 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),
@@ -478,6 +487,8 @@ void CdbEngine::init()
     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();
@@ -730,7 +741,7 @@ bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessa
     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 {
@@ -756,7 +767,7 @@ bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessa
             nativeArguments.push_back(blank);
         nativeArguments += QDir::toNativeSeparators(sp.executable);
         break;
-    case AttachToRemote:
+    case AttachToRemoteServer:
         break;
     case AttachExternal:
     case AttachCrashedExternal:
@@ -768,6 +779,9 @@ bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessa
                 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;
@@ -826,8 +840,9 @@ void CdbEngine::setupInferior()
 {
     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,
@@ -844,7 +859,13 @@ void CdbEngine::runEngine()
         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
@@ -919,7 +940,7 @@ void CdbEngine::shutdownEngine()
         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 {
@@ -1073,7 +1094,7 @@ void CdbEngine::updateLocalVariable(const QByteArray &iname)
 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
@@ -1129,7 +1150,7 @@ void CdbEngine::doContinueInferior()
 
 bool CdbEngine::canInterruptInferior() const
 {
-    return m_effectiveStartMode != AttachToRemote && inferiorPid();
+    return m_effectiveStartMode != AttachToRemoteServer && inferiorPid();
 }
 
 void CdbEngine::interruptInferior()
@@ -1532,16 +1553,149 @@ void CdbEngine::selectThread(int index)
     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)
 {
@@ -1632,8 +1786,10 @@ void CdbEngine::reloadFullStack()
 
 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 {
@@ -1644,8 +1800,8 @@ void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
     }
 }
 
-// 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();
@@ -1655,7 +1811,7 @@ static inline Register parseRegister(const GdbMi &gdbmiReg)
         reg.name += description.data();
         reg.name += ')';
     }
-    reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
+    reg.value = gdbmiReg.findChild("value").data();
     return reg;
 }
 
@@ -1973,8 +2129,14 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
     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);
@@ -2002,12 +2164,14 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
     }
     // 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) {
@@ -2386,6 +2550,8 @@ bool CdbEngine::acceptsBreakpoint(BreakpointModelId id) const
     case BreakpointAtFork:
     case WatchpointAtExpression:
     case BreakpointAtSysCall:
+    case BreakpointOnQmlSignalHandler:
+    case BreakpointAtJavaScriptThrow:
         return false;
     case WatchpointAtAddress:
     case BreakpointByFileAndLine:
@@ -2521,7 +2687,8 @@ void CdbEngine::attemptBreakpointSynchronization()
         }
         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()));