Author: David Spickett
Date: 2023-10-26T08:33:30+01:00
New Revision: d1556e5efbf0cb671c0f6e403fc1eaf9153f8713

URL: 
https://github.com/llvm/llvm-project/commit/d1556e5efbf0cb671c0f6e403fc1eaf9153f8713
DIFF: 
https://github.com/llvm/llvm-project/commit/d1556e5efbf0cb671c0f6e403fc1eaf9153f8713.diff

LOG: [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.

Added: 
    

Modified: 
    lldb/include/lldb/Target/RegisterFlags.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
    lldb/source/Target/RegisterFlags.cpp
    lldb/unittests/Target/RegisterFlagsTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/RegisterFlags.h 
b/lldb/include/lldb/Target/RegisterFlags.h
index d98bc0263e35e23..7c5b97c2265fda3 100644
--- a/lldb/include/lldb/Target/RegisterFlags.h
+++ b/lldb/include/lldb/Target/RegisterFlags.h
@@ -9,20 +9,21 @@
 #ifndef LLDB_TARGET_REGISTERFLAGS_H
 #define LLDB_TARGET_REGISTERFLAGS_H
 
-#include "lldb/Utility/Log.h"
+#include <string>
+#include <vector>
 
 namespace lldb_private {
 
+class StreamString;
+class Log;
+
 class RegisterFlags {
 public:
   class Field {
   public:
     /// Where start is the least significant bit and end is the most
     /// significant bit. The start bit must be <= the end bit.
-    Field(std::string name, unsigned start, unsigned end)
-        : m_name(std::move(name)), m_start(start), m_end(end) {
-      assert(m_start <= m_end && "Start bit must be <= end bit.");
-    }
+    Field(std::string name, unsigned start, unsigned end);
 
     /// Construct a field that occupies a single bit.
     Field(std::string name, unsigned bit_position)
@@ -51,6 +52,11 @@ class RegisterFlags {
     /// covered by either field.
     unsigned PaddingDistance(const Field &other) const;
 
+    /// Output XML that describes this field, to be inserted into a target XML
+    /// file. Reserved characters in field names like "<" are replaced with
+    /// their XML safe equivalents like "&gt;".
+    void ToXML(StreamString &strm) const;
+
     bool operator<(const Field &rhs) const {
       return GetStart() < rhs.GetStart();
     }
@@ -106,6 +112,9 @@ class RegisterFlags {
   /// be split into many tables as needed.
   std::string AsTable(uint32_t max_width) const;
 
+  // Output XML that describes this set of flags.
+  void ToXML(StreamString &strm) const;
+
 private:
   const std::string m_id;
   /// Size in bytes

diff  --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 23c2f18cd388a86..187c23a206094c0 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -3094,6 +3094,12 @@ GDBRemoteCommunicationServerLLGS::BuildTargetXml() {
       continue;
     }
 
+    if (reg_info->flags_type) {
+      response.IndentMore();
+      reg_info->flags_type->ToXML(response);
+      response.IndentLess();
+    }
+
     response.Indent();
     response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32
                     "\" regnum=\"%d\" ",
@@ -3113,6 +3119,9 @@ GDBRemoteCommunicationServerLLGS::BuildTargetXml() {
     if (!format.empty())
       response << "format=\"" << format << "\" ";
 
+    if (reg_info->flags_type)
+      response << "type=\"" << reg_info->flags_type->GetID() << "\" ";
+
     const char *const register_set_name =
         reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index);
     if (register_set_name)

diff  --git a/lldb/source/Target/RegisterFlags.cpp 
b/lldb/source/Target/RegisterFlags.cpp
index 06fb45d777ec36f..49974718ccb514a 100644
--- a/lldb/source/Target/RegisterFlags.cpp
+++ b/lldb/source/Target/RegisterFlags.cpp
@@ -7,13 +7,21 @@
 
//===----------------------------------------------------------------------===//
 
 #include "lldb/Target/RegisterFlags.h"
+#include "lldb/Utility/Log.h"
 #include "lldb/Utility/StreamString.h"
 
+#include "llvm/ADT/StringExtras.h"
+
 #include <numeric>
 #include <optional>
 
 using namespace lldb_private;
 
+RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
+    : m_name(std::move(name)), m_start(start), m_end(end) {
+  assert(m_start <= m_end && "Start bit must be <= end bit.");
+}
+
 void RegisterFlags::Field::log(Log *log) const {
   LLDB_LOG(log, "  Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
            m_end);
@@ -175,3 +183,41 @@ std::string RegisterFlags::AsTable(uint32_t max_width) 
const {
 
   return table;
 }
+
+void RegisterFlags::ToXML(StreamString &strm) const {
+  // Example XML:
+  // <flags id="cpsr_flags" size="4">
+  //   <field name="incorrect" start="0" end="0"/>
+  // </flags>
+  strm.Indent();
+  strm << "<flags id=\"" << GetID() << "\" ";
+  strm.Printf("size=\"%d\"", GetSize());
+  strm << ">";
+  for (const Field &field : m_fields) {
+    // Skip padding fields.
+    if (field.GetName().empty())
+      continue;
+
+    strm << "\n";
+    strm.IndentMore();
+    field.ToXML(strm);
+    strm.IndentLess();
+  }
+  strm.PutChar('\n');
+  strm.Indent("</flags>\n");
+}
+
+void RegisterFlags::Field::ToXML(StreamString &strm) const {
+  // Example XML:
+  // <field name="correct" start="0" end="0"/>
+  strm.Indent();
+  strm << "<field name=\"";
+
+  std::string escaped_name;
+  llvm::raw_string_ostream escape_strm(escaped_name);
+  llvm::printHTMLEscaped(GetName(), escape_strm);
+  strm << escaped_name << "\" ";
+
+  strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
+  strm << "/>";
+}

diff  --git a/lldb/unittests/Target/RegisterFlagsTest.cpp 
b/lldb/unittests/Target/RegisterFlagsTest.cpp
index 167e28d0cecb3bd..c7a419203165538 100644
--- a/lldb/unittests/Target/RegisterFlagsTest.cpp
+++ b/lldb/unittests/Target/RegisterFlagsTest.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "lldb/Target/RegisterFlags.h"
+#include "lldb/Utility/StreamString.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -258,3 +259,52 @@ TEST(RegisterFlagsTest, AsTable) {
             "| really long name |",
             max_many_columns.AsTable(23));
 }
+
+TEST(RegisterFieldsTest, ToXML) {
+  StreamString strm;
+
+  // RegisterFlags requires that some fields be given, so no testing of empty
+  // input.
+
+  // Unnamed fields are padding that are ignored. This applies to fields passed
+  // in, and those generated to fill the other bits (31-1 here).
+  RegisterFlags("Foo", 4, {RegisterFlags::Field("", 0, 0)}).ToXML(strm);
+  ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"
+                              "</flags>\n");
+
+  strm.Clear();
+  RegisterFlags("Foo", 4, {RegisterFlags::Field("abc", 0, 0)}).ToXML(strm);
+  ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"
+                              "  <field name=\"abc\" start=\"0\" end=\"0\"/>\n"
+                              "</flags>\n");
+
+  strm.Clear();
+  // Should use the current indentation level as a starting point.
+  strm.IndentMore();
+  RegisterFlags(
+      "Bar", 5,
+      {RegisterFlags::Field("f1", 25, 32), RegisterFlags::Field("f2", 10, 24)})
+      .ToXML(strm);
+  ASSERT_EQ(strm.GetString(),
+            "  <flags id=\"Bar\" size=\"5\">\n"
+            "    <field name=\"f1\" start=\"25\" end=\"32\"/>\n"
+            "    <field name=\"f2\" start=\"10\" end=\"24\"/>\n"
+            "  </flags>\n");
+
+  strm.Clear();
+  strm.IndentLess();
+  // Should replace any XML unsafe characters in field names.
+  RegisterFlags("Safe", 8,
+                {RegisterFlags::Field("A<", 4), RegisterFlags::Field("B>", 3),
+                 RegisterFlags::Field("C'", 2), RegisterFlags::Field("D\"", 1),
+                 RegisterFlags::Field("E&", 0)})
+      .ToXML(strm);
+  ASSERT_EQ(strm.GetString(),
+            "<flags id=\"Safe\" size=\"8\">\n"
+            "  <field name=\"A&lt;\" start=\"4\" end=\"4\"/>\n"
+            "  <field name=\"B&gt;\" start=\"3\" end=\"3\"/>\n"
+            "  <field name=\"C&apos;\" start=\"2\" end=\"2\"/>\n"
+            "  <field name=\"D&quot;\" start=\"1\" end=\"1\"/>\n"
+            "  <field name=\"E&amp;\" start=\"0\" end=\"0\"/>\n"
+            "</flags>\n");
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to