forked from qt-creator/qt-creator
Debugger[New CDB]: Split up long extension messages.
To accommodate the limitation of output line width of CDB.
This commit is contained in:
@@ -173,7 +173,7 @@ STDMETHODIMP EventCallback::Exception(
|
||||
std::ostringstream str;
|
||||
formatGdbmiHash(str, parameters);
|
||||
ExtensionContext::instance().setStopReason(parameters, "exception");
|
||||
ExtensionContext::instance().report('E', 0, "exception", "%s", str.str().c_str());
|
||||
ExtensionContext::instance().report('E', 0, 0, "exception", "%s", str.str().c_str());
|
||||
return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK;
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ STDMETHODIMP EventCallback::ExitProcess(
|
||||
__in ULONG ExitCode
|
||||
)
|
||||
{
|
||||
ExtensionContext::instance().report('E', 0, eventContextC, "Process exited (%lu)",
|
||||
ExtensionContext::instance().report('E', 0, 0, eventContextC, "Process exited (%lu)",
|
||||
ExitCode);
|
||||
const HRESULT hr = m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK;
|
||||
// Remotely debugged process exited, there is no session-inactive notification.
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include "outputcallback.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
|
||||
// and it's inline functions rely on its existence.
|
||||
WINDBG_EXTENSION_APIS ExtensionApis = {sizeof(WINDBG_EXTENSION_APIS), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
@@ -167,7 +169,7 @@ void ExtensionContext::notifyIdle()
|
||||
// Format
|
||||
std::ostringstream str;
|
||||
formatGdbmiHash(str, stopReasons);
|
||||
report('E', 0, "session_idle", "%s", str.str().c_str());
|
||||
reportLong('E', 0, "session_idle", str.str());
|
||||
m_stopReason.clear();
|
||||
}
|
||||
|
||||
@@ -176,16 +178,16 @@ void ExtensionContext::notifyState(ULONG Notify)
|
||||
const ULONG ex = executionStatus();
|
||||
switch (Notify) {
|
||||
case DEBUG_NOTIFY_SESSION_ACTIVE:
|
||||
report('E', 0, "session_active", "%u", ex);
|
||||
report('E', 0, 0, "session_active", "%u", ex);
|
||||
break;
|
||||
case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted
|
||||
report('E', 0, "session_accessible", "%u", ex);
|
||||
report('E', 0, 0, "session_accessible", "%u", ex);
|
||||
break;
|
||||
case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
|
||||
report('E', 0, "session_inaccessible", "%u", ex);
|
||||
report('E', 0, 0, "session_inaccessible", "%u", ex);
|
||||
break;
|
||||
case DEBUG_NOTIFY_SESSION_INACTIVE:
|
||||
report('E', 0, "session_inactive", "%u", ex);
|
||||
report('E', 0, 0, "session_inactive", "%u", ex);
|
||||
discardSymbolGroup();
|
||||
// We lost the debuggee, at this point restore output.
|
||||
if (ex & DEBUG_STATUS_NO_DEBUGGEE)
|
||||
@@ -218,12 +220,13 @@ void ExtensionContext::discardSymbolGroup()
|
||||
m_symbolGroup.reset();
|
||||
}
|
||||
|
||||
bool ExtensionContext::report(char code, int token, const char *serviceName, PCSTR Format, ...)
|
||||
bool ExtensionContext::report(char code, int token, int remainingChunks, const char *serviceName, PCSTR Format, ...)
|
||||
{
|
||||
if (!isInitialized())
|
||||
return false;
|
||||
// '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'.
|
||||
m_control->Output(DEBUG_OUTPUT_NORMAL, "<qtcreatorcdbext>|%c|%d|%s|", code, token, serviceName);
|
||||
m_control->Output(DEBUG_OUTPUT_NORMAL, "<qtcreatorcdbext>|%c|%d|%d|%s|",
|
||||
code, token, remainingChunks, serviceName);
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
m_control->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
|
||||
@@ -232,6 +235,26 @@ bool ExtensionContext::report(char code, int token, const char *serviceName, PCS
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExtensionContext::reportLong(char code, int token, const char *serviceName, const std::string &message)
|
||||
{
|
||||
const std::string::size_type size = message.size();
|
||||
if (size < outputChunkSize)
|
||||
return report(code, token, 0, serviceName, "%s", message.c_str());
|
||||
// Split up
|
||||
std::string::size_type chunkCount = size / outputChunkSize;
|
||||
if (size % outputChunkSize)
|
||||
chunkCount++;
|
||||
std::string::size_type pos = 0;
|
||||
for (int remaining = int(chunkCount) - 1; remaining >= 0 ; remaining--) {
|
||||
std::string::size_type nextPos = pos + outputChunkSize; // No consistent std::min/std::max in VS8/10
|
||||
if (nextPos > size)
|
||||
nextPos = size;
|
||||
report(code, token, remaining, serviceName, "%s", message.substr(pos, nextPos - pos).c_str());
|
||||
pos = nextPos;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Exported C-functions
|
||||
extern "C" {
|
||||
|
||||
|
||||
@@ -68,11 +68,17 @@ public:
|
||||
// Undo hooking.
|
||||
void unhookCallbacks();
|
||||
|
||||
// Report output in standardized format understood by Qt Creator.
|
||||
// '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'.
|
||||
// Char code is 'R' command reply, 'N' command fail, 'E' event notification
|
||||
bool report(char code, int token, const char *serviceName, PCSTR Format, ...);
|
||||
|
||||
// CDB has a limitation on output, so, long messages need to be split (empirically ca 10KB)
|
||||
static const size_t outputChunkSize = 10240;
|
||||
/* Report output in standardized format understood by Qt Creator.
|
||||
* '<qtcreatorcdbext>|R|<token>|remainingChunks|<serviceName>|<one-line-output>'.
|
||||
* Char code is 'R' command reply, 'N' command fail, 'E' event notification,
|
||||
* 'X' exception, error. If the message is larger than outputChunkSize,
|
||||
* it needs to be split up in chunks, remainingChunks needs to indicate the number
|
||||
* of the following chunks (0 for just one chunk). */
|
||||
bool report(char code, int remainingChunks, int token, const char *serviceName, PCSTR Format, ...);
|
||||
// Convenience for reporting potentially long messages in chunks
|
||||
bool reportLong(char code, int token, const char *serviceName, const std::string &message);
|
||||
ULONG executionStatus() const;
|
||||
// Call from notify handler, tell engine about state.
|
||||
void notifyState(ULONG Notify);
|
||||
|
||||
@@ -97,6 +97,6 @@ STDMETHODIMP OutputCallback::Output(
|
||||
// Base encode as GDBMI is not really made for wide chars
|
||||
std::ostringstream str;
|
||||
base64Encode(str, reinterpret_cast<const unsigned char *>(text), sizeof(wchar_t) * std::wcslen(text));
|
||||
ExtensionContext::instance().report('E', 0, "debuggee_output", str.str().c_str());
|
||||
ExtensionContext::instance().report('E', 0, 0, "debuggee_output", str.str().c_str());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -235,9 +235,9 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
|
||||
commandTokens<StringList>(args, &token);
|
||||
|
||||
if (const ULONG pid = currentProcessId(client)) {
|
||||
ExtensionContext::instance().report('R', token, "pid", "%u", pid);
|
||||
ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid);
|
||||
} else {
|
||||
ExtensionContext::instance().report('N', token, "pid", "0");
|
||||
ExtensionContext::instance().report('N', token, 0, "pid", "0");
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -277,7 +277,7 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
|
||||
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
|
||||
|
||||
if (!symGroup) {
|
||||
ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "expandlocals", errorMessage.c_str());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
|
||||
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) :
|
||||
symGroup->expandList(inames, &errorMessage);
|
||||
|
||||
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
|
||||
ExtensionContext::instance().report('R', token, 0, "expandlocals", "%u/%u %s",
|
||||
succeeded, unsigned(inames.size()), errorMessage.c_str());
|
||||
return S_OK;
|
||||
}
|
||||
@@ -403,9 +403,9 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
|
||||
int token;
|
||||
const std::string output = commmandLocals(exc, args, &token, &errorMessage);
|
||||
if (output.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "locals", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "locals", "%s", output.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "locals", output);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -454,9 +454,9 @@ extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn)
|
||||
int token = 0;
|
||||
const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage);
|
||||
if (value.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "dumplocal", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "dumplocal", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "dumplocal", value.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "dumplocal", value);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -483,9 +483,9 @@ extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args)
|
||||
errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]);
|
||||
}
|
||||
if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) {
|
||||
ExtensionContext::instance().report('R', token, "typecast", "OK");
|
||||
ExtensionContext::instance().report('R', token, 0, "typecast", "OK");
|
||||
} else {
|
||||
ExtensionContext::instance().report('N', token, "typecast", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "typecast", errorMessage.c_str());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -513,9 +513,9 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
|
||||
errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]);
|
||||
}
|
||||
if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) {
|
||||
ExtensionContext::instance().report('R', token, "addsymbol", "OK");
|
||||
ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK");
|
||||
} else {
|
||||
ExtensionContext::instance().report('N', token, "addsymbol", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -554,9 +554,9 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
|
||||
} while (false);
|
||||
|
||||
if (success) {
|
||||
ExtensionContext::instance().report('R', token, "assign", "Ok");
|
||||
ExtensionContext::instance().report('R', token, 0, "assign", "Ok");
|
||||
} else {
|
||||
ExtensionContext::instance().report('N', token, "assign", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "assign", errorMessage.c_str());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -578,9 +578,9 @@ extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR argsIn)
|
||||
exc.advanced(),
|
||||
&errorMessage);
|
||||
if (gdbmi.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "threads", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "threads", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "threads", gdbmi.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "threads", gdbmi);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -598,9 +598,9 @@ extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
|
||||
const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
|
||||
const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage);
|
||||
if (regs.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "registers", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "registers", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "registers", regs.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "registers", regs);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -618,9 +618,9 @@ extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
|
||||
const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
|
||||
const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage);
|
||||
if (modules.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "modules", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "modules", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "modules", modules.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "modules", modules);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -671,11 +671,11 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
|
||||
}
|
||||
|
||||
if (memory.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "memory", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "memory", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "memory", memory.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "memory", memory);
|
||||
if (!errorMessage.empty())
|
||||
ExtensionContext::instance().report('W', token, "memory", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('W', token, 0, "memory", errorMessage.c_str());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -704,9 +704,9 @@ extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
|
||||
maxFrames, humanReadable, &errorMessage);
|
||||
|
||||
if (stack.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "stack", errorMessage.c_str());
|
||||
ExtensionContext::instance().report('N', token, 0, "stack", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "stack", stack.c_str());
|
||||
ExtensionContext::instance().reportLong('R', token, "stack", stack);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -950,7 +950,7 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
|
||||
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, msgDebugEngineComFailed("ExpandSymbol", hr));
|
||||
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str());
|
||||
ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
|
||||
return false;
|
||||
}
|
||||
SymbolGroup::SymbolParameterVector parameters;
|
||||
@@ -1020,12 +1020,12 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name,
|
||||
HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr));
|
||||
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str());
|
||||
ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
|
||||
return 0;
|
||||
}
|
||||
if (index == DEBUG_ANY_ID) { // Occasionally happens for unknown or 'complicated' types
|
||||
*errorMessage = msgCannotAddSymbol(name, "DEBUG_ANY_ID was returned as symbol index by AddSymbol.");
|
||||
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str());
|
||||
ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
|
||||
return 0;
|
||||
}
|
||||
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
|
||||
|
||||
@@ -1585,23 +1585,31 @@ void CdbEngine::parseOutputLine(QByteArray line)
|
||||
// can mix things up.
|
||||
while (isCdbPrompt(line))
|
||||
line.remove(0, CdbPromptLength);
|
||||
// An extension notification
|
||||
// An extension notification (potentially consisting of several chunks)
|
||||
if (line.startsWith(m_creatorExtPrefix)) {
|
||||
// "<qtcreatorcdbext>|type_char|token|serviceName|message"
|
||||
// "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
|
||||
const char type = line.at(m_creatorExtPrefix.size());
|
||||
// integer token
|
||||
const int tokenPos = m_creatorExtPrefix.size() + 2;
|
||||
const int tokenEndPos = line.indexOf('|', tokenPos);
|
||||
QTC_ASSERT(tokenEndPos != -1, return)
|
||||
const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
|
||||
// remainingChunks
|
||||
const int remainingChunksPos = tokenEndPos + 1;
|
||||
const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
|
||||
QTC_ASSERT(remainingChunksEndPos != -1, return)
|
||||
const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
|
||||
// const char 'serviceName'
|
||||
const int whatPos = tokenEndPos + 1;
|
||||
const int whatPos = remainingChunksEndPos + 1;
|
||||
const int whatEndPos = line.indexOf('|', whatPos);
|
||||
QTC_ASSERT(whatEndPos != -1, return)
|
||||
const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
|
||||
// Message
|
||||
const QByteArray message = line.mid(whatEndPos + 1);
|
||||
handleExtensionMessage(type, token, what, message);
|
||||
// Build up buffer, call handler once last chunk was encountered
|
||||
m_extensionMessageBuffer += line.mid(whatEndPos + 1);
|
||||
if (remainingChunks == 0) {
|
||||
handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
|
||||
m_extensionMessageBuffer.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Check for command start/end tokens within which the builtin command
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
bool m_hasDebuggee;
|
||||
QTime m_logTime;
|
||||
mutable int m_elapsedLogTime;
|
||||
QByteArray m_extensionMessageBuffer;
|
||||
};
|
||||
|
||||
} // namespace Cdb
|
||||
|
||||
Reference in New Issue
Block a user