Commit d5707e0e authored by Ulf Hermann's avatar Ulf Hermann
Browse files

Debugger: Use Qt's JSON encoder for debugger protocol



The V4 debug service expects correct JSON as input and gdb, lldb, and
pdb expect Python object literals. There is a subset of JSON that is
also valid as Python object literals and we use that for the protocol
spoken with gdb, lldb, and pdb. The strings passed to CDB are tunneled
through JSON strings and converted to byte arrays before sending them.

Change-Id: I87319b5450e5c3c3b29c565b75cddaa612767611
Task-number: QTCREATORBUG-14931
Reviewed-by: default avatarhjk <hjk@theqtcompany.com>
parent 7d3bb6fd
......@@ -1174,7 +1174,7 @@ void CdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages
// Post command to the cdb process
void CdbEngine::runCommand(const DebuggerCommand &dbgCmd, int flags)
{
QByteArray cmd = dbgCmd.function + dbgCmd.arguments();
QByteArray cmd = dbgCmd.function + dbgCmd.argsToString();
if (!m_accessible) {
const QString msg = QString::fromLatin1("Attempt to issue command \"%1\" to non-accessible session (%2)")
.arg(QString::fromLocal8Bit(cmd), QString::fromLatin1(stateName(state())));
......@@ -1197,8 +1197,8 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd, int flags)
// pass along token for identification in hash.
const int token = m_nextCommandToken++;
str << m_extensionCommandPrefixBA << dbgCmd.function << " -t " << token;
if (!dbgCmd.args.isEmpty())
str << ' ' << dbgCmd.args;
if (dbgCmd.args.isString())
str << ' ' << dbgCmd.argsToString();
m_commandForToken.insert(token, dbgCmd);
} else {
str << cmd;
......@@ -1332,7 +1332,7 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
str << blankSeparator << updateParameters.partialVariable;
DebuggerCommand cmd("locals");
cmd.args = arguments;
cmd.args = QLatin1String(arguments);
cmd.callback = [this, partialUpdate](const DebuggerResponse &r) { handleLocals(r, partialUpdate); };
runCommand(cmd, ExtensionCommand);
}
......@@ -1557,8 +1557,10 @@ void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, q
void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
{
DebuggerCommand cmd("memory");
ByteArrayInputStream str(cmd.args);
QByteArray args;
ByteArrayInputStream str(args);
str << cookie.address << ' ' << cookie.length;
cmd.args = QLatin1String(args);
cmd.callback = [this, cookie](const DebuggerResponse &response) {
if (response.resultClass == ResultDone && cookie.agent) {
const QByteArray data = QByteArray::fromBase64(response.data.data());
......@@ -1619,7 +1621,7 @@ void CdbEngine::reloadFullStack()
if (debug)
qDebug("%s", Q_FUNC_INFO);
DebuggerCommand cmd("stack");
cmd.args = "unlimited";
cmd.args = QStringLiteral("unlimited");
cmd.callback = CB(handleStackTrace);
runCommand(cmd, ExtensionCommand);
}
......@@ -1627,7 +1629,7 @@ void CdbEngine::reloadFullStack()
void CdbEngine::listBreakpoints()
{
DebuggerCommand cmd("breakpoints");
cmd.args = "-v";
cmd.args = QStringLiteral("-v");
cmd.callback = CB(handleBreakPoints);
runCommand(cmd, ExtensionCommand);
}
......@@ -1845,11 +1847,12 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
QString::number(threadId));
DebuggerCommand cmd("expression");
cmd.args = parameters.condition;
if (cmd.args.contains(' ') && !cmd.args.startsWith('"')) {
cmd.args.prepend('"');
cmd.args.append('"');
QByteArray args = parameters.condition;
if (args.contains(' ') && !args.startsWith('"')) {
args.prepend('"');
args.append('"');
}
cmd.args = QLatin1String(args);
cmd.callback = [this, id, stopReason](const DebuggerResponse &response) {
handleExpression(response, id, stopReason);
};
......@@ -3092,7 +3095,7 @@ void CdbEngine::watchPoint(const QPoint &p)
void CdbEngine::postWidgetAtCommand()
{
DebuggerCommand cmd("widgetat");
cmd.args = QByteArray::number(m_watchPointX) + ' ' + QByteArray::number(m_watchPointY);
cmd.args = QString::fromLatin1("%1 %2").arg(m_watchPointX, m_watchPointY);
cmd.callback = CB(handleWidgetAt);
runCommand(cmd, ExtensionCommand);
}
......
......@@ -36,6 +36,8 @@
#include <QHostAddress>
#include <QRegExp>
#include <QTimeZone>
#include <QJsonArray>
#include <QJsonDocument>
#include <ctype.h>
......@@ -779,106 +781,80 @@ QString decodeData(const QByteArray &ba, DebuggerEncoding encoding)
//
//////////////////////////////////////////////////////////////////////////////////
void DebuggerCommand::argHelper(const char *name, const QByteArray &data)
template<typename Value>
QJsonValue addToJsonObject(const QJsonValue &args, const char *name, const Value &value)
{
args.append('"');
args.append(name);
args.append("\":");
args.append(data);
args.append(",");
QTC_ASSERT(args.isObject() || args.isNull(), return args);
QJsonObject obj = args.toObject();
obj.insert(QLatin1String(name), value);
return obj;
}
void DebuggerCommand::arg(const char *name, int value)
{
argHelper(name, QByteArray::number(value));
args = addToJsonObject(args, name, value);
}
void DebuggerCommand::arg(const char *name, qlonglong value)
{
argHelper(name, QByteArray::number(value));
args = addToJsonObject(args, name, value);
}
void DebuggerCommand::arg(const char *name, qulonglong value)
{
argHelper(name, QByteArray::number(value));
// gdb and lldb will correctly cast the value back to unsigned if needed, so this is no problem.
args = addToJsonObject(args, name, qint64(value));
}
void DebuggerCommand::arg(const char *name, const QString &value)
{
arg(name, value.toUtf8().data());
args = addToJsonObject(args, name, value);
}
void DebuggerCommand::arg(const char *name, const QByteArray &value)
{
arg(name, value.data());
args = addToJsonObject(args, name, QLatin1String(value));
}
void DebuggerCommand::arg(const char *name, const char *value)
{
args.append('"');
args.append(name);
args.append("\":\"");
args.append(value);
args.append("\",");
args = addToJsonObject(args, name, QLatin1String(value));
}
void DebuggerCommand::arg(const char *name, const QList<int> &list)
{
beginList(name);
foreach (int item, list) {
args.append(QByteArray::number(item));
args.append(',');
}
endList();
QJsonArray numbers;
foreach (int item, list)
numbers.append(item);
args = addToJsonObject(args, name, numbers);
}
void DebuggerCommand::arg(const char *value)
{
args.append("\"");
args.append(value);
args.append("\",");
}
void DebuggerCommand::beginList(const char *name)
{
if (name) {
args += '"';
args += name;
args += "\":";
}
args += '[';
QTC_ASSERT(args.isArray() || args.isNull(), return);
QJsonArray arr = args.toArray();
arr.append(QLatin1String(value));
args = arr;
}
void DebuggerCommand::endList()
void DebuggerCommand::arg(const char *name, const QJsonValue &value)
{
if (args.endsWith(','))
args.chop(1);
args += "],";
args = addToJsonObject(args, name, value);
}
void DebuggerCommand::beginGroup(const char *name)
QByteArray DebuggerCommand::argsToPython() const
{
if (name) {
args += '"';
args += name;
args += "\":";
}
args += '{';
}
// TODO: Verify that this is really Python.
void DebuggerCommand::endGroup()
{
if (args.endsWith(','))
args.chop(1);
args += "},";
if (args.isArray())
return QJsonDocument(args.toArray()).toJson(QJsonDocument::Compact);
else
return QJsonDocument(args.toObject()).toJson(QJsonDocument::Compact);
}
QByteArray DebuggerCommand::arguments() const
QByteArray DebuggerCommand::argsToString() const
{
QByteArray result = args;
if (result.endsWith(','))
result.chop(1);
return result;
return args.toString().toLatin1();
}
} // namespace Internal
......
......@@ -34,6 +34,8 @@
#include <QByteArray>
#include <QList>
#include <QString>
#include <QJsonValue>
#include <QJsonObject>
#include <functional>
#include <vector>
......@@ -53,7 +55,7 @@ public:
DebuggerCommand(const char *f, int flags = 0, Callback cb = Callback())
: function(f), callback(cb), flags(flags)
{}
DebuggerCommand(const char *f, const char *a, int flags = 0, Callback cb = Callback())
DebuggerCommand(const char *f, const QJsonValue &a, int flags = 0, Callback cb = Callback())
: function(f), args(a), callback(cb), flags(flags)
{}
DebuggerCommand(const char *f, Callback cb)
......@@ -61,7 +63,7 @@ public:
{}
DebuggerCommand(const QByteArray &f) : function(f), flags(0) {}
void arg(const char *name);
void arg(const char *value);
void arg(const char *name, int value);
void arg(const char *name, qlonglong value);
void arg(const char *name, qulonglong value);
......@@ -69,21 +71,16 @@ public:
void arg(const char *name, const QByteArray &value);
void arg(const char *name, const char *value);
void arg(const char *name, const QList<int> &list);
void arg(const char *name, const QJsonValue &value);
void beginList(const char *name = 0);
void endList();
void beginGroup(const char *name = 0);
void endGroup();
QByteArray arguments() const;
QByteArray argsToPython() const;
QByteArray argsToString() const;
QByteArray function;
QByteArray args;
QJsonValue args;
Callback callback;
uint postTime; // msecsSinceStartOfDay
int flags;
private:
void argHelper(const char *name, const QByteArray &value);
};
/*
......
......@@ -84,6 +84,7 @@
#include <QProcess>
#include <QPushButton>
#include <QTemporaryFile>
#include <QJsonArray>
using namespace Core;
using namespace ProjectExplorer;
......@@ -874,7 +875,7 @@ void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
void GdbEngine::runCommand(const DebuggerCommand &command)
{
QByteArray cmd = command.function + "({" + command.args + "})";
QByteArray cmd = command.function + "(" + command.argsToPython() + ")";
postCommand("python theDumper." + cmd, command.flags, command.callback);
}
......
......@@ -66,6 +66,7 @@
#include <QTimer>
#include <QToolTip>
#include <QVariant>
#include <QJsonArray>
using namespace Core;
using namespace Utils;
......@@ -136,7 +137,7 @@ void LldbEngine::runCommand(const DebuggerCommand &command_)
DebuggerCommand command = command_;
command.arg("token", tok);
QByteArray token = QByteArray::number(tok);
QByteArray cmd = command.function + "({" + command.args + "})";
QByteArray cmd = command.function + "(" + command.argsToPython() + ")";
showMessage(_(token + cmd + '\n'), LogInput);
m_commandForToken[currentToken()] = command;
m_lldbProc.write("script theDumper." + cmd + "\n");
......@@ -335,10 +336,10 @@ void LldbEngine::setupInferior()
cmd2.arg("useTerminal", rp.useTerminal);
cmd2.arg("startMode", rp.startMode);
cmd2.beginList("processArgs");
QJsonArray processArgs;
foreach (const QString &arg, args.toUnixArgs())
cmd2.arg(arg.toUtf8().toHex());
cmd2.endList();
processArgs.append(QLatin1String(arg.toUtf8().toHex()));
cmd2.arg("processArgs", processArgs);
if (rp.useTerminal) {
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
......@@ -830,7 +831,7 @@ void LldbEngine::doUpdateLocals(const UpdateParameters &params)
//cmd.arg("resultvarname", m_resultVarName);
m_lastDebuggableCommand = cmd;
m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1");
m_lastDebuggableCommand.arg("passexceptions", 0);
cmd.callback = [this](const DebuggerResponse &response) {
updateLocalsView(response.data);
......
......@@ -62,6 +62,7 @@
#include <QFileInfo>
#include <QTimer>
#include <QVariant>
#include <QJsonArray>
using namespace Core;
......@@ -97,7 +98,7 @@ void PdbEngine::postDirectCommand(const QByteArray &command)
void PdbEngine::runCommand(const DebuggerCommand &cmd)
{
QTC_ASSERT(m_proc.state() == QProcess::Running, notifyEngineIll());
QByteArray command = "qdebug('" + cmd.function + "',{" + cmd.args + "})";
QByteArray command = "qdebug('" + cmd.function + "'," + cmd.argsToPython() + ")";
showMessage(_(command), LogInput);
m_proc.write(command + '\n');
}
......
......@@ -1733,14 +1733,15 @@ QmlV8ObjectData QmlEnginePrivate::extractData(const QVariant &data) const
void QmlEnginePrivate::runCommand(const DebuggerCommand &command, const QmlCallback &cb)
{
QByteArray msg = "{\"seq\":" + QByteArray::number(++sequence) + ","
+ "\"type\":\"request\","
+ "\"command\":\"" + command.function + "\","
+ "\"arguments\":{" + command.arguments() + "}}";
QJsonObject object;
object.insert(QStringLiteral("seq"), ++sequence);
object.insert(QStringLiteral("type"), QStringLiteral("request"));
object.insert(QStringLiteral("command"), QLatin1String(command.function));
object.insert(QStringLiteral("arguments"), command.args);
if (cb)
callbackForToken[sequence] = cb;
runDirectCommand(V8REQUEST, msg);
runDirectCommand(V8REQUEST, QJsonDocument(object).toJson(QJsonDocument::Compact));
}
void QmlEnginePrivate::runDirectCommand(const QByteArray &type, const QByteArray &msg)
......
......@@ -58,6 +58,8 @@
#include <QProcess>
#include <QTabWidget>
#include <QTextEdit>
#include <QJsonArray>
#include <QJsonObject>
#include <QTimer>
#include <algorithm>
......@@ -1591,57 +1593,55 @@ QByteArray WatchHandler::individualFormatRequests() const
void WatchHandler::appendFormatRequests(DebuggerCommand *cmd)
{
cmd->beginList("expanded");
QJsonArray expanded;
QSetIterator<QByteArray> jt(m_model->m_expandedINames);
while (jt.hasNext()) {
QByteArray iname = jt.next();
//WatchItem *item = m_model->findItem(iname);
cmd->arg(iname);
//cmd->arg("format", item->requestedFormat());
}
cmd->endList();
while (jt.hasNext())
expanded.append(QLatin1String(jt.next()));
cmd->arg("expanded", expanded);
cmd->beginGroup("typeformats");
QJsonObject typeformats;
QHashIterator<QByteArray, int> it(theTypeFormats);
while (it.hasNext()) {
it.next();
const int format = it.value();
if (format != AutomaticFormat)
cmd->arg(it.key(), format);
typeformats.insert(QLatin1String(it.key()), format);
}
cmd->endGroup();
cmd->arg("typeformats", typeformats);
cmd->beginGroup("formats");
QJsonObject formats;
QHashIterator<QByteArray, int> it2(theIndividualFormats);
while (it2.hasNext()) {
it2.next();
const int format = it2.value();
if (format != AutomaticFormat)
cmd->arg(it2.key(), format);
formats.insert(QLatin1String(it2.key()), format);
}
cmd->endGroup();
cmd->arg("formats", formats);
}
static inline QJsonObject watcher(const QByteArray &iname, const QByteArray &exp)
{
QJsonObject watcher;
watcher.insert(QStringLiteral("iname"), QLatin1String(iname));
watcher.insert(QStringLiteral("exp"), QLatin1String(exp.toHex()));
return watcher;
}
void WatchHandler::appendWatchersAndTooltipRequests(DebuggerCommand *cmd)
{
cmd->beginList("watchers");
QJsonArray watchers;
DebuggerToolTipContexts toolTips = DebuggerToolTipManager::pendingTooltips(m_model->m_engine);
foreach (const DebuggerToolTipContext &p, toolTips) {
cmd->beginGroup();
cmd->arg("iname", p.iname);
cmd->arg("exp", p.expression.toLatin1().toHex());
cmd->endGroup();
}
foreach (const DebuggerToolTipContext &p, toolTips)
watchers.append(watcher(p.iname, p.expression.toLatin1()));
QHashIterator<QByteArray, int> it(WatchHandler::watcherNames());
while (it.hasNext()) {
it.next();
cmd->beginGroup();
cmd->arg("iname", "watch." + QByteArray::number(it.value()));
cmd->arg("exp", it.key().toHex());
cmd->endGroup();
watchers.append(watcher("watch." + QByteArray::number(it.value()), it.key()));
}
cmd->endList();
cmd->arg("watchers", watchers);
}
void WatchHandler::addDumpers(const GdbMi &dumpers)
......
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