forked from qt-creator/qt-creator
Debugger[CDB]: Display more context in disassembly.
Emulate gdb's behaviour trying to disassemble the function. Task-number: QTCREATORBUG-5205 Change-Id: I2f4dc9393e12324b0423734c5235758a25395fec Reviewed-on: http://codereview.qt.nokia.com/1029 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
This commit is contained in:
@@ -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<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 < offset) {
|
||||
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)
|
||||
{
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QVariantList>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QMultiHash>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QList>
|
||||
@@ -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<quint64> &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<QString, QString> m_fileNameModuleHash;
|
||||
QMultiHash<QString, quint64> m_symbolAddressCache;
|
||||
bool m_ignoreCdbOutput;
|
||||
QVariantList m_customSpecialStopData;
|
||||
QList<SourcePathMapping> m_sourcePathMappings;
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user