Commit 435b4608 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger[CDB]: Add a pretense to breakpoint conditions.

Add the infrastructure for checking on breakpoint conditions
on the QtCreator side (for conditions, bitfield watchpoints).
Have cdb evaluate breakpoint conditions as integer expressions.
parent efed3fd7
......@@ -46,7 +46,7 @@
#include <wdbgexts.h>
#include <dbgeng.h>
typedef IDebugControl CIDebugControl;
typedef IDebugControl3 CIDebugControl;
typedef IDebugSymbols3 CIDebugSymbols;
typedef IDebugSymbolGroup2 CIDebugSymbolGroup;
typedef IDebugClient5 CIDebugClient;
......
......@@ -756,3 +756,54 @@ std::string gdbmiBreakpoints(CIDebugControl *ctrl,
str << ']';
return str.str();
}
std::string msgEvaluateExpressionFailed(const std::string &expression,
const std::string &why)
{
std::ostringstream str;
str << "Failed to evaluate expression '" << expression << "': " << why;
return str.str();
}
bool evaluateExpression(CIDebugControl *control, const std::string expression,
ULONG desiredType, DEBUG_VALUE *v, std::string *errorMessage)
{
// Ensure we are in C++
ULONG oldSyntax;
HRESULT hr = control->GetExpressionSyntax(&oldSyntax);
if (FAILED(hr)) {
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("GetExpressionSyntax", hr));
return false;
}
if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) {
HRESULT hr = control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS);
if (FAILED(hr)) {
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr));
return false;
}
}
hr = control->Evaluate(expression.c_str(), desiredType, v, NULL);
if (FAILED(hr)) {
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("Evaluate", hr));
return false;
}
if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) {
HRESULT hr = control->SetExpressionSyntax(oldSyntax);
if (FAILED(hr)) {
*errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr));
return false;
}
}
return true;
}
bool evaluateInt64Expression(CIDebugControl *control, const std::string expression,
LONG64 *v, std::string *errorMessage)
{
*v= 0;
DEBUG_VALUE dv;
if (!evaluateExpression(control, expression, DEBUG_VALUE_INT64, &dv, errorMessage))
return false;
*v = dv.I64;
return true;
}
......@@ -173,4 +173,10 @@ std::string gdbmiStack(CIDebugControl *debugControl, CIDebugSymbols *debugSymbol
std::string widgetAt(const SymbolGroupValueContext &ctx,
int x, int y, std::string *errorMessage);
bool evaluateExpression(CIDebugControl *control, const std::string expression,
ULONG desiredType, DEBUG_VALUE *v, std::string *errorMessage);
bool evaluateInt64Expression(CIDebugControl *control, const std::string expression,
LONG64 *, std::string *errorMessage);
#endif // THREADLIST_H
......@@ -16,6 +16,7 @@ modules
idle
help
memory
expression
shutdownex
test
stack
......
......@@ -107,6 +107,7 @@ enum Command {
CmdIdle,
CmdHelp,
CmdMemory,
CmdExpression,
CmdStack,
CmdShutdownex,
CmdAddWatch,
......@@ -165,6 +166,7 @@ static const CommandDescription commandDescriptions[] = {
"Intended to be used with .idle_cmd to obtain proper stop notification.",""},
{"help","Prints help.",""},
{"memory","Prints memory contents in Base64 encoding.","[-t token] <address> <length>"},
{"expression","Prints expression value.","[-t token] <expression>"},
{"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"},
{"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""},
{"addwatch","Add watch expression","<iname> <expression>"},
......@@ -924,6 +926,32 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
return S_OK;
}
extern "C" HRESULT CALLBACK expression(CIDebugClient *Client, PCSTR argsIn)
{
ExtensionCommandContext exc(Client);
std::string errorMessage;
LONG64 value = 0;
int token = 0;
do {
const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
if (tokens.size() != 1) {
errorMessage = singleLineUsage(commandDescriptions[CmdExpression]);
break;
}
if (!evaluateInt64Expression(exc.control(), tokens.front(), &value, &errorMessage))
break;
} while (false);
if (errorMessage.empty()) {
ExtensionContext::instance().reportLong('R', token, "expression", toString(value));
} else {
ExtensionContext::instance().report('N', token, 0, "expression", errorMessage.c_str());
}
return S_OK;
}
// Extension command 'stack'
// Report stack correctly as 'k' does not list instruction pointer
// correctly.
......
......@@ -183,11 +183,19 @@ struct MemoryChangeCookie
QByteArray data;
};
struct ConditionalBreakPointCookie
{
ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {}
BreakpointId id;
GdbMi stopReason;
};
} // namespace Internal
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
namespace Debugger {
namespace Internal {
......@@ -1051,6 +1059,7 @@ unsigned CdbEngine::debuggerCapabilities() const
return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
|WatchpointCapability|JumpToLineCapability|AddWatcherCapability
|BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
|BreakConditionCapability|TracePointCapability
|BreakModuleCapability;
}
......@@ -1755,9 +1764,25 @@ enum StopActionFlags
StopShutdownInProgress = 0x80 // Shutdown in progress
};
static inline QString msgTracePointTriggered(BreakpointId id, const int number,
const QString &threadId)
{
return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
.arg(id).arg(number).arg(threadId);
}
static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
const QByteArray &condition,
const QString &threadId)
{
return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
.arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
}
unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
QString *message,
QString *exceptionBoxMessage)
QString *exceptionBoxMessage,
bool conditionalBreakPointTriggered)
{
// Report stop reason (GDBMI)
unsigned rc = 0;
......@@ -1788,7 +1813,22 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
if (breakpointIdG.isValid()) {
id = breakpointIdG.data().toULongLong();
if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
number = breakHandler()->response(id).number;
const BreakpointResponse parameters = breakHandler()->response(id);
// Trace point? Just report.
number = parameters.number;
if (parameters.tracepoint) {
*message = msgTracePointTriggered(id, number, QString::number(threadId));
return StopReportLog|StopIgnoreContinue;
}
// Trigger evaluation of BP expression unless we are already in the response.
if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
*message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
QString::number(threadId));
ConditionalBreakPointCookie cookie(id);
cookie.stopReason = stopReason;
evaluateExpression(parameters.condition, qVariantFromValue(cookie));
return StopReportLog;
}
} else {
id = 0;
}
......@@ -1879,14 +1919,19 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
notifyEngineSetupOk();
return;
}
GdbMi stopReason;
stopReason.fromString(messageBA);
processStop(stopReason, false);
}
void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
{
// Further examine stop and report to user
QString message;
QString exceptionBoxMessage;
GdbMi stopReason;
stopReason.fromString(messageBA);
int forcedThreadId = -1;
const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
conditionalBreakPointTriggered);
// Do the non-blocking log reporting
if (stopFlags & StopReportLog)
showMessage(message, LogMisc);
......@@ -1894,7 +1939,7 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
showStatusMessage(message);
if (stopFlags & StopReportParseError)
showMessage(message, LogError);
// Ignore things like WOW64
// Ignore things like WOW64, report tracepoints.
if (stopFlags & StopIgnoreContinue) {
postCommand("g", 0);
return;
......@@ -2513,6 +2558,41 @@ void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
}
}
void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
{
int value = 0;
if (command->success) {
value = command->reply.toInt();
} else {
showMessage(command->errorMessage, LogError);
}
// Is this a conditional breakpoint?
if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
const QString message = value ?
tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
arg(value).arg(cookie.id) :
tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
arg(cookie.id);
showMessage(message, LogMisc);
// Stop if evaluation is true, else continue
if (value) {
processStop(cookie.stopReason, true);
} else {
postCommand("g", 0);
}
}
}
void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
{
if (exp.contains(' ') && !exp.startsWith('"')) {
exp.prepend('"');
exp.append('"');
}
postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
}
void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
{
postCommandSequence(command->commandSequence);
......
......@@ -191,7 +191,9 @@ private:
bool startConsole(const DebuggerStartParameters &sp, QString *errorMessage);
void init();
unsigned examineStopReason(const GdbMi &stopReason, QString *message,
QString *exceptionBoxMessage);
QString *exceptionBoxMessage,
bool conditionalBreakPointTriggered = false);
void processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered = false);
bool commandsPending() const;
void handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message);
bool doSetupEngine(QString *errorMessage);
......@@ -208,6 +210,7 @@ private:
void syncOperateByInstruction(bool operateByInstruction);
void postWidgetAtCommand();
void handleCustomSpecialStop(const QVariant &v);
void evaluateExpression(QByteArray exp, const QVariant &cookie = QVariant());
// Builtin commands
void dummyHandler(const CdbBuiltinCommandPtr &);
......@@ -215,6 +218,7 @@ private:
void handleRegisters(const CdbBuiltinCommandPtr &);
void handleDisassembler(const CdbBuiltinCommandPtr &);
void handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &);
void handleExpression(const CdbExtensionCommandPtr &);
void jumpToAddress(quint64 address);
// Extension commands
void handleThreads(const CdbExtensionCommandPtr &);
......
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