DavidSpickett created this revision.
Herald added a project: All.
DavidSpickett requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This teaches DumpRegisterInfo to generate a table from the register
flags type. It just calls a method on RegisterFlags.

As such, the extra tests are minimal and only show that the intergration
works. Exhaustive formatting tests are done with RegisterFlags itself.

Example:

  (lldb) register info cpsr
         Name: cpsr
         Size: 4 bytes (32 bits)
      In sets: general (index 0)
  
  | 31 | 30 | 29 | 28 | 27-26 | 25  | 24  | 23  | 22  | 21 | 20 | 19-13 |  12  
| 11-10 | 9 | 8 | 7 | 6 | 5 |  4  | 3-2 | 1 | 0  |
  
|----|----|----|----|-------|-----|-----|-----|-----|----|----|-------|------|-------|---|---|---|---|---|-----|-----|---|----|
  | N  | Z  | C  | V  |       | TCO | DIT | UAO | PAN | SS | IL |       | SSBS 
|       | D | A | I | F |   | nRW | EL  |   | SP |

LLDB limits the max terminal width to 80 chars by default.
So to get that full width output you will need to change the "term-width"
setting to something higher.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D152918

Files:
  lldb/include/lldb/Core/DumpRegisterInfo.h
  lldb/source/Commands/CommandObjectRegister.cpp
  lldb/source/Core/DumpRegisterInfo.cpp
  lldb/test/API/commands/register/register/register_command/TestRegisters.py
  lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
  lldb/unittests/Core/DumpRegisterInfoTest.cpp

Index: lldb/unittests/Core/DumpRegisterInfoTest.cpp
===================================================================
--- lldb/unittests/Core/DumpRegisterInfoTest.cpp
+++ 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  |");
+}
Index: lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
===================================================================
--- lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
+++ 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(
+            '<field name="D" start="0" end="7"/>'
+            '<field name="C" start="8" end="15"/>'
+            '<field name="B" start="16" end="23"/>'
+            '<field name="A" start="24" end="31"/>'
+        )
+
+        # 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
Index: lldb/test/API/commands/register/register/register_command/TestRegisters.py
===================================================================
--- lldb/test/API/commands/register/register/register_command/TestRegisters.py
+++ 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()
 
Index: lldb/source/Core/DumpRegisterInfo.cpp
===================================================================
--- lldb/source/Core/DumpRegisterInfo.cpp
+++ 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<const char *, uint32_t>;
 
 void lldb_private::DumpRegisterInfo(Stream &strm, RegisterContext &ctx,
-                                    const RegisterInfo &info) {
+                                    const RegisterInfo &info,
+                                    uint32_t terminal_width) {
   std::vector<const char *> 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 <typename ElementType>
@@ -85,7 +88,8 @@
     Stream &strm, const char *name, const char *alt_name, uint32_t byte_size,
     const std::vector<const char *> &invalidates,
     const std::vector<const char *> &read_from,
-    const std::vector<SetInfo> &in_sets) {
+    const std::vector<SetInfo> &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());
 }
Index: lldb/source/Commands/CommandObjectRegister.cpp
===================================================================
--- lldb/source/Commands/CommandObjectRegister.cpp
+++ 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",
Index: lldb/include/lldb/Core/DumpRegisterInfo.h
===================================================================
--- lldb/include/lldb/Core/DumpRegisterInfo.h
+++ 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<const char *> &invalidates,
     const std::vector<const char *> &read_from,
-    const std::vector<std::pair<const char *, uint32_t>> &in_sets);
+    const std::vector<std::pair<const char *, uint32_t>> &in_sets,
+    const RegisterFlags *flags_type, uint32_t terminal_width);
 
 } // namespace lldb_private
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to