Commit 7fbff9c3 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger[New CDB]: Fix disassembly.

Introduce GDBMI-based 'stack' extension command instead of the
builtin 'k' as this does not print the correct instruction pointer.
parent 3e002e6d
......@@ -33,7 +33,6 @@
#include "gdbmihelpers.h"
static const char eventContextC[] = "event";
static const char moduleContextC[] = "module";
// Special exception codes (see dbgwinutils.cpp).
enum { winExceptionCppException = 0xe06d7363,
......@@ -239,9 +238,6 @@ STDMETHODIMP EventCallback::LoadModule(
__in ULONG TimeDateStamp
)
{
ExtensionContext::instance().report('E', 0, moduleContextC, "L:%s:%s:0x%llx:0x%llx\n",
ModuleName, ImageName, BaseOffset, ModuleSize);
return m_wrapped ? m_wrapped->LoadModule(ImageFileHandle, BaseOffset,
ModuleSize, ModuleName, ImageName,
CheckSum, TimeDateStamp) : S_OK;
......@@ -253,9 +249,6 @@ STDMETHODIMP EventCallback::UnloadModule(
__in ULONG64 BaseOffset
)
{
ExtensionContext::instance().report('U', 0, moduleContextC, "U:%s\n",
ImageBaseName);
return m_wrapped ? m_wrapped->UnloadModule(ImageBaseName, BaseOffset) : S_OK;
}
......
......@@ -48,8 +48,18 @@ void StackFrame::formatGDBMI(std::ostream &str, unsigned level) const
{
str << "frame={level=\"" << level << "\",addr=\"0x"
<< std::hex << address << std::dec << '"';
if (!function.empty())
str << ",func=\"" << gdbmiWStringFormat(function) << '"';
if (!function.empty()) {
// Split into module/function
const std::wstring::size_type exclPos = function.find('!');
if (exclPos == std::wstring::npos) {
str << ",func=\"" << gdbmiWStringFormat(function) << '"';
} else {
const std::wstring module = function.substr(0, exclPos);
const std::wstring fn = function.substr(exclPos + 1, function.size() - exclPos - 1);
str << ",func=\"" << gdbmiWStringFormat(fn)
<< "\",from=\"" << gdbmiWStringFormat(module) << '"';
}
}
if (!fullPathName.empty()) { // Creator/gdbmi expects 'clean paths'
std::wstring cleanPath = fullPathName;
replace(cleanPath, L'\\', L'/');
......@@ -559,3 +569,50 @@ std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length,
delete [] buffer;
return str.str();
}
// Format stack as GDBMI
static StackFrames getStackTrace(CIDebugControl *debugControl,
CIDebugSymbols *debugSymbols,
unsigned maxFrames,
std::string *errorMessage)
{
if (!maxFrames)
return StackFrames();
DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[maxFrames];
ULONG frameCount = 0;
const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, maxFrames, &frameCount);
if (FAILED(hr)) {
delete [] frames;
*errorMessage = msgDebugEngineComFailed("GetStackTrace", hr);
}
StackFrames rc(frameCount, StackFrame());
for (ULONG f = 0; f < frameCount; f++)
getFrame(debugSymbols, frames[f], &(rc[f]));
delete [] frames;
return rc;
}
std::string gdbmiStack(CIDebugControl *debugControl,
CIDebugSymbols *debugSymbols,
unsigned maxFrames,
bool humanReadable, std::string *errorMessage)
{
const StackFrames frames = getStackTrace(debugControl, debugSymbols,
maxFrames, errorMessage);
if (frames.empty() && maxFrames > 0)
return std::string();
std::ostringstream str;
str << '[';
const StackFrames::size_type size = frames.size();
for (StackFrames::size_type i = 0; i < size; i++) {
if (i)
str << ',';
frames.at(i).formatGDBMI(str, (int)i);
if (humanReadable)
str << '\n';
}
str << ']';
return str.str();
}
......@@ -51,6 +51,8 @@ struct StackFrame
ULONG line;
};
typedef std::vector<StackFrame> StackFrames;
bool getFrame(unsigned n, StackFrame *f, std::string *errorMessage);
bool getFrame(CIDebugSymbols *debugSymbols, CIDebugControl *debugControl,
unsigned n, StackFrame *f, std::string *errorMessage);
......@@ -145,4 +147,12 @@ std::string gdbmiRegisters(CIDebugRegisters *regs,
std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage);
// Stack helpers
StackFrames getStackTrace(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols,
unsigned maxFrames, std::string *errorMessage);
std::string gdbmiStack(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols,
unsigned maxFrames, bool humanReadable,
std::string *errorMessage);
#endif // THREADLIST_H
......@@ -13,4 +13,5 @@ idle
help
memory
shutdownex
stack
KnownStructOutput
......@@ -351,6 +351,37 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
return S_OK;
}
// Extension command 'stack'
// Report stack correctly as 'k' does not list instruction pointer
// correctly.
extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
{
ExtensionCommandContext exc(Client);
std::string errorMessage;
int token;
bool humanReadable = false;
unsigned maxFrames = 1000;
StringList tokens = commandTokens<StringList>(argsIn, &token);
if (!tokens.empty() && tokens.front() == "-h") {
humanReadable = true;
tokens.pop_front();
}
if (!tokens.empty())
integerFromString(tokens.front(), &maxFrames);
const std::string stack = gdbmiStack(exc.control(), exc.symbols(),
maxFrames, humanReadable, &errorMessage);
if (stack.empty()) {
ExtensionContext::instance().report('N', token, "stack", errorMessage.c_str());
} else {
ExtensionContext::instance().report('R', token, "stack", stack.c_str());
}
return S_OK;
}
// Extension command 'shutdownex' (shutdown is reserved):
// Unhook the output callbacks. This is normally done by the session
// inaccessible notification, however, this does not work for remote-controlled sessions.
......
......@@ -308,8 +308,22 @@ CdbEngine::~CdbEngine()
void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
{
// To be set next time session becomes accessible
m_operateByInstructionPending = operateByInstruction;
if (state() == InferiorStopOk) {
syncOperateByInstruction(operateByInstruction);
} else {
// To be set next time session becomes accessible
m_operateByInstructionPending = operateByInstruction;
}
}
void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
{
if (m_operateByInstruction == operateByInstruction)
return;
QTC_ASSERT(m_accessible, return; )
m_operateByInstruction = operateByInstruction;
postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
}
void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
......@@ -1197,11 +1211,7 @@ void CdbEngine::handleSessionIdle(const QByteArray &message)
stateName(state()), m_specialStopMode);
// Switch source level debugging
if (m_operateByInstructionPending != m_operateByInstruction) {
m_operateByInstruction = m_operateByInstructionPending;
postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
}
syncOperateByInstruction(m_operateByInstructionPending);
const SpecialStopMode specialStopMode = m_specialStopMode;
m_specialStopMode = NoSpecialStop;
......@@ -1704,21 +1714,58 @@ QString CdbEngine::normalizeFileName(const QString &f)
return normalized;
}
void CdbEngine::handleStackTrace(const CdbBuiltinCommandPtr &command)
{
StackFrames frames;
const int current = parseCdbStackTrace(command->reply, &frames);
if (debug)
qDebug("handleStackTrace %d of %d", current, frames.size());
const StackFrames::iterator end = frames.end();
for (StackFrames::iterator it = frames.begin(); it != end; ++it) {
if (!it->file.isEmpty())
it->file = QDir::cleanPath(normalizeFileName(it->file));
// Parse frame from GDBMI. Duplicate of the gdb code, but that
// has more processing.
static StackFrames parseFrames(const QByteArray &data)
{
GdbMi gdbmi;
gdbmi.fromString(data);
if (!gdbmi.isValid())
return StackFrames();
StackFrames rc;
const int count = gdbmi.childCount();
rc.reserve(count);
for (int i = 0; i < count; i++) {
const GdbMi &frameMi = gdbmi.childAt(i);
StackFrame frame;
frame.level = i;
const GdbMi fullName = frameMi.findChild("fullname");
if (fullName.isValid()) {
frame.file = QFile::decodeName(fullName.data());
frame.line = frameMi.findChild("line").data().toInt();
}
frame.function = QLatin1String(frameMi.findChild("func").data());
frame.from = QLatin1String(frameMi.findChild("from").data());
frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
rc.push_back(frame);
}
return rc;
}
stackHandler()->setFrames(frames);
activateFrame(current);
postCommandSequence(command->commandSequence);
void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
{
// Parse frames, find current.
if (command->success) {
int current = -1;
StackFrames frames = parseFrames(command->reply);
const int count = frames.size();
for (int i = 0; i < count; i++) {
if (!frames.at(i).file.isEmpty()) {
frames[i].file = QDir::cleanPath(normalizeFileName(frames.at(i).file));
if (current == -1)
current = i;
}
}
if (count && current == -1) // No usable frame, use assembly.
current = 0;
// Set
stackHandler()->setFrames(frames);
activateFrame(current);
postCommandSequence(command->commandSequence);
} else {
showMessage(command->errorMessage, LogError);
}
}
void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
......@@ -1739,7 +1786,7 @@ void CdbEngine::postCommandSequence(unsigned mask)
return;
}
if (mask & CommandListStack) {
postBuiltinCommand("k", 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
return;
}
if (mask & CommandListRegisters) {
......
......@@ -152,10 +152,11 @@ private:
inline void parseOutputLine(QByteArray line);
inline bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; }
bool canInterruptInferior() const;
void syncOperateByInstruction(bool operateByInstruction);
// Builtin commands
void dummyHandler(const CdbBuiltinCommandPtr &);
void handleStackTrace(const CdbBuiltinCommandPtr &);
void handleStackTrace(const CdbExtensionCommandPtr &);
void handleRegisters(const CdbBuiltinCommandPtr &);
void handleDisassembler(const CdbBuiltinCommandPtr &);
void handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &);
......
......@@ -154,75 +154,6 @@ QVariant cdbIntegerValue(const QByteArray &t)
return converted;
}
/* Parse: 64bit:
\code
Child-SP RetAddr Call Site
00000000`0012a290 00000000`70deb844 QtCored4!QString::QString+0x18 [c:\qt\src\corelib\tools\qstring.h @ 729]
\endcode 32bit:
\code
ChildEBP RetAddr
0012cc68 6714d114 QtCored4!QString::QString+0xf [d:\dev\qt4.7-vs8\qt\src\corelib\tools\qstring.h @ 729]
\endcode */
static inline bool isHexDigit(char c)
{
return std::isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
static inline bool parseStackFrame(QByteArray line, Debugger::Internal::StackFrame *frame)
{
frame->clear();
if (line.isEmpty() || line.startsWith("Child") || !isHexDigit(line.at(0)))
return false;
if (line.endsWith(']')) {
const int sourceFilePos = line.lastIndexOf('[');
const int sepPos = line.lastIndexOf(" @ ");
if (sourceFilePos != -1 && sepPos != -1) {
const QString fileName = QString::fromLocal8Bit(line.mid(sourceFilePos + 1, sepPos - sourceFilePos - 1));
frame->file = QDir::cleanPath(fileName);
frame->line = line.mid(sepPos + 3, line.size() - sepPos - 4).toInt();
line.truncate(sourceFilePos - 1);
}
}
// Split address tokens
const int retAddrPos = line.indexOf(' ');
const int symbolPos = retAddrPos != -1 ? line.indexOf(' ', retAddrPos + 1) : -1;
if (symbolPos == -1)
return false;
// Remove offset off symbol
const int offsetPos = line.lastIndexOf("+0x");
if (offsetPos != -1)
line.truncate(offsetPos);
frame->address = cdbIntegerValue(line.mid(0, retAddrPos)).toULongLong();
// Module!foo
frame->function = QString::fromAscii(line.mid(symbolPos));
const int moduleSep = frame->function.indexOf(QLatin1Char('!'));
if (moduleSep != -1) {
frame->from = frame->function.left(moduleSep);
frame->function.remove(0, moduleSep + 1);
}
return true;
}
int parseCdbStackTrace(const QList<QByteArray> &in, QList<Debugger::Internal::StackFrame> *frames)
{
frames->clear();
Debugger::Internal::StackFrame frame;
frames->reserve(in.size());
int level = 0;
int current = -1;
foreach(const QByteArray &line, in)
if (parseStackFrame(line, &frame)) {
frame.level = level++;
if (current == -1 && frame.isUsable())
current = frames->size();
frames->push_back(frame);
}
return current;
}
/* \code
0:002> ~ [Debugger-Id] Id: <hex pid> <hex tid> Suspends count thread environment block add state name
0 Id: 133c.1374 Suspend: 1 Teb: 000007ff`fffdd000 Unfrozen
......
......@@ -63,9 +63,6 @@ QByteArray fixCdbIntegerValue(QByteArray t, bool stripLeadingZeros = false, int
// Convert a CDB integer value into quint64 or int64
QVariant cdbIntegerValue(const QByteArray &t);
// Parse stack frames and return current
int parseCdbStackTrace(const QList<QByteArray> &in, QList<Debugger::Internal::StackFrame> *frames);
QString debugByteArray(const QByteArray &a);
QString StringFromBase64EncodedUtf16(const QByteArray &a);
......
Supports Markdown
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