Commit c27a463f authored by Friedemann Kleint's avatar Friedemann Kleint Committed by hjk

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: default avatarhjk <qthjk@ovi.com>
parent ccf7caec
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment