forked from qt-creator/qt-creator
Debugger: Add Disassembling of functions.
- Add "Disassemble function..." action with dialog for name to stack window. - Add "Disassemble" with function name from code model to the Editor context menu. - Change the engines to be able to disassemble a function without address. Change-Id: I812f4672d97d9a866ee7f5a38dbd18b2876bccfa Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
@@ -1555,24 +1555,32 @@ void CdbEngine::selectThread(int index)
|
||||
// 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
|
||||
/* Called with a stack frame (address and function) or just a function
|
||||
* name from the context menu. When address and function are
|
||||
* passed, try to emulate gdb's behaviour to display the whole function.
|
||||
* CDB's 'u' (disassemble) command takes a symbol,
|
||||
* but displays 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()) {
|
||||
const Location location = agent->location();
|
||||
if (debug)
|
||||
qDebug() << "CdbEngine::fetchDisassembler 0x"
|
||||
<< QString::number(location.address(), 16)
|
||||
<< location.from() << '!' << location.functionName();
|
||||
if (!location.functionName().isEmpty()) {
|
||||
// Resolve function (from stack frame with function and address
|
||||
// or just function from editor).
|
||||
postResolveSymbol(location.from(), location.functionName(), cookie);
|
||||
} else if (location.address()) {
|
||||
// No function, display a default range.
|
||||
postDisassemblerCommand(agent->address(), cookie);
|
||||
postDisassemblerCommand(location.address(), cookie);
|
||||
} else {
|
||||
postResolveSymbol(module, function, cookie);
|
||||
QTC_ASSERT(false, return);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1594,12 +1602,14 @@ void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress,
|
||||
void CdbEngine::postResolveSymbol(const QString &module, const QString &function,
|
||||
const QVariant &cookie)
|
||||
{
|
||||
const QString symbol = module + QLatin1Char('!') + function;
|
||||
QString symbol = module.isEmpty() ? QString(QLatin1Char('*')) : module;
|
||||
symbol += QLatin1Char('!');
|
||||
symbol += function;
|
||||
const QList<quint64> addresses = m_symbolAddressCache.values(symbol);
|
||||
if (addresses.isEmpty()) {
|
||||
QVariantList cookieList;
|
||||
cookieList << QVariant(symbol) << cookie;
|
||||
showMessage(QLatin1String("Resolving symbol: ") + symbol, LogMisc);
|
||||
showMessage(QLatin1String("Resolving symbol: ") + symbol + QLatin1String("..."), LogMisc);
|
||||
postBuiltinCommand(QByteArray("x ") + symbol.toLatin1(), 0,
|
||||
&CdbEngine::handleResolveSymbol, 0,
|
||||
QVariant(cookieList));
|
||||
@@ -1674,6 +1684,26 @@ static inline quint64 findClosestFunctionAddress(const QList<quint64> &addresses
|
||||
return addresses.at(closestIndex);
|
||||
}
|
||||
|
||||
static inline QString msgAmbiguousFunction(const QString &functionName,
|
||||
quint64 address,
|
||||
const QList<quint64> &addresses)
|
||||
{
|
||||
QString result;
|
||||
QTextStream str(&result);
|
||||
str.setIntegerBase(16);
|
||||
str.setNumberFlags(str.numberFlags() | QTextStream::ShowBase);
|
||||
str << "Several overloads of function '" << functionName
|
||||
<< "()' were found (";
|
||||
for (int i = 0; i < addresses.size(); ++i) {
|
||||
if (i)
|
||||
str << ", ";
|
||||
str << addresses.at(i);
|
||||
|
||||
}
|
||||
str << "), using " << address << '.';
|
||||
return result;
|
||||
}
|
||||
|
||||
void CdbEngine::handleResolveSymbol(const QList<quint64> &addresses, const QVariant &cookie)
|
||||
{
|
||||
// Disassembly mode: Determine suitable range containing the
|
||||
@@ -1681,18 +1711,37 @@ void CdbEngine::handleResolveSymbol(const QList<quint64> &addresses, const QVari
|
||||
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;
|
||||
quint64 functionAddress = 0;
|
||||
quint64 endAddress = 0;
|
||||
if (agentAddress) {
|
||||
// We have an address from the agent, find closest.
|
||||
if (const quint64 closest = findClosestFunctionAddress(addresses, agentAddress)) {
|
||||
if (closest <= agentAddress) {
|
||||
functionAddress = closest;
|
||||
endAddress = agentAddress + DisassemblerRange / 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No agent address, disassembly was started with a function name only.
|
||||
if (!addresses.isEmpty()) {
|
||||
functionAddress = addresses.first();
|
||||
endAddress = functionAddress + DisassemblerRange / 2;
|
||||
if (addresses.size() > 1)
|
||||
showMessage(msgAmbiguousFunction(agent->location().functionName(), functionAddress, addresses), LogMisc);
|
||||
}
|
||||
}
|
||||
// Disassemble a function, else use default range around agent address
|
||||
if (functionAddress) {
|
||||
if (const quint64 remainder = endAddress % 8)
|
||||
endAddress += 8 - remainder;
|
||||
postDisassemblerCommand(functionAddress, endAddress, cookie);
|
||||
} else {
|
||||
} else if (agentAddress) {
|
||||
postDisassemblerCommand(agentAddress, cookie);
|
||||
} else {
|
||||
QTC_ASSERT(false, return);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} // DisassemblerAgent
|
||||
}
|
||||
|
||||
// Parse: "00000000`77606060 cc int 3"
|
||||
|
||||
@@ -1022,6 +1022,15 @@ public slots:
|
||||
currentEngine()->executeJumpToLine(data);
|
||||
}
|
||||
|
||||
void slotDisassembleFunction()
|
||||
{
|
||||
const QAction *action = qobject_cast<const QAction *>(sender());
|
||||
QTC_ASSERT(action, return);
|
||||
const StackFrame frame = action->data().value<StackFrame>();
|
||||
QTC_ASSERT(!frame.function.isEmpty(), return);
|
||||
currentEngine()->openDisassemblerView(Location(frame));
|
||||
}
|
||||
|
||||
void handleAddToWatchWindow()
|
||||
{
|
||||
// Requires a selection, but that's the only case we want anyway.
|
||||
@@ -1939,8 +1948,9 @@ void DebuggerPluginPrivate::requestContextMenu(ITextEditor *editor,
|
||||
bool contextUsable = true;
|
||||
|
||||
BreakpointModelId id = BreakpointModelId();
|
||||
const QString fileName = editor->file()->fileName();
|
||||
if (editor->property("DisassemblerView").toBool()) {
|
||||
args.fileName = editor->file()->fileName();
|
||||
args.fileName = fileName;
|
||||
QString line = editor->contents()
|
||||
.section(QLatin1Char('\n'), lineNumber - 1, lineNumber - 1);
|
||||
BreakpointResponse needle;
|
||||
@@ -2031,6 +2041,20 @@ void DebuggerPluginPrivate::requestContextMenu(ITextEditor *editor,
|
||||
connect(jumpToLineAction, SIGNAL(triggered()), SLOT(slotJumpToLine()));
|
||||
menu->addAction(jumpToLineAction);
|
||||
}
|
||||
// Disassemble current function in stopped state.
|
||||
if (currentEngine()->state() == InferiorStopOk
|
||||
&& currentEngine()->hasCapability(DisassemblerCapability)) {
|
||||
StackFrame frame;
|
||||
frame.function = cppFunctionAt(fileName, lineNumber);
|
||||
frame.line = 42; // trick gdb into mixed mode.
|
||||
if (!frame.function.isEmpty()) {
|
||||
const QString text = tr("Disassemble '%1()'").arg(frame.function);
|
||||
QAction *disassembleAction = new QAction(text, menu);
|
||||
disassembleAction->setData(QVariant::fromValue(frame));
|
||||
connect(disassembleAction, SIGNAL(triggered()), SLOT(slotDisassembleFunction()));
|
||||
menu->addAction(disassembleAction );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4325,13 +4325,28 @@ void GdbEngine::fetchDisassemblerByMiRangePlain(const DisassemblerAgentCookie &a
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline QByteArray disassemblerCommand(const Location &location, bool mixed)
|
||||
{
|
||||
QByteArray command = "disassemble ";
|
||||
if (mixed)
|
||||
command += "/m ";
|
||||
if (const quint64 address = location.address()) {
|
||||
command += "0x";
|
||||
command += QByteArray::number(address, 16);
|
||||
} else if (!location.functionName().isEmpty()) {
|
||||
command += location.functionName().toLatin1();
|
||||
} else {
|
||||
QTC_ASSERT(false, return QByteArray(); );
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
void GdbEngine::fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac0)
|
||||
{
|
||||
DisassemblerAgentCookie ac = ac0;
|
||||
QTC_ASSERT(ac.agent, return);
|
||||
const quint64 address = ac.agent->address();
|
||||
QByteArray cmd = "disassemble /m 0x" + QByteArray::number(address, 16);
|
||||
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliPointMixed),
|
||||
postCommand(disassemblerCommand(ac.agent->location(), true), Discardable,
|
||||
CB(handleFetchDisassemblerByCliPointMixed),
|
||||
QVariant::fromValue(ac));
|
||||
}
|
||||
|
||||
@@ -4339,9 +4354,8 @@ void GdbEngine::fetchDisassemblerByCliPointPlain(const DisassemblerAgentCookie &
|
||||
{
|
||||
DisassemblerAgentCookie ac = ac0;
|
||||
QTC_ASSERT(ac.agent, return);
|
||||
const quint64 address = ac.agent->address();
|
||||
QByteArray cmd = "disassemble 0x" + QByteArray::number(address, 16);
|
||||
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliPointPlain),
|
||||
postCommand(disassemblerCommand(ac.agent->location(), false), Discardable,
|
||||
CB(handleFetchDisassemblerByCliPointPlain),
|
||||
QVariant::fromValue(ac));
|
||||
}
|
||||
|
||||
@@ -4463,18 +4477,21 @@ void GdbEngine::handleFetchDisassemblerByCliPointPlain(const GdbResponse &respon
|
||||
{
|
||||
DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
|
||||
QTC_ASSERT(ac.agent, return);
|
||||
|
||||
// Agent address is 0 when disassembling a function name only
|
||||
const quint64 agentAddress = ac.agent->address();
|
||||
if (response.resultClass == GdbResultDone) {
|
||||
DisassemblerLines dlines = parseDisassembler(response);
|
||||
if (dlines.coversAddress(ac.agent->address())) {
|
||||
if (!agentAddress || dlines.coversAddress(agentAddress)) {
|
||||
ac.agent->setContents(dlines);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ac.agent->isMixed())
|
||||
fetchDisassemblerByCliRangeMixed(ac);
|
||||
else
|
||||
fetchDisassemblerByCliRangePlain(ac);
|
||||
if (agentAddress) {
|
||||
if (ac.agent->isMixed())
|
||||
fetchDisassemblerByCliRangeMixed(ac);
|
||||
else
|
||||
fetchDisassemblerByCliRangePlain(ac);
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::handleFetchDisassemblerByCliRangeMixed(const GdbResponse &response)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QContextMenuEvent>
|
||||
#include <QtGui/QHeaderView>
|
||||
#include <QtGui/QInputDialog>
|
||||
#include <QtGui/QMenu>
|
||||
|
||||
namespace Debugger {
|
||||
@@ -91,6 +92,33 @@ void StackWindow::setModel(QAbstractItemModel *model)
|
||||
showAddressColumn(debuggerCore()->action(UseAddressInStackView)->isChecked());
|
||||
}
|
||||
|
||||
// Input a function to be disassembled. Accept CDB syntax
|
||||
// 'Module!function' for module specification
|
||||
|
||||
static inline StackFrame inputFunctionForDisassembly()
|
||||
{
|
||||
StackFrame frame;
|
||||
QInputDialog dialog;
|
||||
dialog.setInputMode(QInputDialog::TextInput);
|
||||
dialog.setLabelText(StackWindow::tr("Function:"));
|
||||
dialog.setWindowTitle(StackWindow::tr("Disassemble Function"));
|
||||
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return frame;
|
||||
const QString function = dialog.textValue();
|
||||
if (function.isEmpty())
|
||||
return frame;
|
||||
const int bangPos = function.indexOf(QLatin1Char('!'));
|
||||
if (bangPos != -1) {
|
||||
frame.from = function.left(bangPos);
|
||||
frame.function = function.mid(bangPos + 1);
|
||||
} else {
|
||||
frame.function = function;
|
||||
}
|
||||
frame.line = 42; // trick gdb into mixed mode.
|
||||
return frame;
|
||||
}
|
||||
|
||||
void StackWindow::contextMenuEvent(QContextMenuEvent *ev)
|
||||
{
|
||||
DebuggerEngine *engine = currentEngine();
|
||||
@@ -124,11 +152,13 @@ void StackWindow::contextMenuEvent(QContextMenuEvent *ev)
|
||||
}
|
||||
|
||||
QAction *actShowDisassemblerAt = 0;
|
||||
QAction *actShowDisassembler = 0;
|
||||
QAction *actShowDisassemblerAtAddress = 0;
|
||||
QAction *actShowDisassemblerAtFunction = 0;
|
||||
|
||||
if (engine->hasCapability(DisassemblerCapability)) {
|
||||
actShowDisassemblerAt = menu.addAction(QString());
|
||||
actShowDisassembler = menu.addAction(tr("Open Disassembler..."));
|
||||
actShowDisassemblerAtAddress = menu.addAction(tr("Open Disassembler at address..."));
|
||||
actShowDisassemblerAtFunction = menu.addAction(tr("Disassemble Function..."));
|
||||
if (address == 0) {
|
||||
actShowDisassemblerAt->setText(tr("Open Disassembler"));
|
||||
actShowDisassemblerAt->setEnabled(false);
|
||||
@@ -165,12 +195,16 @@ void StackWindow::contextMenuEvent(QContextMenuEvent *ev)
|
||||
ml.push_back(MemoryMarkup(address, 1, QColor(Qt::blue).lighter(),
|
||||
tr("Frame #%1 (%2)").arg(row).arg(frame.function)));
|
||||
engine->openMemoryView(address, 0, ml, QPoint(), title);
|
||||
} else if (act == actShowDisassembler) {
|
||||
} else if (act == actShowDisassemblerAtAddress) {
|
||||
AddressDialog dialog;
|
||||
if (address)
|
||||
dialog.setAddress(address);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
currentEngine()->openDisassemblerView(Location(dialog.address()));
|
||||
} else if (act == actShowDisassemblerAtFunction) {
|
||||
const StackFrame frame = inputFunctionForDisassembly();
|
||||
if (!frame.function.isEmpty())
|
||||
currentEngine()->openDisassemblerView(Location(frame));
|
||||
} else if (act == actShowDisassemblerAt)
|
||||
engine->openDisassemblerView(frame);
|
||||
else if (act == actLoadSymbols)
|
||||
|
||||
Reference in New Issue
Block a user