Skip to content

Commit 7ebcd88

Browse files
committed
Add DumpBinaryEscaped method to JSONGenerator, avoid extra copy
All uses of JSONGenerator in debugserver would create a JSON text dump of the object collection, then copy that string into a binary-escaped string, then send it up to the lldb side or make a compressed version and send that. This adds a DumpBinaryEscaped method to JSONGenerator which does the gdb remote serial protocol binary escaping directly, and removes the need to pass over the string and have an additional copy in memory. Differential Revision: https://reviews.llvm.org/D122882 rdar://91117456
1 parent 686406a commit 7ebcd88

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

lldb/tools/debugserver/source/JSONGenerator.h

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ class JSONGenerator {
113113

114114
virtual void Dump(std::ostream &s) const = 0;
115115

116+
virtual void DumpBinaryEscaped(std::ostream &s) const = 0;
117+
116118
private:
117119
Type m_type;
118120
};
@@ -136,6 +138,17 @@ class JSONGenerator {
136138
s << "]";
137139
}
138140

141+
void DumpBinaryEscaped(std::ostream &s) const override {
142+
s << "[";
143+
const size_t arrsize = m_items.size();
144+
for (size_t i = 0; i < arrsize; ++i) {
145+
m_items[i]->DumpBinaryEscaped(s);
146+
if (i + 1 < arrsize)
147+
s << ",";
148+
}
149+
s << "]";
150+
}
151+
139152
protected:
140153
typedef std::vector<ObjectSP> collection;
141154
collection m_items;
@@ -151,6 +164,8 @@ class JSONGenerator {
151164

152165
void Dump(std::ostream &s) const override { s << m_value; }
153166

167+
void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); }
168+
154169
protected:
155170
uint64_t m_value;
156171
};
@@ -165,6 +180,8 @@ class JSONGenerator {
165180

166181
void Dump(std::ostream &s) const override { s << m_value; }
167182

183+
void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); }
184+
168185
protected:
169186
double m_value;
170187
};
@@ -184,6 +201,8 @@ class JSONGenerator {
184201
s << "false";
185202
}
186203

204+
void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); }
205+
187206
protected:
188207
bool m_value;
189208
};
@@ -199,15 +218,33 @@ class JSONGenerator {
199218
void SetValue(const std::string &string) { m_value = string; }
200219

201220
void Dump(std::ostream &s) const override {
202-
std::string quoted;
221+
s << '"';
222+
const size_t strsize = m_value.size();
223+
for (size_t i = 0; i < strsize; ++i) {
224+
char ch = m_value[i];
225+
if (ch == '"')
226+
s << '\\';
227+
s << ch;
228+
}
229+
s << '"';
230+
}
231+
232+
void DumpBinaryEscaped(std::ostream &s) const override {
233+
s << '"';
203234
const size_t strsize = m_value.size();
204235
for (size_t i = 0; i < strsize; ++i) {
205236
char ch = m_value[i];
206237
if (ch == '"')
207-
quoted.push_back('\\');
208-
quoted.push_back(ch);
238+
s << '\\';
239+
// gdb remote serial protocol binary escaping
240+
if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
241+
s << '}'; // 0x7d next character is escaped
242+
s << static_cast<char>(ch ^ 0x20);
243+
} else {
244+
s << ch;
245+
}
209246
}
210-
s << '"' << quoted.c_str() << '"';
247+
s << '"';
211248
}
212249

213250
protected:
@@ -269,7 +306,43 @@ class JSONGenerator {
269306
s << "}";
270307
}
271308

