This revision was automatically updated to reflect the committed changes.
Closed by commit rGba85f206fe6f: [lldb] Add "register info" command 
(authored by DavidSpickett).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D152916

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

Index: lldb/unittests/Core/DumpRegisterInfoTest.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/Core/DumpRegisterInfoTest.cpp
@@ -0,0 +1,82 @@
+//===-- DumpRegisterInfoTest.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DumpRegisterInfo.h"
+#include "lldb/Utility/StreamString.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+
+TEST(DoDumpRegisterInfoTest, MinimumInfo) {
+  StreamString strm;
+  DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {});
+  ASSERT_EQ(strm.GetString(), "       Name: foo\n"
+                              "       Size: 4 bytes (32 bits)");
+}
+
+TEST(DoDumpRegisterInfoTest, AltName) {
+  StreamString strm;
+  DoDumpRegisterInfo(strm, "foo", "bar", 4, {}, {}, {});
+  ASSERT_EQ(strm.GetString(), "       Name: foo (bar)\n"
+                              "       Size: 4 bytes (32 bits)");
+}
+
+TEST(DoDumpRegisterInfoTest, Invalidates) {
+  StreamString strm;
+  DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2"}, {}, {});
+  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"}, {}, {});
+  ASSERT_EQ(strm.GetString(), "       Name: foo\n"
+                              "       Size: 4 bytes (32 bits)\n"
+                              "Invalidates: foo2, foo3, foo4");
+}
+
+TEST(DoDumpRegisterInfoTest, ReadFrom) {
+  StreamString strm;
+  DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {"foo1"}, {});
+  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"}, {});
+  ASSERT_EQ(strm.GetString(), "       Name: foo\n"
+                              "       Size: 4 bytes (32 bits)\n"
+                              "  Read from: foo1, foo2, foo3");
+}
+
+TEST(DoDumpRegisterInfoTest, InSets) {
+  StreamString strm;
+  DoDumpRegisterInfo(strm, "foo", nullptr, 4, {}, {}, {{"set1", 101}});
+  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}});
+  ASSERT_EQ(strm.GetString(),
+            "       Name: foo\n"
+            "       Size: 4 bytes (32 bits)\n"
+            "    In sets: set1 (index 0), set2 (index 1), set3 (index 2)");
+}
+
+TEST(DoDumpRegisterInfoTest, MaxInfo) {
+  StreamString strm;
+  DoDumpRegisterInfo(strm, "foo", nullptr, 4, {"foo2", "foo3"},
+                     {"foo3", "foo4"}, {{"set1", 1}, {"set2", 2}});
+  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)");
+}
Index: lldb/unittests/Core/CMakeLists.txt
===================================================================
--- lldb/unittests/Core/CMakeLists.txt
+++ lldb/unittests/Core/CMakeLists.txt
@@ -2,6 +2,7 @@
   CommunicationTest.cpp
   DiagnosticEventTest.cpp
   DumpDataExtractorTest.cpp
+  DumpRegisterInfoTest.cpp
   FileSpecListTest.cpp
   FormatEntityTest.cpp
   MangledTest.cpp
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
@@ -567,3 +567,41 @@
             error=True,
             substrs=["error: Register not found for 'blub'."],
         )
