Skip to content

Commit d1556e5

Browse files
[lldb][lldb-server] Enable sending RegisterFlags as XML (#69951)
This adds ToXML methods to encode RegisterFlags and its fields into XML according to GDB's target XML format: https://sourceware.org/gdb/onlinedocs/gdb/Target-Description-Format.html#Target-Description-Format lldb-server does not use libXML to build XML, so this follows the existing code that uses strings. Indentation is used so the result is still human readable. ``` <flags id=\"Foo\" size=\"4\"> <field name=\"abc\" start=\"0\" end=\"0\"/> </flags> ``` This is used by lldb-server when building target XML, though no one sets any fields yet. That'll come in a later commit.
1 parent 4602802 commit d1556e5

File tree

4 files changed

+119
-5
lines changed

4 files changed

+119
-5
lines changed

lldb/include/lldb/Target/RegisterFlags.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@
99
#ifndef LLDB_TARGET_REGISTERFLAGS_H
1010
#define LLDB_TARGET_REGISTERFLAGS_H
1111

12-
#include "lldb/Utility/Log.h"
12+
#include <string>
13+
#include <vector>
1314

1415
namespace lldb_private {
1516

17+
class StreamString;
18+
class Log;
19+
1620
class RegisterFlags {
1721
public:
1822
class Field {
1923
public:
2024
/// Where start is the least significant bit and end is the most
2125
/// significant bit. The start bit must be <= the end bit.
22-
Field(std::string name, unsigned start, unsigned end)
23-
: m_name(std::move(name)), m_start(start), m_end(end) {
24-
assert(m_start <= m_end && "Start bit must be <= end bit.");
25-
}
26+
Field(std::string name, unsigned start, unsigned end);
2627

2728
/// Construct a field that occupies a single bit.
2829
Field(std::string name, unsigned bit_position)
@@ -51,6 +52,11 @@ class RegisterFlags {
5152
/// covered by either field.
5253
unsigned PaddingDistance(const Field &other) const;
5354

55+
/// Output XML that describes this field, to be inserted into a target XML
56+
/// file. Reserved characters in field names like "<" are replaced with
57+
/// their XML safe equivalents like "&gt;".
58+
void ToXML(StreamString &strm) const;
59+
5460
bool operator<(const Field &rhs) const {
5561
return GetStart() < rhs.GetStart();
5662
}
@@ -106,6 +112,9 @@ class RegisterFlags {
106112
/// be split into many tables as needed.
107113
std::string AsTable(uint32_t max_width) const;
108114

115+
// Output XML that describes this set of flags.
116+
void ToXML(StreamString &strm) const;
117+
109118
private:
110119
const std::string m_id;
111120
/// Size in bytes

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,12 @@ GDBRemoteCommunicationServerLLGS::BuildTargetXml() {
30943094
continue;
30953095
}
30963096

3097+
if (reg_info->flags_type) {
3098+
response.IndentMore();
3099+
reg_info->flags_type->ToXML(response);
3100+
response.IndentLess();
3101+
}
3102+
30973103
response.Indent();
30983104
response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32
30993105
"\" regnum=\"%d\" ",
@@ -3113,6 +3119,9 @@ GDBRemoteCommunicationServerLLGS::BuildTargetXml() {
31133119
if (!format.empty())
31143120
response << "format=\"" << format << "\" ";
31153121

3122+
if (reg_info->flags_type)
3123+
response << "type=\"" << reg_info->flags_type->GetID() << "\" ";
3124+
31163125
const char *const register_set_name =
31173126
reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index);
31183127
if (register_set_name)

lldb/source/Target/RegisterFlags.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/Target/RegisterFlags.h"
10+
#include "lldb/Utility/Log.h"
1011
#include "lldb/Utility/StreamString.h"
1112

13+
#include "llvm/ADT/StringExtras.h"
14+
1215
#include <numeric>
1316
#include <optional>
1417

1518
using namespace lldb_private;
1619

20+
RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
21+
: m_name(std::move(name)), m_start(start), m_end(end) {
22+
assert(m_start <= m_end && "Start bit must be <= end bit.");
23+
}
24+
1725
void RegisterFlags::Field::log(Log *log) const {
1826
LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
1927
m_end);
@@ -175,3 +183,41 @@ std::string RegisterFlags::AsTable(uint32_t max_width) const {
175183

176184
return table;
177185
}
186+
187+
void RegisterFlags::ToXML(StreamString &strm) const {
188+
// Example XML:
189+
// <flags id="cpsr_flags" size="4">
190+
// <field name="incorrect" start="0" end="0"/>
191+
// </flags>
192+
strm.Indent();
193+
strm << "<flags id=\"" << GetID() << "\" ";
194+
strm.Printf("size=\"%d\"", GetSize());
195+
strm << ">";
196+
for (const Field &field : m_fields) {
197+
// Skip padding fields.
198+
if (field.GetName().empty())
199+
continue;
200+
201+
strm << "\n";
202+
strm.IndentMore();
203+
field.ToXML(strm);
204+
strm.IndentLess();
205+
}
206+
strm.PutChar('\n');
207+
strm.Indent("</flags>\n");
208+
}
209+
210+
void RegisterFlags::Field::ToXML(StreamString &strm) const {
211+
// Example XML:
212+
// <field name="correct" start="0" end="0"/>
213+
strm.Indent();
214+
strm << "<field name=\"";
215+
216+
std::string escaped_name;
217+
llvm::raw_string_ostream escape_strm(escaped_name);
218+
llvm::printHTMLEscaped(GetName(), escape_strm);
219+
strm << escaped_name << "\" ";
220+
221+
strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
222+
strm << "/>";
223+
}

lldb/unittests/Target/RegisterFlagsTest.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/Target/RegisterFlags.h"
10+
#include "lldb/Utility/StreamString.h"
1011
#include "gmock/gmock.h"
1112
#include "gtest/gtest.h"
1213

@@ -258,3 +259,52 @@ TEST(RegisterFlagsTest, AsTable) {
258259
"| really long name |",
259260
max_many_columns.AsTable(23));
260261
}
262+
263+
TEST(RegisterFieldsTest, ToXML) {
264+
StreamString strm;
265+
266+
// RegisterFlags requires that some fields be given, so no testing of empty
267+
// input.
268+
269+
// Unnamed fields are padding that are ignored. This applies to fields passed
270+
// in, and those generated to fill the other bits (31-1 here).
271+
RegisterFlags("Foo", 4, {RegisterFlags::Field("", 0, 0)}).ToXML(strm);
272+
ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"
273+
"</flags>\n");
274+
275+
strm.Clear();
276+
RegisterFlags("Foo", 4, {RegisterFlags::Field("abc", 0, 0)}).ToXML(strm);
277+
ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"
278+
" <field name=\"abc\" start=\"0\" end=\"0\"/>\n"
279+
"</flags>\n");
280+
281+
strm.Clear();
282+
// Should use the current indentation level as a starting point.
283+
strm.IndentMore();
284+
RegisterFlags(
285+
"Bar", 5,
286+
{RegisterFlags::Field("f1", 25, 32), RegisterFlags::Field("f2", 10, 24)})
287+
.ToXML(strm);
288+
ASSERT_EQ(strm.GetString(),
289+
" <flags id=\"Bar\" size=\"5\">\n"
290+
" <field name=\"f1\" start=\"25\" end=\"32\"/>\n"
291+
" <field name=\"f2\" start=\"10\" end=\"24\"/>\n"
292+
" </flags>\n");
293+
294+
strm.Clear();
295+
strm.IndentLess();
296+
// Should replace any XML unsafe characters in field names.
297+
RegisterFlags("Safe", 8,
298+
{RegisterFlags::Field("A<", 4), RegisterFlags::Field("B>", 3),
299+
RegisterFlags::Field("C'", 2), RegisterFlags::Field("D\"", 1),
300+
RegisterFlags::Field("E&", 0)})
301+
.ToXML(strm);
302+
ASSERT_EQ(strm.GetString(),
303+
"<flags id=\"Safe\" size=\"8\">\n"
304+
" <field name=\"A&lt;\" start=\"4\" end=\"4\"/>\n"
305+
" <field name=\"B&gt;\" start=\"3\" end=\"3\"/>\n"
306+
" <field name=\"C&apos;\" start=\"2\" end=\"2\"/>\n"
307+
" <field name=\"D&quot;\" start=\"1\" end=\"1\"/>\n"
308+
" <field name=\"E&amp;\" start=\"0\" end=\"0\"/>\n"
309+
"</flags>\n");
310+
}

0 commit comments

Comments
 (0)