diff --git a/lldb/include/lldb/Core/DumpRegisterInfo.h b/lldb/include/lldb/Core/DumpRegisterInfo.h --- a/lldb/include/lldb/Core/DumpRegisterInfo.h +++ b/lldb/include/lldb/Core/DumpRegisterInfo.h @@ -18,16 +18,18 @@ class Stream; class RegisterContext; struct RegisterInfo; +class RegisterFlags; void DumpRegisterInfo(Stream &strm, RegisterContext &ctx, - const RegisterInfo &info); + const RegisterInfo &info, uint32_t terminal_width); // For testing only. Use DumpRegisterInfo instead. void DoDumpRegisterInfo( Stream &strm, const char *name, const char *alt_name, uint32_t byte_size, const std::vector &invalidates, const std::vector &read_from, - const std::vector> &in_sets); + const std::vector> &in_sets, + const RegisterFlags *flags_type, uint32_t terminal_width); } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectRegister.cpp b/lldb/source/Commands/CommandObjectRegister.cpp --- a/lldb/source/Commands/CommandObjectRegister.cpp +++ b/lldb/source/Commands/CommandObjectRegister.cpp @@ -11,6 +11,7 @@ #include "lldb/Core/DumpRegisterInfo.h" #include "lldb/Core/DumpRegisterValue.h" #include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionArgumentTable.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" @@ -418,6 +419,8 @@ read from the wider register. In sets (*) The register sets that contain this register. For example the PC will be in the "General Purpose Register" set. +Fields (*) A table of the names and bit positions of the values contained + in this register. Fields marked with (*) may not always be present. Some information may be different for the same register when connected to different debug servers.)"); @@ -453,7 +456,9 @@ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); if (reg_info) { - DumpRegisterInfo(result.GetOutputStream(), *reg_ctx, *reg_info); + DumpRegisterInfo( + result.GetOutputStream(), *reg_ctx, *reg_info, + GetCommandInterpreter().GetDebugger().GetTerminalWidth()); result.SetStatus(eReturnStatusSuccessFinishResult); } else result.AppendErrorWithFormat("No register found with name '%s'.\n", diff --git a/lldb/source/Core/DumpRegisterInfo.cpp b/lldb/source/Core/DumpRegisterInfo.cpp --- a/lldb/source/Core/DumpRegisterInfo.cpp +++ b/lldb/source/Core/DumpRegisterInfo.cpp @@ -8,6 +8,7 @@ #include "lldb/Core/DumpRegisterInfo.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/RegisterFlags.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -16,7 +17,8 @@ using SetInfo = std::pair; void lldb_private::DumpRegisterInfo(Stream &strm, RegisterContext &ctx, - const RegisterInfo &info) { + const RegisterInfo &info, + uint32_t terminal_width) { std::vector invalidates; if (info.invalidate_regs) { for (uint32_t *inv_regs = info.invalidate_regs; @@ -60,7 +62,8 @@ } DoDumpRegisterInfo(strm, info.name, info.alt_name, info.byte_size, - invalidates, read_from, in_sets); + invalidates, read_from, in_sets, info.flags_type, + terminal_width); } template @@ -85,7 +88,8 @@ Stream &strm, const char *name, const char *alt_name, uint32_t byte_size, const std::vector &invalidates, const std::vector &read_from, - const std::vector &in_sets) { + const std::vector &in_sets, const RegisterFlags *flags_type, + uint32_t terminal_width) { strm << " Name: " << name; if (alt_name) strm << " (" << alt_name << ")"; @@ -106,4 +110,7 @@ strm.Printf("%s (index %d)", info.first, info.second); }; DumpList(strm, " In sets: ", in_sets, emit_set); + + if (flags_type) + strm.Printf("\n\n%s", flags_type->AsTable(terminal_width).c_str()); } diff --git a/lldb/test/API/commands/register/register/register_command/TestRegisters.py b/lldb/test/API/commands/register/register/register_command/TestRegisters.py --- a/lldb/test/API/commands/register/register/register_command/TestRegisters.py +++ b/lldb/test/API/commands/register/register/register_command/TestRegisters.py @@ -587,7 +587,8 @@ def test_info_register(self): # The behaviour of this command is generic but the specific registers # are not, so this is written for AArch64 only. - # Text alignment and ordering are checked in the DumpRegisterInfo unit tests. + # Text alignment and ordering are checked in the DumpRegisterInfo and + # RegisterFlags unit tests. self.build() self.common_setup() diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py --- a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py @@ -605,3 +605,31 @@ self.expect("register read x0", substrs=["(correct = 1)"]) self.expect("register read x1", substrs=["(correct = 1)"]) + + @skipIfXmlSupportMissing + @skipIfRemote + def test_flags_in_register_info(self): + # See RegisterFlags for comprehensive formatting tests. + self.setup_flags_test( + '' + '' + '' + '' + ) + + # The table should split according to terminal width. + self.runCmd("settings set term-width 17") + + self.expect("register info cpsr", + substrs=[ + " Name: cpsr\n" + " Size: 4 bytes (32 bits)\n" + " In sets: general (index 0)\n" + "\n" + "| 31-24 | 23-16 |\n" + "|-------|-------|\n" + "| A | B |\n" + "\n" + "| 15-8 | 7-0 |\n" + "|------|-----|\n" + "| C | D |"]) \ No newline at end of file diff --git a/lldb/unittests/Core/DumpRegisterInfoTest.cpp b/lldb/unittests/Core/DumpRegisterInfoTest.cpp --- a/lldb/unittests/Core/DumpRegisterInfoTest.cpp +++ b/lldb/unittests/Core/DumpRegisterInfoTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Core/DumpRegisterInfo.h" +#include "lldb/Target/RegisterFlags.h" #include "lldb/Utility/StreamString.h" #include "gtest/gtest.h" @@ -14,27 +15,28 @@ TEST(DoDumpRegisterInfoTest, MinimumInfo) { StreamString strm; - DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {}); + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {}, nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)"); } TEST(DoDumpRegisterInfoTest, AltName) { StreamString strm; - DoDumpRegisterInfo(strm, "foo", "bar", 4, {}, {}, {}); + DoDumpRegisterInfo(strm, "foo", "bar", 4, {}, {}, {}, nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo (bar)\n" " Size: 4 bytes (32 bits)"); } TEST(DoDumpRegisterInfoTest, Invalidates) { StreamString strm; - DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2"}, {}, {}); + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2"}, {}, {}, nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" "Invalidates: foo2"); strm.Clear(); - DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2", "foo3", "foo4"}, {}, {}); + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2", "foo3", "foo4"}, {}, {}, + nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" "Invalidates: foo2, foo3, foo4"); @@ -42,13 +44,14 @@ TEST(DoDumpRegisterInfoTest, ReadFrom) { StreamString strm; - DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {"foo1"}, {}); + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {"foo1"}, {}, nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" " Read from: foo1"); strm.Clear(); - DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {"foo1", "foo2", "foo3"}, {}); + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {"foo1", "foo2", "foo3"}, {}, + nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" " Read from: foo1, foo2, foo3"); @@ -56,14 +59,15 @@ TEST(DoDumpRegisterInfoTest, InSets) { StreamString strm; - DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {{"set1", 101}}); + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {{"set1", 101}}, nullptr, + 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" " In sets: set1 (index 101)"); strm.Clear(); DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, - {{"set1", 0}, {"set2", 1}, {"set3", 2}}); + {{"set1", 0}, {"set2", 1}, {"set3", 2}}, nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" @@ -73,10 +77,28 @@ TEST(DoDumpRegisterInfoTest, MaxInfo) { StreamString strm; DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2", "foo3"}, - {"foo3", "foo4"}, {{"set1", 1}, {"set2", 2}}); + {"foo3", "foo4"}, {{"set1", 1}, {"set2", 2}}, nullptr, 0); ASSERT_EQ(strm.GetString(), " Name: foo\n" " Size: 4 bytes (32 bits)\n" "Invalidates: foo2, foo3\n" " Read from: foo3, foo4\n" " In sets: set1 (index 1), set2 (index 2)"); } + +TEST(DoDumpRegisterInfoTest, FieldsTable) { + // This is thoroughly tested in RegisterFlags itself, only checking the + // integration here. + StreamString strm; + RegisterFlags flags( + "", 4, + {RegisterFlags::Field("A", 24, 31), RegisterFlags::Field("B", 16, 23), + RegisterFlags::Field("C", 8, 15), RegisterFlags::Field("D", 0, 7)}); + + DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {}, &flags, 100); + ASSERT_EQ(strm.GetString(), " Name: foo\n" + " Size: 4 bytes (32 bits)\n" + "\n" + "| 31-24 | 23-16 | 15-8 | 7-0 |\n" + "|-------|-------|------|-----|\n" + "| A | B | C | D |"); +}