This revision was automatically updated to reflect the committed changes.
Closed by commit rG8b73a2e82191: [LLDB] Add table formatting for register 
fields (authored by DavidSpickett).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152917/new/

https://reviews.llvm.org/D152917

Files:
  lldb/include/lldb/Target/RegisterFlags.h
  lldb/source/Target/RegisterFlags.cpp
  lldb/unittests/Target/RegisterFlagsTest.cpp

Index: lldb/unittests/Target/RegisterFlagsTest.cpp
===================================================================
--- lldb/unittests/Target/RegisterFlagsTest.cpp
+++ lldb/unittests/Target/RegisterFlagsTest.cpp
@@ -137,3 +137,121 @@
                      make_field(28, 28)});
   ASSERT_EQ(0x00000005ULL, rf3.ReverseFieldOrder(0xA0000000));
 }
+
+TEST(RegisterFlagsTest, AsTable) {
+  // Anonymous fields are shown with an empty name cell,
+  // whether they are known up front or added during construction.
+  RegisterFlags anon_field("", 4, {make_field(0, 31)});
+  ASSERT_EQ("| 31-0 |\n"
+            "|------|\n"
+            "|      |",
+            anon_field.AsTable(100));
+
+  RegisterFlags anon_with_pad("", 4, {make_field(16, 31)});
+  ASSERT_EQ("| 31-16 | 15-0 |\n"
+            "|-------|------|\n"
+            "|       |      |",
+            anon_with_pad.AsTable(100));
+
+  // Use the wider of position and name to set the column width.
+  RegisterFlags name_wider("", 4, {RegisterFlags::Field("aardvark", 0, 31)});
+  ASSERT_EQ("|   31-0   |\n"
+            "|----------|\n"
+            "| aardvark |",
+            name_wider.AsTable(100));
+  // When the padding is an odd number, put the remaining 1 on the right.
+  RegisterFlags pos_wider("", 4, {RegisterFlags::Field("?", 0, 31)});
+  ASSERT_EQ("| 31-0 |\n"
+            "|------|\n"
+            "|  ?   |",
+            pos_wider.AsTable(100));
+
+  // Single bit fields don't need to show start and end, just one of them.
+  RegisterFlags single_bit("", 4, {make_field(31, 31)});
+  ASSERT_EQ("| 31 | 30-0 |\n"
+            "|----|------|\n"
+            "|    |      |",
+            single_bit.AsTable(100));
+
+  // Columns are printed horizontally if max width allows.
+  RegisterFlags many_fields("", 4,
+                            {RegisterFlags::Field("cat", 28, 31),
+                             RegisterFlags::Field("pigeon", 20, 23),
+                             RegisterFlags::Field("wolf", 12, 12),
+                             RegisterFlags::Field("x", 0, 4)});
+  ASSERT_EQ("| 31-28 | 27-24 | 23-20  | 19-13 |  12  | 11-5 | 4-0 |\n"
+            "|-------|-------|--------|-------|------|------|-----|\n"
+            "|  cat  |       | pigeon |       | wolf |      |  x  |",
+            many_fields.AsTable(100));
+
+  // max_width tells us when we need to split into further tables.
+  // Here no split is needed.
+  RegisterFlags exact_max_single_col("", 4, {RegisterFlags::Field("?", 0, 31)});
+  ASSERT_EQ("| 31-0 |\n"
+            "|------|\n"
+            "|  ?   |",
+            exact_max_single_col.AsTable(9));
+  RegisterFlags exact_max_two_col(
+      "", 4,
+      {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});
+  ASSERT_EQ("| 31-16 | 15-0 |\n"
+            "|-------|------|\n"
+            "|   ?   |  #   |",
+            exact_max_two_col.AsTable(16));
+
+  // If max is less than a single column, just print the single column. The user
+  // will have to put up with some wrapping in this niche case.
+  RegisterFlags zero_max_single_col("", 4, {RegisterFlags::Field("?", 0, 31)});
+  ASSERT_EQ("| 31-0 |\n"
+            "|------|\n"
+            "|  ?   |",
+            zero_max_single_col.AsTable(0));
+  // Same logic for any following columns. Effectively making a "vertical"
+  // table, just with more grid lines.
+  RegisterFlags zero_max_two_col(
+      "", 4,
+      {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});
+  ASSERT_EQ("| 31-16 |\n"
+            "|-------|\n"
+            "|   ?   |\n"
+            "\n"
+            "| 15-0 |\n"
+            "|------|\n"
+            "|  #   |",
+            zero_max_two_col.AsTable(0));
+
+  RegisterFlags max_less_than_single_col("", 4,
+                                         {RegisterFlags::Field("?", 0, 31)});
+  ASSERT_EQ("| 31-0 |\n"
+            "|------|\n"
+            "|  ?   |",
+            max_less_than_single_col.AsTable(3));
+  RegisterFlags max_less_than_two_col(
+      "", 4,
+      {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});
+  ASSERT_EQ("| 31-16 |\n"
+            "|-------|\n"
+            "|   ?   |\n"
+            "\n"
+            "| 15-0 |\n"
+            "|------|\n"
+            "|  #   |",
+            max_less_than_two_col.AsTable(9));
+  RegisterFlags max_many_columns(
+      "", 4,
+      {RegisterFlags::Field("A", 24, 31), RegisterFlags::Field("B", 16, 23),
+       RegisterFlags::Field("C", 8, 15),
+       RegisterFlags::Field("really long name", 0, 7)});
+  ASSERT_EQ("| 31-24 | 23-16 |\n"
+            "|-------|-------|\n"
+            "|   A   |   B   |\n"
+            "\n"
+            "| 15-8 |\n"
+            "|------|\n"
+            "|  C   |\n"
+            "\n"
+            "|       7-0        |\n"
+            "|------------------|\n"
+            "| really long name |",
+            max_many_columns.AsTable(23));
+}
Index: lldb/source/Target/RegisterFlags.cpp
===================================================================
--- lldb/source/Target/RegisterFlags.cpp
+++ lldb/source/Target/RegisterFlags.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Target/RegisterFlags.h"
+#include "lldb/Utility/StreamString.h"
 