309+
void DumpBinaryEscaped(std::ostream &s) const override {
310+
bool have_printed_one_elem = false;
311+
s << "{";
312+
for (collection::const_iterator iter = m_dict.begin();
313+
iter != m_dict.end(); ++iter) {
314+
if (!have_printed_one_elem) {
315+
have_printed_one_elem = true;
316+
} else {
317+
s << ",";
318+
}
319+
s << "\"" << binary_encode_string(iter->first) << "\":";
320+
iter->second->DumpBinaryEscaped(s);
321+
}
322+
// '}' must be escaped for the gdb remote serial
323+
// protocol.
324+
s << "}";
325+
s << static_cast<char>('}' ^ 0x20);
326+
}
327+
272328
protected:
329+
std::string binary_encode_string(const std::string &s) const {
330+
std::string output;
331+
const size_t s_size = s.size();
332+
const char *s_chars = s.c_str();
333+
334+
for (size_t i = 0; i < s_size; i++) {
335+
unsigned char ch = *(s_chars + i);
336+
if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
337+
output.push_back('}'); // 0x7d
338+
output.push_back(ch ^ 0x20);
339+
} else {
340+
output.push_back(ch);
341+
}
342+
}
343+
return output;
344+
}
345+
273346
// Keep the dictionary as a vector so the dictionary doesn't reorder itself
274347
// when you dump it
275348
// We aren't accessing keys by name, so this won't affect performance
@@ -288,6 +361,8 @@ class JSONGenerator {
288361

289362
void Dump(std::ostream &s) const override { s << "null"; }
290363

364+
void DumpBinaryEscaped(std::ostream &s) const override { Dump(s); }
365+
291366
protected:
292367
};
293368

@@ -304,6 +379,8 @@ class JSONGenerator {
304379

305380
void Dump(std::ostream &s) const override;
306381

382+
void DumpBinaryEscaped(std::ostream &s) const override;
383+
307384
private:
308385
void *m_object;
309386
};

lldb/tools/debugserver/source/RNBRemote.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -582,9 +582,8 @@ RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary) {
582582
// of these coming out at the wrong time (i.e. when the remote side
583583
// is not waiting for a process control completion response).
584584
stream << "JSON-async:";
585-
dictionary.Dump(stream);
586-
const std::string payload = binary_encode_string(stream.str());
587-
return SendPacket(payload);
585+
dictionary.DumpBinaryEscaped(stream);
586+
return SendPacket(stream.str());
588587
}
589588

590589
// Given a std::string packet contents to send, possibly encode/compress it.
@@ -2793,6 +2792,7 @@ rnb_err_t RNBRemote::SendStopReplyPacketForThread(nub_thread_t tid) {
27932792
ostrm << std::hex << "jstopinfo:";
27942793
std::ostringstream json_strm;
27952794
threads_info_sp->Dump(json_strm);
2795+
threads_info_sp->Clear();
27962796
append_hexified_string(ostrm, json_strm.str());
27972797
ostrm << ';';
27982798
}
@@ -5556,11 +5556,10 @@ rnb_err_t RNBRemote::HandlePacket_jThreadsInfo(const char *p) {
55565556

55575557
if (threads_info_sp) {
55585558
std::ostringstream strm;
5559-
threads_info_sp->Dump(strm);
5559+
threads_info_sp->DumpBinaryEscaped(strm);
55605560
threads_info_sp->Clear();
5561-
std::string binary_packet = binary_encode_string(strm.str());
5562-
if (!binary_packet.empty())
5563-
return SendPacket(binary_packet);
5561+
if (strm.str().size() > 0)
5562+
return SendPacket(strm.str());
55645563
}
55655564
}
55665565
return SendPacket("E85");
@@ -5881,11 +5880,10 @@ RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos(const char *p) {
58815880

58825881
if (json_sp.get()) {
58835882
std::ostringstream json_str;
5884-
json_sp->Dump(json_str);
5883+
json_sp->DumpBinaryEscaped(json_str);
58855884
json_sp->Clear();
58865885
if (json_str.str().size() > 0) {
5887-
std::string json_str_quoted = binary_encode_string(json_str.str());
5888-
return SendPacket(json_str_quoted);
5886+
return SendPacket(json_str.str());
58895887
} else {
58905888
SendPacket("E84");
58915889
}
@@ -5915,11 +5913,10 @@ rnb_err_t RNBRemote::HandlePacket_jGetSharedCacheInfo(const char *p) {
59155913

59165914
if (json_sp.get()) {
59175915
std::ostringstream json_str;
5918-
json_sp->Dump(json_str);
5916+
json_sp->DumpBinaryEscaped(json_str);
59195917
json_sp->Clear();
59205918
if (json_str.str().size() > 0) {
5921-
std::string json_str_quoted = binary_encode_string(json_str.str());
5922-
return SendPacket(json_str_quoted);
5919+
return SendPacket(json_str.str());
59235920
} else {
59245921
SendPacket("E86");
59255922
}

0 commit comments

Comments
 (0)