+
+    def test_info_unknown_register(self):
+        self.build()
+        self.common_setup()
+
+        self.expect("register info blub", error=True,
+                    substrs=["error: No register found with name 'blub'."])
+
+    def test_info_many_registers(self):
+        self.build()
+        self.common_setup()
+
+        # Only 1 register allowed at this time.
+        self.expect("register info abc def", error=True,
+                    substrs=["error: register info takes exactly 1 argument"])
+
+    @skipIf(archs=no_match(["aarch64"]))
+    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.
+        self.build()
+        self.common_setup()
+
+        # Standard register. Doesn't invalidate anything, doesn't have an alias.
+        self.expect("register info x1", substrs=[
+                   "Name: x1",
+                   "Size: 8 bytes (64 bits)",
+                   "In sets: General Purpose Registers"])
+        self.expect("register info x1", substrs=["Invalidates:", "Name: x1 ("],
+                    matching=False)
+
+        # These registers invalidate others as they are subsets of those registers.
+        self.expect("register info w1", substrs=["Invalidates: x1"])
+        self.expect("register info s0", substrs=["Invalidates: v0, d0"])
+
+        # This has an alternative name according to the ABI.
+        self.expect("register info x30", substrs=["Name: lr (x30)"])
Index: lldb/source/Core/DumpRegisterInfo.cpp
===================================================================
--- /dev/null
+++ lldb/source/Core/DumpRegisterInfo.cpp
@@ -0,0 +1,109 @@
+//===-- DumpRegisterInfo.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DumpRegisterInfo.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+using SetInfo = std::pair<const char *, uint32_t>;
+
+void lldb_private::DumpRegisterInfo(Stream &strm, RegisterContext &ctx,
+                                    const RegisterInfo &info) {
+  std::vector<const char *> invalidates;
+  if (info.invalidate_regs) {
+    for (uint32_t *inv_regs = info.invalidate_regs;
+         *inv_regs != LLDB_INVALID_REGNUM; ++inv_regs) {
+      const RegisterInfo *inv_info =
+          ctx.GetRegisterInfo(lldb::eRegisterKindLLDB, *inv_regs);
+      assert(
+          inv_info &&
+          "Register invalidate list refers to a register that does not exist.");
+      invalidates.push_back(inv_info->name);
+    }
+  }
+
+  // We include the index here so that you can use it with "register read -s".
+  std::vector<SetInfo> in_sets;
+  for (uint32_t set_idx = 0; set_idx < ctx.GetRegisterSetCount(); ++set_idx) {
+    const RegisterSet *set = ctx.GetRegisterSet(set_idx);
+    assert(set && "Register set should be valid.");
+    for (uint32_t reg_idx = 0; reg_idx < set->num_registers; ++reg_idx) {
+      const RegisterInfo *set_reg_info =
+          ctx.GetRegisterInfoAtIndex(set->registers[reg_idx]);
+      assert(set_reg_info && "Register info should be valid.");
+
+      if (set_reg_info == &info) {
+        in_sets.push_back({set->name, set_idx});
+        break;
+      }
+    }
+  }
+
+  std::vector<const char *> read_from;
+  if (info.value_regs) {
+    for (uint32_t *read_regs = info.value_regs;
+         *read_regs != LLDB_INVALID_REGNUM; ++read_regs) {
+      const RegisterInfo *read_info =
+          ctx.GetRegisterInfo(lldb::eRegisterKindLLDB, *read_regs);
+      assert(read_info && "Register value registers list refers to a register "
+                          "that does not exist.");
+      read_from.push_back(read_info->name);
+    }
+  }
+
+  DoDumpRegisterInfo(strm, info.name, info.alt_name, info.byte_size,
+                     invalidates, read_from, in_sets);
+}
+
+template <typename ElementType>
+static void DumpList(Stream &strm, const char *title,
+                     const std::vector<ElementType> &list,
+                     std::function<void(Stream &, ElementType)> emitter) {
+  if (list.empty())
+    return;
+
+  strm.EOL();
+  strm << title;
+  bool first = true;
+  for (ElementType elem : list) {
+    if (!first)
+      strm << ", ";
+    first = false;
+    emitter(strm, elem);
+  }
+}
+
+void lldb_private::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<SetInfo> &in_sets) {
+  strm << "       Name: " << name;
+  if (alt_name)
+    strm << " (" << alt_name << ")";
+  strm.EOL();
+
+  // Size in bits may seem obvious for the usual 32 or 64 bit registers.
+  // When we get to vector registers, then scalable vector registers, it is very
+  // useful to know without the user doing extra work.
+  strm.Printf("       Size: %d bytes (%d bits)", byte_size, byte_size * 8);
+
+  std::function<void(Stream &, const char *)> emit_str =
+      [](Stream &strm, const char *s) { strm << s; };
+  DumpList(strm, "Invalidates: ", invalidates, emit_str);
+  DumpList(strm, "  Read from: ", read_from, emit_str);
+
+  std::function<void(Stream &, SetInfo)> emit_set = [](Stream &strm,
+                                                       SetInfo info) {
+    strm.Printf("%s (index %d)", info.first, info.second);
+  };
+  DumpList(strm, "    In sets: ", in_sets, emit_set);
+}
Index: lldb/source/Core/CMakeLists.txt
===================================================================
--- lldb/source/Core/CMakeLists.txt
+++ lldb/source/Core/CMakeLists.txt
@@ -33,6 +33,7 @@
   Disassembler.cpp
   DumpDataExtractor.cpp
   DumpRegisterValue.cpp
