forked from qt-creator/qt-creator
Debugger: Split long cdbextension commands
Fixes: QTCREATORBUG-22922 Change-Id: I5def321f0f97717728bc5cdcd5309b458a8ecfa1 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -191,6 +191,12 @@ static inline bool isOption(const std::string &opt)
|
||||
return opt.size() == 2 && opt.at(0) == '-' && opt != "--";
|
||||
}
|
||||
|
||||
struct CommandToken
|
||||
{
|
||||
int majorPart = 0;
|
||||
int minorPart = 0;
|
||||
};
|
||||
|
||||
// Helper for commandTokens() below:
|
||||
// Simple splitting of command lines allowing for '"'-quoted tokens
|
||||
// 'typecast local.i "class QString *"' -> ('typecast','local.i','class QString *')
|
||||
@@ -251,6 +257,38 @@ static inline void splitCommand(PCSTR args, Inserter it)
|
||||
}
|
||||
}
|
||||
|
||||
// check whether arguments start with a "-t 23.1" or "-t 3" token identifier
|
||||
CommandToken extractToken(PCSTR args, PCSTR *afterToken)
|
||||
{
|
||||
CommandToken token;
|
||||
while (*args == ' ') // skip whitespace
|
||||
++args;
|
||||
if (*args != '-')
|
||||
return {};
|
||||
if (*(++args) != 't')
|
||||
return {};
|
||||
++args;
|
||||
while (*args == ' ') // skip whitespace
|
||||
++args;
|
||||
PSTR end = nullptr;
|
||||
token.majorPart = strtol(args, &end, 10);
|
||||
if (args >= end)
|
||||
return {};
|
||||
args = end;
|
||||
if (*args == '.') {
|
||||
++args;
|
||||
end = nullptr;
|
||||
token.minorPart = strtol(args, &end, 10);
|
||||
if (args >= end)
|
||||
return {};
|
||||
args = end;
|
||||
}
|
||||
while (*args == ' ') // skip whitespace
|
||||
++args;
|
||||
*afterToken = args;
|
||||
return token;
|
||||
}
|
||||
|
||||
// Split & Parse the arguments of a command and extract the
|
||||
// optional first integer token argument ('command -t <number> remaining arguments')
|
||||
// Each command takes the 'token' argument and includes it in its output
|
||||
@@ -259,18 +297,25 @@ static inline void splitCommand(PCSTR args, Inserter it)
|
||||
template<class StringContainer>
|
||||
static inline StringContainer commandTokens(PCSTR args, int *token = 0)
|
||||
{
|
||||
typedef typename StringContainer::iterator ContainerIterator;
|
||||
static std::map<int, std::string> s_commandBuffer;
|
||||
|
||||
if (token)
|
||||
*token = -1; // Handled as 'display' in engine, so that user can type commands
|
||||
|
||||
StringContainer tokens;
|
||||
splitCommand(args, std::back_inserter(tokens));
|
||||
// Check for token
|
||||
ContainerIterator it = tokens.begin();
|
||||
if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
|
||||
if (token)
|
||||
std::istringstream(*it) >> *token;
|
||||
tokens.erase(tokens.begin(), ++it);
|
||||
CommandToken commandToken = extractToken(args, &args);
|
||||
if (commandToken.majorPart != 0) {
|
||||
s_commandBuffer[commandToken.majorPart].append(args);
|
||||
if (commandToken.minorPart == 0) {
|
||||
DebugPrint() << commandToken.majorPart << ':' << s_commandBuffer[commandToken.majorPart];
|
||||
splitCommand(s_commandBuffer[commandToken.majorPart].data(), std::back_inserter(tokens));
|
||||
if (token)
|
||||
*token = commandToken.majorPart;
|
||||
} else if (token) {
|
||||
*token = 0;
|
||||
}
|
||||
} else {
|
||||
splitCommand(args, std::back_inserter(tokens));
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
@@ -283,6 +328,8 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
|
||||
|
||||
int token;
|
||||
commandTokens<StringList>(args, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
dprintf("Qt Creator CDB extension version 4.3 %d bit.\n",
|
||||
sizeof(void *) * 8);
|
||||
if (const ULONG pid = currentProcessId(client))
|
||||
@@ -304,6 +351,9 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
|
||||
|
||||
int token;
|
||||
StringList tokens = commandTokens<StringList>(args, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
StringVector inames;
|
||||
bool runComplexDumpers = false;
|
||||
do {
|
||||
@@ -423,10 +473,12 @@ static std::string commandLocals(ExtensionCommandContext &commandExtCtx,PCSTR ar
|
||||
typedef InameExpressionMap::value_type InameExpressionMapEntry;
|
||||
|
||||
// Parse the command
|
||||
StringList tokens = commandTokens<StringList>(args, token);
|
||||
if (token == 0) // partial arguments
|
||||
return {};
|
||||
ExtensionContext &extCtx = ExtensionContext::instance();
|
||||
DumpCommandParameters parameters;
|
||||
std::string iname;
|
||||
StringList tokens = commandTokens<StringList>(args, token);
|
||||
StringVector expandedInames;
|
||||
StringVector uninitializedInames;
|
||||
InameExpressionMap watcherInameExpressionMap;
|
||||
@@ -577,8 +629,11 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn)
|
||||
ExtensionCommandContext exc(client);
|
||||
int token;
|
||||
#ifdef WITH_PYTHON
|
||||
const StringList args = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial arguments
|
||||
return {};
|
||||
std::stringstream command;
|
||||
for (std::string arg : commandTokens<StringList>(argsIn, &token))
|
||||
for (std::string arg : args)
|
||||
command << arg << ' ';
|
||||
|
||||
PyObject *ptype = NULL;
|
||||
@@ -594,6 +649,8 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn)
|
||||
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||
#else
|
||||
commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial arguments
|
||||
return {};
|
||||
ExtensionContext::instance().report('N', token, 0, "script",
|
||||
"Python is not supported in this CDB extension.\n"
|
||||
"You need to define PYTHON_INSTALL_DIR in your creator build environment "
|
||||
@@ -608,6 +665,8 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
|
||||
std::string errorMessage;
|
||||
int token;
|
||||
const std::string output = commandLocals(exc, args, &token, &errorMessage);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
SymbolGroupValue::verbose = 0;
|
||||
if (output.empty())
|
||||
ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
|
||||
@@ -625,6 +684,9 @@ static std::string commmandWatches(ExtensionCommandContext &exc, PCSTR args, int
|
||||
// Parse the command
|
||||
DumpCommandParameters parameters;
|
||||
StringList tokens = commandTokens<StringList>(args, token);
|
||||
if (token == 0) // partial message
|
||||
return {};
|
||||
|
||||
// Parse away options
|
||||
for (bool optionLeft = true; optionLeft && !tokens.empty(); ) {
|
||||
switch (parameters.parseOption(&tokens)) {
|
||||
@@ -662,6 +724,9 @@ extern "C" HRESULT CALLBACK watches(CIDebugClient *client, PCSTR args)
|
||||
std::string errorMessage = "e";
|
||||
int token = 0;
|
||||
const std::string output = commmandWatches(exc, args, &token, &errorMessage);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
SymbolGroupValue::verbose = 0;
|
||||
if (output.empty())
|
||||
ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
|
||||
@@ -677,6 +742,9 @@ static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int
|
||||
{
|
||||
// Parse the command
|
||||
StringList tokens = commandTokens<StringList>(args, token);
|
||||
if (token == 0) // partial message
|
||||
return {};
|
||||
|
||||
// Frame and iname
|
||||
unsigned frame;
|
||||
if (tokens.empty() || integerFromString(tokens.front(), &frame)) {
|
||||
@@ -714,6 +782,9 @@ extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn)
|
||||
std::string errorMessage;
|
||||
int token = 0;
|
||||
const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (value.empty())
|
||||
ExtensionContext::instance().report('N', token, 0, "dumplocal", errorMessage.c_str());
|
||||
else
|
||||
@@ -733,6 +804,9 @@ extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args)
|
||||
|
||||
int token;
|
||||
const StringVector tokens = commandTokens<StringVector>(args, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
std::string iname;
|
||||
std::string desiredType;
|
||||
if (tokens.size() == 3u && integerFromString(tokens.front(), &frame)) {
|
||||
@@ -761,6 +835,9 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
|
||||
|
||||
int token;
|
||||
const StringVector tokens = commandTokens<StringVector>(args, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
std::string name;
|
||||
std::string iname;
|
||||
if (tokens.size() >= 2u && integerFromString(tokens.front(), &frame)) {
|
||||
@@ -790,6 +867,9 @@ extern "C" HRESULT CALLBACK addwatch(CIDebugClient *client, PCSTR argsIn)
|
||||
bool success = false;
|
||||
do {
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (tokens.size() != 2) {
|
||||
errorMessage = singleLineUsage(commandDescriptions[CmdAddWatch]);
|
||||
break;
|
||||
@@ -824,6 +904,9 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
|
||||
int token = 0;
|
||||
do {
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (tokens.empty()) {
|
||||
errorMessage = singleLineUsage(commandDescriptions[CmdAssign]);
|
||||
break;
|
||||
@@ -883,6 +966,8 @@ extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR argsIn)
|
||||
|
||||
int token;
|
||||
commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
const std::string gdbmi = gdbmiThreadList(exc.systemObjects(),
|
||||
exc.symbols(),
|
||||
@@ -906,6 +991,9 @@ extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
|
||||
|
||||
int token;
|
||||
const StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
|
||||
const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage);
|
||||
if (regs.empty())
|
||||
@@ -925,6 +1013,9 @@ extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
|
||||
|
||||
int token;
|
||||
const StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
|
||||
const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage);
|
||||
if (modules.empty())
|
||||
@@ -949,6 +1040,9 @@ extern "C" HRESULT CALLBACK setparameter(CIDebugClient *, PCSTR args)
|
||||
{
|
||||
int token;
|
||||
StringVector tokens = commandTokens<StringVector>(args, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
const size_t count = tokens.size();
|
||||
size_t success = 0;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
@@ -1014,6 +1108,9 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
|
||||
ULONG length = 0;
|
||||
|
||||
const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (tokens.size() == 2
|
||||
&& integerFromString(tokens.front(), &address)
|
||||
&& integerFromString(tokens.at(1), &length)) {
|
||||
@@ -1041,6 +1138,9 @@ extern "C" HRESULT CALLBACK expression(CIDebugClient *Client, PCSTR argsIn)
|
||||
|
||||
do {
|
||||
const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (tokens.size() != 1) {
|
||||
|
||||
errorMessage = singleLineUsage(commandDescriptions[CmdExpression]);
|
||||
@@ -1070,6 +1170,9 @@ extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
|
||||
unsigned maxFrames = ExtensionContext::instance().parameters().maxStackDepth;
|
||||
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (!tokens.empty() && tokens.front() == "-h") {
|
||||
humanReadable = true;
|
||||
tokens.pop_front();
|
||||
@@ -1106,6 +1209,9 @@ extern "C" HRESULT CALLBACK qmlstack(CIDebugClient *client, PCSTR argsIn)
|
||||
|
||||
do {
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (!tokens.empty() && tokens.front() == "-h") {
|
||||
humanReadable = true;
|
||||
tokens.pop_front();
|
||||
@@ -1170,6 +1276,9 @@ extern "C" HRESULT CALLBACK widgetat(CIDebugClient *client, PCSTR argsIn)
|
||||
int y = -1;
|
||||
|
||||
const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
if (tokens.size() != 2) {
|
||||
errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]);
|
||||
break;
|
||||
@@ -1197,6 +1306,9 @@ extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn)
|
||||
bool humanReadable = false;
|
||||
unsigned verbose = 0;
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
|
||||
switch (tokens.front().at(1)) {
|
||||
case 'h':
|
||||
@@ -1226,6 +1338,9 @@ extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn)
|
||||
Mode mode = Invalid;
|
||||
int token = 0;
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (token == 0) // partial message
|
||||
return S_OK;
|
||||
|
||||
// Parse away options
|
||||
while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
|
||||
const char option = tokens.front().at(1);
|
||||
|
@@ -959,6 +959,13 @@ void CdbEngine::executeDebuggerCommand(const QString &command)
|
||||
// Post command to the cdb process
|
||||
void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
||||
{
|
||||
constexpr int maxCommandLength = 4096;
|
||||
constexpr int maxTokenLength = 4 /*" -t "*/
|
||||
+ 5 /* 99999 tokens should be enough for a single qc run time*/
|
||||
+ 1 /* token part splitter '.' */
|
||||
+ 3 /* 1000 parts should also be more than enough */
|
||||
+ 1 /* final space */;
|
||||
|
||||
QString cmd = dbgCmd.function + dbgCmd.argsToString();
|
||||
if (!m_accessible) {
|
||||
doInterruptInferior([this, dbgCmd](){
|
||||
@@ -970,11 +977,26 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbgCmd.flags == ScriptCommand) {
|
||||
// repack script command into an extension command
|
||||
DebuggerCommand newCmd("script", ExtensionCommand, dbgCmd.callback);
|
||||
if (!dbgCmd.args.isNull())
|
||||
newCmd.args = QString{dbgCmd.function + '(' + dbgCmd.argsToPython() + ')'};
|
||||
else
|
||||
newCmd.args = dbgCmd.function;
|
||||
runCommand(newCmd);
|
||||
return;
|
||||
}
|
||||
|
||||
QString fullCmd;
|
||||
if (dbgCmd.flags == NoFlags) {
|
||||
fullCmd = cmd;
|
||||
fullCmd = cmd + '\n';
|
||||
if (fullCmd.length() > maxCommandLength) {
|
||||
showMessage("Command is longer than 4096 characters execution will likely fail.",
|
||||
LogWarning);
|
||||
}
|
||||
} else {
|
||||
const int token = m_nextCommandToken++;
|
||||
const int token = ++m_nextCommandToken;
|
||||
StringInputStream str(fullCmd);
|
||||
if (dbgCmd.flags == BuiltinCommand) {
|
||||
// Post a built-in-command producing free-format output with a callback.
|
||||
@@ -982,23 +1004,35 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
||||
// printing a specially formatted token to be identifiable in the output.
|
||||
str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
|
||||
<< cmd << "\n"
|
||||
<< ".echo \"" << m_tokenPrefix << token << ">\"";
|
||||
<< ".echo \"" << m_tokenPrefix << token << ">\"" << '\n';
|
||||
if (fullCmd.length() > maxCommandLength) {
|
||||
showMessage("Command is longer than 4096 characters execution will likely fail.",
|
||||
LogWarning);
|
||||
}
|
||||
} else if (dbgCmd.flags == ExtensionCommand) {
|
||||
|
||||
// Post an extension command producing one-line output with a callback,
|
||||
// pass along token for identification in hash.
|
||||
str << m_extensionCommandPrefix << dbgCmd.function << "%1%2";
|
||||
if (dbgCmd.args.isString())
|
||||
str << ' ' << dbgCmd.argsToString();
|
||||
cmd = fullCmd.arg("", "");
|
||||
fullCmd = fullCmd.arg(" -t ").arg(token);
|
||||
} else if (dbgCmd.flags == ScriptCommand) {
|
||||
// Add extension prefix and quotes the script command
|
||||
// pass along token for identification in hash.
|
||||
str << m_extensionCommandPrefix + "script %1%2 " << dbgCmd.function;
|
||||
if (!dbgCmd.args.isNull())
|
||||
str << '(' << dbgCmd.argsToPython() << ')';
|
||||
cmd = fullCmd.arg("", "");
|
||||
fullCmd = fullCmd.arg(" -t ").arg(token);
|
||||
const QString prefix = m_extensionCommandPrefix + dbgCmd.function;
|
||||
QList<QStringRef> splittedArguments;
|
||||
if (dbgCmd.args.isString()) {
|
||||
const QString &arguments = dbgCmd.argsToString();
|
||||
cmd = prefix + arguments;
|
||||
int argumentSplitPos = 0;
|
||||
QList<QStringRef> splittedArguments;
|
||||
int maxArgumentSize = maxCommandLength - prefix.length() - maxTokenLength;
|
||||
while (argumentSplitPos < arguments.size()) {
|
||||
splittedArguments << arguments.midRef(argumentSplitPos, maxArgumentSize);
|
||||
argumentSplitPos += splittedArguments.last().length();
|
||||
}
|
||||
QTC_CHECK(argumentSplitPos == arguments.size());
|
||||
int tokenPart = splittedArguments.size();
|
||||
for (const QStringRef part : qAsConst(splittedArguments))
|
||||
str << prefix << " -t " << token << '.' << --tokenPart << ' ' << part << '\n';
|
||||
} else {
|
||||
cmd = prefix;
|
||||
str << prefix << " -t " << token << '.' << 0 << '\n';
|
||||
}
|
||||
}
|
||||
m_commandForToken.insert(token, dbgCmd);
|
||||
}
|
||||
@@ -1011,7 +1045,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
||||
qDebug("CdbEngine::postCommand: resulting command '%s'\n", qPrintable(fullCmd));
|
||||
}
|
||||
showMessage(cmd, LogInput);
|
||||
m_process.write(fullCmd.toLocal8Bit() + '\n');
|
||||
m_process.write(fullCmd.toLocal8Bit());
|
||||
}
|
||||
|
||||
void CdbEngine::activateFrame(int index)
|
||||
|
@@ -42,6 +42,7 @@ public:
|
||||
StringInputStream &operator<<(char a) { m_target.append(a); return *this; }
|
||||
StringInputStream &operator<<(const char *a) { m_target.append(QString::fromUtf8(a)); return *this; }
|
||||
StringInputStream &operator<<(const QString &a) { m_target.append(a); return *this; }
|
||||
StringInputStream &operator<<(const QStringRef &a) { m_target.append(a); return *this; }
|
||||
|
||||
StringInputStream &operator<<(int i) { appendInt(i); return *this; }
|
||||
StringInputStream &operator<<(unsigned i) { appendInt(i); return *this; }
|
||||
|
Reference in New Issue
Block a user