Debugger[New CDB]: Add support for "Select Widget to Watch".

in stopped state. Add helper for executing calls
to ExtensionContext including recording of output in
OutputCallback. Extend symbol resolution to return addresses
as well since QApplication::widgetAt() is ambiguous and needs
to be called by address. Add 'widgetat' extension command
to return the widget.
This commit is contained in:
Friedemann Kleint
2011-01-18 11:40:45 +01:00
parent a471f54240
commit 11c6ca71ac
13 changed files with 329 additions and 37 deletions

View File

@@ -48,7 +48,7 @@ const char *ExtensionContext::stopReasonKeyC = "reason";
ExtensionContext::ExtensionContext() :
m_hookedClient(0),
m_oldEventCallback(0), m_oldOutputCallback(0),
m_creatorEventCallback(0), m_creatorOutputCallback(0)
m_creatorEventCallback(0), m_creatorOutputCallback(0), m_stateNotification(true)
{
}
@@ -81,6 +81,20 @@ void ExtensionContext::hookCallbacks(CIDebugClient *client)
}
}
void ExtensionContext::startRecordingOutput()
{
if (m_creatorOutputCallback) {
m_creatorOutputCallback->startRecording();
} else {
report('X', 0, 0, "Error", "ExtensionContext::startRecordingOutput() called with no output hooked.\n");
}
}
std::wstring ExtensionContext::stopRecordingOutput()
{
return m_creatorOutputCallback ? m_creatorOutputCallback->stopRecording() : std::wstring();
}
void ExtensionContext::setStopReason(const StopReasonMap &r, const std::string &reason)
{
m_stopReason = r;
@@ -163,37 +177,41 @@ static inline ExtensionContext::StopReasonMap
void ExtensionContext::notifyIdle()
{
discardSymbolGroup();
const StopReasonMap stopReasons = completeStopReasons(m_stopReason, executionStatus());
m_stopReason.clear();
// Format
std::ostringstream str;
formatGdbmiHash(str, stopReasons);
reportLong('E', 0, "session_idle", str.str());
if (m_stateNotification) {
const StopReasonMap stopReasons = completeStopReasons(m_stopReason, executionStatus());
// Format
std::ostringstream str;
formatGdbmiHash(str, stopReasons);
reportLong('E', 0, "session_idle", str.str());
}
m_stopReason.clear();
}
void ExtensionContext::notifyState(ULONG Notify)
{
const ULONG ex = executionStatus();
switch (Notify) {
case DEBUG_NOTIFY_SESSION_ACTIVE:
report('E', 0, 0, "session_active", "%u", ex);
break;
case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted
report('E', 0, 0, "session_accessible", "%u", ex);
break;
case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
report('E', 0, 0, "session_inaccessible", "%u", ex);
break;
case DEBUG_NOTIFY_SESSION_INACTIVE:
report('E', 0, 0, "session_inactive", "%u", ex);
if (m_stateNotification) {
switch (Notify) {
case DEBUG_NOTIFY_SESSION_ACTIVE:
report('E', 0, 0, "session_active", "%u", ex);
break;
case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted
report('E', 0, 0, "session_accessible", "%u", ex);
break;
case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
report('E', 0, 0, "session_inaccessible", "%u", ex);
break;
case DEBUG_NOTIFY_SESSION_INACTIVE:
report('E', 0, 0, "session_inactive", "%u", ex);
break;
}
}
if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE) {
discardSymbolGroup();
discardWatchesSymbolGroup();
// We lost the debuggee, at this point restore output.
if (ex & DEBUG_STATUS_NO_DEBUGGEE)
unhookCallbacks();
break;
}
}
@@ -280,6 +298,48 @@ bool ExtensionContext::reportLong(char code, int token, const char *serviceName,
return true;
}
bool ExtensionContext::call(const std::string &functionCall,
std::wstring *output,
std::string *errorMessage)
{
if (!m_creatorOutputCallback) {
*errorMessage = "Attempt to issue a call with no output hooked.";
return false;
}
// Set up arguments
const std::string call = ".call " + functionCall;
HRESULT hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, call.c_str(), DEBUG_EXECUTE_ECHO);
if (FAILED(hr)) {
*errorMessage = msgDebugEngineComFailed("Execute", hr);
return 0;
}
// Execute in current thread. TODO: This must not crash, else we are in an inconsistent state
// (need to call 'gh', etc.)
hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "~. g", DEBUG_EXECUTE_ECHO);
if (FAILED(hr)) {
*errorMessage = msgDebugEngineComFailed("Execute", hr);
return 0;
}
// Wait until finished
startRecordingOutput();
m_stateNotification = false;
m_control->WaitForEvent(0, INFINITE);
*output = stopRecordingOutput();
m_stateNotification = true;
// Crude attempt at recovering from a crash: Issue 'gN' (go with exception not handled).
const bool crashed = output->find(L"This exception may be expected and handled.") != std::string::npos;
if (crashed) {
m_stopReason.clear();
m_stateNotification = false;
hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "~. gN", DEBUG_EXECUTE_ECHO);
m_control->WaitForEvent(0, INFINITE);
m_stateNotification = true;
*errorMessage = "A crash occurred while calling: " + functionCall;
return false;
}
return true;
}
// Exported C-functions
extern "C" {