+#include <numeric>
 #include <optional>
 
 using namespace lldb_private;
@@ -91,3 +93,85 @@
   for (const Field &field : m_fields)
     field.log(log);
 }
+
+static StreamString FormatCell(const StreamString &content,
+                               unsigned column_width) {
+  unsigned pad = column_width - content.GetString().size();
+  std::string pad_l;
+  std::string pad_r;
+  if (pad) {
+    pad_l = std::string(pad / 2, ' ');
+    pad_r = std::string((pad / 2) + (pad % 2), ' ');
+  }
+
+  StreamString aligned;
+  aligned.Printf("|%s%s%s", pad_l.c_str(), content.GetString().data(),
+                 pad_r.c_str());
+  return aligned;
+}
+
+static void EmitTable(std::string &out, std::array<std::string, 3> &table) {
+  // Close the table.
+  for (std::string &line : table)
+    line += '|';
+
+  out += std::accumulate(table.begin() + 1, table.end(), table.front(),
+                         [](std::string lhs, const auto &rhs) {
+                           return std::move(lhs) + "\n" + rhs;
+                         });
+}
+
+std::string RegisterFlags::AsTable(uint32_t max_width) const {
+  std::string table;
+  // position / gridline / name
+  std::array<std::string, 3> lines;
+  uint32_t current_width = 0;
+
+  for (const RegisterFlags::Field &field : m_fields) {
+    StreamString position;
+    if (field.GetEnd() == field.GetStart())
+      position.Printf(" %d ", field.GetEnd());
+    else
+      position.Printf(" %d-%d ", field.GetEnd(), field.GetStart());
+
+    StreamString name;
+    name.Printf(" %s ", field.GetName().c_str());
+
+    unsigned column_width = position.GetString().size();
+    unsigned name_width = name.GetString().size();
+    if (name_width > column_width)
+      column_width = name_width;
+
+    // If the next column would overflow and we have already formatted at least
+    // one column, put out what we have and move to a new table on the next line
+    // (+1 here because we need to cap the ends with '|'). If this is the first
+    // column, just let it overflow and we'll wrap next time around. There's not
+    // mich we can do with a very small terminal.
+    if (current_width && ((current_width + column_width + 1) >= max_width)) {
+      EmitTable(table, lines);
+      // Blank line between each.
+      table += "\n\n";
+
+      for (std::string &line : lines)
+        line.clear();
+      current_width = 0;
+    }
+
+    StreamString aligned_position = FormatCell(position, column_width);
+    lines[0] += aligned_position.GetString();
+    StreamString grid;
+    grid << '|' << std::string(column_width, '-');
+    lines[1] += grid.GetString();
+    StreamString aligned_name = FormatCell(name, column_width);
+    lines[2] += aligned_name.GetString();
+
+    // +1 for the left side '|'.
+    current_width += column_width + 1;
+  }
+
+  // If we didn't overflow and still have table to print out.
+  if (lines[0].size())
+    EmitTable(table, lines);
+
+  return table;
+}
Index: lldb/include/lldb/Target/RegisterFlags.h
===================================================================
--- lldb/include/lldb/Target/RegisterFlags.h
+++ lldb/include/lldb/Target/RegisterFlags.h
@@ -93,6 +93,13 @@
   unsigned GetSize() const { return m_size; }
   void log(Log *log) const;
 
+  /// Produce a text table showing the layout of all the fields. Unamed/padding
+  /// fields will be included, with only their positions shown.
+  /// max_width will be the width in characters of the terminal you are
+  /// going to print the table to. If the table would exceed this width, it will
+  /// be split into many tables as needed.
+  std::string AsTable(uint32_t max_width) const;
+
 private:
   const std::string m_id;
   /// Size in bytes
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to