+  DumpRegisterInfo.cpp
   DynamicLoader.cpp
   EmulateInstruction.cpp
   FileLineResolver.cpp
Index: lldb/source/Commands/CommandObjectRegister.cpp
===================================================================
--- lldb/source/Commands/CommandObjectRegister.cpp
+++ lldb/source/Commands/CommandObjectRegister.cpp
@@ -8,6 +8,7 @@
 
 #include "CommandObjectRegister.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpRegisterInfo.h"
 #include "lldb/Core/DumpRegisterValue.h"
 #include "lldb/Host/OptionParser.h"
 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
@@ -398,16 +399,82 @@
   }
 };
 
+// "register info"
+class CommandObjectRegisterInfo : public CommandObjectParsed {
+public:
+  CommandObjectRegisterInfo(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "register info",
+                            "View information about a register.", nullptr,
+                            eCommandRequiresRegContext |
+                                eCommandProcessMustBeLaunched) {
+    SetHelpLong(R"(
+Name             The name lldb uses for the register, optionally with an alias.
+Size             The size of the register in bytes and again in bits.
+Invalidates (*)  The registers that would be changed if you wrote this
+                 register. For example, writing to a narrower alias of a wider
+                 register would change the value of the wider register.
+Read from   (*)  The registers that the value of this register is constructed
+                 from. For example, a narrower alias of a wider register will be
+                 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 marked with (*) may not always be present. Some information may be
+different for the same register when connected to different debug servers.)");
+
+    CommandArgumentData register_arg;
+    register_arg.arg_type = eArgTypeRegisterName;
+    register_arg.arg_repetition = eArgRepeatPlain;
+
+    CommandArgumentEntry arg1;
+    arg1.push_back(register_arg);
+    m_arguments.push_back(arg1);
+  }
+
+  ~CommandObjectRegisterInfo() override = default;
+
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    if (!m_exe_ctx.HasProcessScope() || request.GetCursorIndex() != 0)
+      return;
+    CommandCompletions::InvokeCommonCompletionCallbacks(
+        GetCommandInterpreter(), lldb::eRegisterCompletion, request, nullptr);
+  }
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (command.GetArgumentCount() != 1) {
+      result.AppendError("register info takes exactly 1 argument: <reg-name>");
+      return result.Succeeded();
+    }
+
+    llvm::StringRef reg_name = command[0].ref();
+    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);
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else
+      result.AppendErrorWithFormat("No register found with name '%s'.\n",
+                                   reg_name.str().c_str());
+
+    return result.Succeeded();
+  }
+};
+
 // CommandObjectRegister constructor
 CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter)
     : CommandObjectMultiword(interpreter, "register",
                              "Commands to access registers for the current "
                              "thread and stack frame.",
-                             "register [read|write] ...") {
+                             "register [read|write|info] ...") {
   LoadSubCommand("read",
                  CommandObjectSP(new CommandObjectRegisterRead(interpreter)));
   LoadSubCommand("write",
                  CommandObjectSP(new CommandObjectRegisterWrite(interpreter)));
+  LoadSubCommand("info",
+                 CommandObjectSP(new CommandObjectRegisterInfo(interpreter)));
 }
 
 CommandObjectRegister::~CommandObjectRegister() = default;
Index: lldb/include/lldb/Core/DumpRegisterInfo.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Core/DumpRegisterInfo.h
@@ -0,0 +1,34 @@
+//===-- DumpRegisterInfo.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_CORE_DUMPREGISTERINFO_H
+#define LLDB_CORE_DUMPREGISTERINFO_H
+
+#include <stdint.h>
+#include <utility>
+#include <vector>
+
+namespace lldb_private {
+
+class Stream;
+class RegisterContext;
+struct RegisterInfo;
+
+void DumpRegisterInfo(Stream &strm, RegisterContext &ctx,
+                      const RegisterInfo &info);
+
+// 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);
+
+} // namespace lldb_private
+
+#endif // LLDB_CORE_DUMPREGISTERINFO_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to