diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index e8b464d5370..9d2d1e30264 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -478,6 +478,7 @@ void CdbEngine::init() m_extensionMessageBuffer.clear(); m_pendingBreakpointMap.clear(); m_customSpecialStopData.clear(); + m_symbolAddressCache.clear(); // Create local list of mappings in native separators m_sourcePathMappings.clear(); @@ -1532,16 +1533,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(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(agent); + str << "u " << hex < 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 &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 < offset) { + closestOffset = offset; + closestIndex = i; + } + } + } + return addresses.at(closestIndex); +} + +void CdbEngine::handleResolveSymbol(const QList &addresses, const QVariant &cookie) +{ + // Disassembly mode: Determine suitable range containing the + // agent's address within the function to display. + if (qVariantCanConvert(cookie)) { + DisassemblerAgent *agent = cookie.value(); + 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) { diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index ef97f1fc729..9c21dd4b0a8 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -211,8 +212,12 @@ private: void postWidgetAtCommand(); void handleCustomSpecialStop(const QVariant &v); void postFetchMemory(const MemoryViewCookie &c); + inline void postDisassemblerCommand(quint64 address, const QVariant &cookie = QVariant()); + void postDisassemblerCommand(quint64 address, quint64 endAddress, + const QVariant &cookie = QVariant()); + void postResolveSymbol(const QString &module, const QString &function, + const QVariant &cookie = QVariant()); void evaluateExpression(QByteArray exp, const QVariant &cookie = QVariant()); - // Builtin commands void dummyHandler(const CdbBuiltinCommandPtr &); void handleStackTrace(const CdbExtensionCommandPtr &); @@ -220,7 +225,10 @@ private: void handleDisassembler(const CdbBuiltinCommandPtr &); void handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &); void handleExpression(const CdbExtensionCommandPtr &); + void handleResolveSymbol(const CdbBuiltinCommandPtr &command); + void handleResolveSymbol(const QList &addresses, const QVariant &cookie); void jumpToAddress(quint64 address); + // Extension commands void handleThreads(const CdbExtensionCommandPtr &); void handlePid(const CdbExtensionCommandPtr &reply); @@ -270,6 +278,7 @@ private: int m_watchPointY; PendingBreakPointMap m_pendingBreakpointMap; QHash m_fileNameModuleHash; + QMultiHash m_symbolAddressCache; bool m_ignoreCdbOutput; QVariantList m_customSpecialStopData; QList m_sourcePathMappings; diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index a46841c2f12..61a8e3bf717 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -97,6 +97,7 @@ Internal::Location::Location(const StackFrame &frame, bool marker) m_functionName = frame.function; m_hasDebugInfo = frame.isUsable(); m_address = frame.address; + m_from = frame.from; } QDebug operator<<(QDebug d, DebuggerState state) diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 8fc593c8e93..7fabfd253d9 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -100,6 +100,7 @@ public: Location(const StackFrame &frame, bool marker = true); QString fileName() const { return m_fileName; } QString functionName() const { return m_functionName; } + QString from() const { return m_from; } int lineNumber() const { return m_lineNumber; } void setNeedsRaise(bool on) { m_needsRaise = on; } void setNeedsMarker(bool on) { m_needsMarker = on; } @@ -118,6 +119,7 @@ private: int m_lineNumber; QString m_fileName; QString m_functionName; + QString m_from; quint64 m_address; };