From d1556e5efbf0cb671c0f6e403fc1eaf9153f8713 Mon Sep 17 00:00:00 2001 From: David Spickett Date: Thu, 26 Oct 2023 08:33:30 +0100 Subject: [PATCH] [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. ``` ``` This is used by lldb-server when building target XML, though no one sets any fields yet. That'll come in a later commit. --- lldb/include/lldb/Target/RegisterFlags.h | 19 +++++-- .../GDBRemoteCommunicationServerLLGS.cpp | 9 ++++ lldb/source/Target/RegisterFlags.cpp | 46 +++++++++++++++++ lldb/unittests/Target/RegisterFlagsTest.cpp | 50 +++++++++++++++++++ 4 files changed, 119 insertions(+), 5 deletions(-) diff --git a/lldb/include/lldb/Target/RegisterFlags.h b/lldb/include/lldb/Target/RegisterFlags.h index d98bc0263e35e..7c5b97c2265fd 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 +#include 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 ">". + 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 23c2f18cd388a..187c23a206094 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("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 06fb45d777ec3..49974718ccb51 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 #include 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: + // + // + // + strm.Indent(); + 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("\n"); +} + +void RegisterFlags::Field::ToXML(StreamString &strm) const { + // Example XML: + // + strm.Indent(); + strm << ""; +} diff --git a/lldb/unittests/Target/RegisterFlagsTest.cpp b/lldb/unittests/Target/RegisterFlagsTest.cpp index 167e28d0cecb3..c7a4192031655 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(), "\n" + "\n"); + + strm.Clear(); + RegisterFlags("Foo", 4, {RegisterFlags::Field("abc", 0, 0)}).ToXML(strm); + ASSERT_EQ(strm.GetString(), "\n" + " \n" + "\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(), + " \n" + " \n" + " \n" + " \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(), + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); +}