wallace updated this revision to Diff 297127.
wallace added a comment.

Rebase and made some cosmetic fixes.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D88769

Files:
  lldb/include/lldb/Target/Target.h
  lldb/include/lldb/Target/Thread.h
  lldb/include/lldb/Target/Trace.h
  lldb/source/Commands/CommandObjectThread.cpp
  lldb/source/Commands/Options.td
  lldb/source/Plugins/Process/CMakeLists.txt
  lldb/source/Plugins/Process/Trace/CMakeLists.txt
  lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
  lldb/source/Plugins/Process/Trace/ProcessTrace.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
  lldb/source/Target/Target.cpp
  lldb/source/Target/Thread.cpp
  lldb/test/API/commands/trace/TestTraceDumpInstructions.py
  lldb/test/API/commands/trace/TestTraceLoad.py
  lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json

Index: lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
@@ -0,0 +1,35 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
+    }
+  },
+  "processes": [
+    {
+      "pid": 1234,
+      "triple": "x86_64-*-linux",
+      "threads": [
+        {
+          "tid": 3842849,
+          "traceFile": "3842849.trace"
+        },
+        {
+          "tid": 3842850,
+          "traceFile": "3842849.trace"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000400000",
+          "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+        }
+      ]
+    }
+  ]
+}
Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceLoad.py
+++ lldb/test/API/commands/trace/TestTraceLoad.py
@@ -35,6 +35,10 @@
 
         self.assertEqual("6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A", module.GetUUIDString())
 
+        # check that the Process and Thread objects were created correctly
+        self.expect("thread info", substrs=["tid = 3842849"])
+        self.expect("thread list", substrs=["Process 1234 stopped", "tid = 3842849"])
+
 
     def testLoadInvalidTraces(self):
         src_dir = self.getSourceDir()
Index: lldb/test/API/commands/trace/TestTraceDumpInstructions.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -0,0 +1,91 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceDumpInstructions(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        TestBase.setUp(self)
+        if 'intel-pt' not in configuration.enabled_plugins:
+            self.skipTest("The intel-pt test plugin is not enabled")
+
+    def testErrorMessages(self):
+        # We first check the output when there are no targets
+        self.expect("thread trace dump instructions",
+            substrs=["error: invalid target, create a target using the 'target create' command"],
+            error=True)
+
+        # We now check the output when there's a non-running target
+        self.expect("target create " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+
+        self.expect("thread trace dump instructions",
+            substrs=["error: invalid process"],
+            error=True)
+
+        # Now we check the output when there's a running target without a trace
+        self.expect("b main")
+        self.expect("run")
+
+        self.expect("thread trace dump instructions",
+            substrs=["error: no trace in this target"])
+
+    def testDumpInstructions(self):
+        self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
+            substrs=["intel-pt"])
+
+        self.expect("thread trace dump instructions",
+            substrs=['thread #1: tid = 3842849, total instructions = 1000',
+                     'would print 20 instructions from position 0'])
+
+        # We check if we can pass count and offset
+        self.expect("thread trace dump instructions --count 5 --start-position 10",
+            substrs=['thread #1: tid = 3842849, total instructions = 1000',
+                     'would print 5 instructions from position 10'])
+
+        # We check if we can access the thread by index id
+        self.expect("thread trace dump instructions 1",
+            substrs=['thread #1: tid = 3842849, total instructions = 1000',
+                     'would print 20 instructions from position 0'])
+
+        # We check that we get an error when using an invalid thread index id
+        self.expect("thread trace dump instructions 10", error=True,
+            substrs=['error: no thread with index: "10"'])
+
+    def testDumpInstructionsWithMultipleThreads(self):
+        # We load a trace with two threads
+        self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json"))
+
+        # We print the instructions of two threads simultaneously
+        self.expect("thread trace dump instructions 1 2",
+            substrs=['''thread #1: tid = 3842849, total instructions = 1000
+  would print 20 instructions from position 0
+thread #2: tid = 3842850, total instructions = 1000
+  would print 20 instructions from position 0'''])
+
+        # We use custom --count and --start-position, saving the command to history for later
+        ci = self.dbg.GetCommandInterpreter()
+
+        result = lldb.SBCommandReturnObject()
+        ci.HandleCommand("thread trace dump instructions 1 2 --count 12 --start-position 5", result, True)
+        self.assertIn('''thread #1: tid = 3842849, total instructions = 1000
+  would print 12 instructions from position 5
+thread #2: tid = 3842850, total instructions = 1000
+  would print 12 instructions from position 5''', result.GetOutput())
+
+        # We use a repeat command and ensure the previous count is used and the start-position has moved to the next position
+        result = lldb.SBCommandReturnObject()
+        ci.HandleCommand("", result)
+        self.assertIn('''thread #1: tid = 3842849, total instructions = 1000
+  would print 12 instructions from position 17
+thread #2: tid = 3842850, total instructions = 1000
+  would print 12 instructions from position 17''', result.GetOutput())
+
+        ci.HandleCommand("", result)
+        self.assertIn('''thread #1: tid = 3842849, total instructions = 1000
+  would print 12 instructions from position 29
+thread #2: tid = 3842850, total instructions = 1000
+  would print 12 instructions from position 29''', result.GetOutput())
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -1614,6 +1614,18 @@
   return Status();
 }
 
+void Thread::DumpTraceInstructions(Stream &s, size_t count,
+                                   size_t start_position) const {
+  if (!GetProcess()->GetTarget().GetTrace()) {
+    s << "error: no trace in this target\n";
+    return;
+  }
+  s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = 1000\n",
+           GetIndexID(), GetID());
+  s.Printf("  would print %zu instructions from position %zu\n", count,
+           start_position);
+}
+
 void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
                                      bool stop_format) {
   ExecutionContext exe_ctx(shared_from_this());
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ lldb/source/Target/Target.cpp
@@ -2965,6 +2965,10 @@
   return error;
 }
 
+void Target::SetTrace(const TraceSP &trace_sp) { m_trace_sp = trace_sp; }
+
+TraceSP &Target::GetTrace() { return m_trace_sp; }
+
 Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
   auto state = eStateInvalid;
   auto process_sp = GetProcessSP();
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -60,7 +60,7 @@
   m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
 
   ProcessSP process_sp(target_sp->CreateProcess(
-      /*listener*/ nullptr, /*plugin_name*/ StringRef(),
+      /*listener*/ nullptr, "trace",
       /*crash_file*/ nullptr));
   process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
 
@@ -71,7 +71,16 @@
     if (Error err = ParseModule(target_sp, module))
       return err;
   }
-  return Error::success();
+
+  if (!process.threads.empty())
+    process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+  // We invoke DidAttach to create a correct stopped state for the process and
+  // its threads.
+  ArchSpec process_arch;
+  process_sp->DidAttach(process_arch);
+
+  return llvm::Error::success();
 }
 
 void TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
@@ -105,7 +114,7 @@
     return std::move(err);
   }
 
-  return std::make_shared<TraceIntelPT>(m_pt_cpu, m_targets);
+  return TraceIntelPT::CreateInstance(m_pt_cpu, m_targets);
 }
 
 namespace llvm {
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -52,6 +52,21 @@
   CreateInstance(const llvm::json::Value &trace_session_file,
                  llvm::StringRef session_file_dir, Debugger &debugger);
 
+  /// Create an instance of this class.
+  ///
+  /// \param[in] pt_cpu
+  ///     The libipt.h cpu information needed for decoding correctling the
+  ///     traces.
+  ///
+  /// \param[in] targets
+  ///     The list of targets to associate with this trace instance
+  ///
+  /// \return
+  ///     An intel-pt trace instance.
+  static lldb::TraceSP
+  CreateInstance(const pt_cpu &pt_cpu,
+                 const std::vector<lldb::TargetSP> &targets);
+
   static ConstString GetPluginNameStatic();
 
   uint32_t GetPluginVersion() override;
@@ -59,13 +74,13 @@
 
   llvm::StringRef GetSchema() override;
 
+private:
   TraceIntelPT(const pt_cpu &pt_cpu, const std::vector<lldb::TargetSP> &targets)
       : Trace(), m_pt_cpu(pt_cpu) {
     for (const lldb::TargetSP &target_sp : targets)
       m_targets.push_back(target_sp);
   }
 
-private:
   pt_cpu m_pt_cpu;
   std::vector<std::weak_ptr<Target>> m_targets;
 };
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -10,6 +10,7 @@
 
 #include "TraceIntelPTSessionFileParser.h"
 #include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Target.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -45,12 +46,21 @@
 
 uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
 
-void TraceIntelPT::Dump(lldb_private::Stream *s) const {}
+void TraceIntelPT::Dump(Stream *s) const {}
 
-Expected<lldb::TraceSP>
+Expected<TraceSP>
 TraceIntelPT::CreateInstance(const json::Value &trace_session_file,
                              StringRef session_file_dir, Debugger &debugger) {
   return TraceIntelPTSessionFileParser(debugger, trace_session_file,
                                        session_file_dir)
       .Parse();
 }
+
+TraceSP TraceIntelPT::CreateInstance(const pt_cpu &pt_cpu,
+                                     const std::vector<TargetSP> &targets) {
+  TraceSP trace_instance(new TraceIntelPT(pt_cpu, targets));
+  for (const TargetSP &target_sp : targets)
+    target_sp->SetTrace(trace_instance->shared_from_this());
+
+  return trace_instance;
+}
Index: lldb/source/Plugins/Process/Trace/ProcessTrace.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/Trace/ProcessTrace.h
@@ -0,0 +1,86 @@
+//===-- ProcessTrace.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_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
+
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+namespace process_trace {
+
+class ProcessTrace : public Process {
+public:
+  static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+                                        lldb::ListenerSP listener_sp,
+                                        const FileSpec *crash_file_path);
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static ConstString GetPluginNameStatic();
+
+  static const char *GetPluginDescriptionStatic();
+
+  ProcessTrace(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+
+  ~ProcessTrace() override;
+
+  bool CanDebug(lldb::TargetSP target_sp,
+                bool plugin_specified_by_name) override;
+
+  void DidAttach(ArchSpec &process_arch) override;
+
+  DynamicLoader *GetDynamicLoader() override { return nullptr; }
+
+  SystemRuntime *GetSystemRuntime() override { return nullptr; }
+
+  ConstString GetPluginName() override;
+
+  uint32_t GetPluginVersion() override;
+
+  Status DoDestroy() override;
+
+  void RefreshStateAfterStop() override;
+
+  Status WillResume() override {
+    Status error;
+    error.SetErrorStringWithFormat(
+        "error: %s does not support resuming processes",
+        GetPluginName().GetCString());
+    return error;
+  }
+
+  bool IsAlive() override;
+
+  bool WarnBeforeDetach() const override { return false; }
+
+  size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                    Status &error) override;
+
+  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                      Status &error) override;
+
+  ArchSpec GetArchitecture();
+
+  bool GetProcessInfo(ProcessInstanceInfo &info) override;
+
+protected:
+  void Clear();
+
+  bool UpdateThreadList(ThreadList &old_thread_list,
+                        ThreadList &new_thread_list) override;
+};
+
+} // namespace process_trace
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
Index: lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
@@ -0,0 +1,128 @@
+//===-- ProcessTrace.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 "ProcessTrace.h"
+
+#include <memory>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_trace;
+
+LLDB_PLUGIN_DEFINE(ProcessTrace)
+
+ConstString ProcessTrace::GetPluginNameStatic() {
+  static ConstString g_name("trace");
+  return g_name;
+}
+
+const char *ProcessTrace::GetPluginDescriptionStatic() {
+  return "Trace process plug-in.";
+}
+
+void ProcessTrace::Terminate() {
+  PluginManager::UnregisterPlugin(ProcessTrace::CreateInstance);
+}
+
+ProcessSP ProcessTrace::CreateInstance(TargetSP target_sp,
+                                       ListenerSP listener_sp,
+                                       const FileSpec *crash_file) {
+  return std::make_shared<ProcessTrace>(target_sp, listener_sp);
+}
+
+bool ProcessTrace::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) {
+  return plugin_specified_by_name;
+}
+
+ProcessTrace::ProcessTrace(TargetSP target_sp, ListenerSP listener_sp)
+    : Process(target_sp, listener_sp) {}
+
+ProcessTrace::~ProcessTrace() {
+  Clear();
+  // We need to call finalize on the process before destroying ourselves to
+  // make sure all of the broadcaster cleanup goes as planned. If we destruct
+  // this class, then Process::~Process() might have problems trying to fully
+  // destroy the broadcaster.
+  Finalize();
+}
+
+ConstString ProcessTrace::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessTrace::GetPluginVersion() { return 1; }
+
+void ProcessTrace::DidAttach(ArchSpec &process_arch) {
+  ListenerSP listener_sp(
+      Listener::MakeListener("lldb.process_trace.did_attach_listener"));
+  HijackProcessEvents(listener_sp);
+
+  SetCanJIT(false);
+  StartPrivateStateThread();
+  SetPrivateState(eStateStopped);
+
+  EventSP event_sp;
+  WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp);
+
+  RestoreProcessEvents();
+
+  Process::DidAttach(process_arch);
+}
+
+bool ProcessTrace::UpdateThreadList(ThreadList &old_thread_list,
+                                    ThreadList &new_thread_list) {
+  return false;
+}
+
+void ProcessTrace::RefreshStateAfterStop() {}
+
+Status ProcessTrace::DoDestroy() { return Status(); }
+
+bool ProcessTrace::IsAlive() { return true; }
+
+size_t ProcessTrace::ReadMemory(addr_t addr, void *buf, size_t size,
+                                Status &error) {
+  // Don't allow the caching that lldb_private::Process::ReadMemory does since
+  // we have it all cached in the trace files.
+  return DoReadMemory(addr, buf, size, error);
+}
+
+void ProcessTrace::Clear() { m_thread_list.Clear(); }
+
+void ProcessTrace::Initialize() {
+  static llvm::once_flag g_once_flag;
+
+  llvm::call_once(g_once_flag, []() {
+    PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                  GetPluginDescriptionStatic(), CreateInstance);
+  });
+}
+
+ArchSpec ProcessTrace::GetArchitecture() {
+  return GetTarget().GetArchitecture();
+}
+
+bool ProcessTrace::GetProcessInfo(ProcessInstanceInfo &info) {
+  info.Clear();
+  info.SetProcessID(GetID());
+  info.SetArchitecture(GetArchitecture());
+  ModuleSP module_sp = GetTarget().GetExecutableModule();
+  if (module_sp) {
+    const bool add_exe_file_as_first_arg = false;
+    info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
+                           add_exe_file_as_first_arg);
+  }
+  return true;
+}
+
+size_t ProcessTrace::DoReadMemory(addr_t addr, void *buf, size_t size,
+                                  Status &error) {
+  return 0;
+}
Index: lldb/source/Plugins/Process/Trace/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/Trace/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_lldb_library(lldbPluginProcessTrace PLUGIN
+  ProcessTrace.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbTarget
+    lldbUtility
+    lldbPluginProcessUtility
+  LINK_COMPONENTS
+    BinaryFormat
+    Object
+    Support
+  )
Index: lldb/source/Plugins/Process/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Process/CMakeLists.txt
+++ lldb/source/Plugins/Process/CMakeLists.txt
@@ -17,3 +17,4 @@
 add_subdirectory(elf-core)
 add_subdirectory(mach-core)
 add_subdirectory(minidump)
+add_subdirectory(Trace)
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -1005,6 +1005,21 @@
     Desc<"Display thread plans for unreported threads">;
 }
 
+let Command = "thread trace dump instructions" in {
+  def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
+    Arg<"Count">,
+    Desc<"The number of instructions to display starting at the current "
+    "position in reverse order chronologically.">;
+  def thread_trace_dump_instructions_start_position:
+    Option<"start-position", "s">,
+    Group<1>,
+    Arg<"Index">,
+    Desc<"The position of the first instruction to print. Defaults to the "
+    "current position, i.e. where the thread is stopped. The instructions are "
+    "indexed in reverse order, which means that a start position of 0 refers "
+    "to the last instruction chronologically.">;
+}
+
 let Command = "type summary add" in {
   def type_summary_add_category : Option<"category", "w">, Arg<"Name">,
     Desc<"Add this to the given category instead of the default one.">;
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -8,6 +8,8 @@
 
 #include "CommandObjectThread.h"
 
+#include <sstream>
+
 #include "lldb/Core/ValueObject.h"
 #include "lldb/Host/OptionParser.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
@@ -2165,6 +2167,164 @@
   ~CommandObjectMultiwordThreadPlan() override = default;
 };
 
+// Next are the subcommands of CommandObjectMultiwordTrace
+
+// CommandObjectTraceDumpInstructions
+#define LLDB_OPTIONS_thread_trace_dump_instructions
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpInstructions
+    : public CommandObjectIterateOverThreads {
+public:
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'c': {
+        int32_t count;
+        if (option_arg.empty() || option_arg.getAsInteger(0, count) ||
+            count < 0)
+          error.SetErrorStringWithFormat(
+              "invalid integer value for option '%s'",
+              option_arg.str().c_str());
+        else
+          m_count = count;
+        break;
+      }
+      case 's': {
+        int32_t start_position;
+        if (option_arg.empty() || option_arg.getAsInteger(0, start_position) ||
+            start_position < 0)
+          error.SetErrorStringWithFormat(
+              "invalid integer value for option '%s'",
+              option_arg.str().c_str());
+        else
+          m_start_position = start_position;
+        break;
+      }
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_count = kDefaultCount;
+      m_start_position = kDefaultStartPosition;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_thread_trace_dump_instructions_options);
+    }
+
+    static const uint32_t kDefaultCount = 20;
+    static const uint32_t kDefaultStartPosition = 0;
+
+    // Instance variables to hold the values for command options.
+    uint32_t m_count;
+    uint32_t m_start_position;
+  };
+
+  CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
+      : CommandObjectIterateOverThreads(
+            interpreter, "thread trace dump instructions",
+            "Dump the traced instructions for one or more threads.  If no "
+            "threads are specified, show the current thread.  Use the "
+            "thread-index \"all\" to see all threads.",
+            nullptr,
+            eCommandRequiresProcess | eCommandTryTargetAPILock |
+                eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+        m_options(), m_create_repeat_command_just_invoked(false) {}
+
+  ~CommandObjectTraceDumpInstructions() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+  const char *GetRepeatCommand(Args &current_command_args,
+                               uint32_t index) override {
+    current_command_args.GetCommandString(m_repeat_command);
+    m_create_repeat_command_just_invoked = true;
+    return m_repeat_command.c_str();
+  }
+
+protected:
+  bool DoExecute(Args &args, CommandReturnObject &result) override {
+    bool status = CommandObjectIterateOverThreads::DoExecute(args, result);
+    PrepareRepeatArguments();
+    return status;
+  }
+
+  void PrepareRepeatArguments() {
+    m_repeat_start_position = m_options.m_count + GetStartPosition();
+    m_create_repeat_command_just_invoked = false;
+  }
+
+  bool IsRepeatCommand() {
+    return !m_repeat_command.empty() && !m_create_repeat_command_just_invoked;
+  }
+
+  uint32_t GetStartPosition() {
+    return IsRepeatCommand() ? m_repeat_start_position
+                             : m_options.m_start_position;
+  }
+
+  bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+    ThreadSP thread_sp =
+        m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+
+    thread_sp->DumpTraceInstructions(result.GetOutputStream(),
+                                     m_options.m_count, GetStartPosition());
+    return true;
+  }
+
+  CommandOptions m_options;
+
+  // Repeat command helpers
+  std::string m_repeat_command;
+  bool m_create_repeat_command_just_invoked;
+  uint32_t m_repeat_start_position;
+};
+
+// CommandObjectMultiwordTraceDump
+class CommandObjectMultiwordTraceDump : public CommandObjectMultiword {
+public:
+  CommandObjectMultiwordTraceDump(CommandInterpreter &interpreter)
+      : CommandObjectMultiword(
+            interpreter, "dump",
+            "Commands for displaying trace information of the threads "
+            "in the current process.",
+            "thread trace dump <subcommand> [<subcommand objects>]") {
+    LoadSubCommand(
+        "instructions",
+        CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter)));
+  }
+  ~CommandObjectMultiwordTraceDump() override = default;
+};
+
+// CommandObjectMultiwordTrace
+class CommandObjectMultiwordTrace : public CommandObjectMultiword {
+public:
+  CommandObjectMultiwordTrace(CommandInterpreter &interpreter)
+      : CommandObjectMultiword(
+            interpreter, "trace",
+            "Commands for operating on traces of the threads in the current "
+            "process.",
+            "thread trace <subcommand> [<subcommand objects>]") {
+    LoadSubCommand("dump", CommandObjectSP(new CommandObjectMultiwordTraceDump(
+                               interpreter)));
+  }
+
+  ~CommandObjectMultiwordTrace() override = default;
+};
+
 // CommandObjectMultiwordThread
 
 CommandObjectMultiwordThread::CommandObjectMultiwordThread(
@@ -2240,6 +2400,8 @@
 
   LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan(
                              interpreter)));
+  LoadSubCommand("trace",
+                 CommandObjectSP(new CommandObjectMultiwordTrace(interpreter)));
 }
 
 CommandObjectMultiwordThread::~CommandObjectMultiwordThread() = default;
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -32,7 +32,8 @@
 /// Processor trace information can also be fetched through the process
 /// interfaces during a live debug session if your process supports gathering
 /// this information.
-class Trace : public PluginInterface {
+class Trace : public PluginInterface,
+              public std::enable_shared_from_this<Trace> {
 public:
   /// Dump the trace data that this plug-in has access to.
   ///
Index: lldb/include/lldb/Target/Thread.h
===================================================================
--- lldb/include/lldb/Target/Thread.h
+++ lldb/include/lldb/Target/Thread.h
@@ -469,6 +469,24 @@
     // the backing thread for all memory threads each time we stop.
   }
 
+  /// Dump \a count instructions of the thread's \a Trace starting at the \a
+  /// start_position position in reverse order.
+  ///
+  /// The instructions are indexed in reverse order, which means that the \a
+  /// start_position 0 represents the last instruction of the trace
+  /// chronologically.
+  ///
+  /// \param[in] s
+  ///   The stream object where the instructions are printed.
+  ///
+  /// \param[in] count
+  ///     The number of instructions to print.
+  ///
+  /// \param[in] start_position
+  ///     The position of the first instruction to print.
+  void DumpTraceInstructions(Stream &s, size_t count,
+                             size_t start_position = 0) const;
+
   // If stop_format is true, this will be the form used when we print stop
   // info. If false, it will be the form we use for thread list and co.
   void DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -1105,6 +1105,20 @@
 
   void ClearAllLoadedSections();
 
+  /// Set the \a Trace object containing processor trace information of this
+  /// target.
+  ///
+  /// \param[in] trace_sp
+  ///   The trace object.
+  void SetTrace(const lldb::TraceSP &trace_sp);
+
+  /// Get the \a Trace object containing processor trace information of this
+  /// target.
+  ///
+  /// \return
+  ///   The trace object. It might be undefined.
+  lldb::TraceSP &GetTrace();
+
   // Since expressions results can persist beyond the lifetime of a process,
   // and the const expression results are available after a process is gone, we
   // provide a way for expressions to be evaluated from the Target itself. If
@@ -1394,6 +1408,9 @@
   bool m_suppress_stop_hooks;
   bool m_is_dummy_target;
   unsigned m_next_persistent_variable_index = 0;
+  /// An optional \a lldb_private::Trace object containing processor trace
+  /// information of this target.
+  lldb::TraceSP m_trace_sp;
   /// Stores the frame recognizers of this target.
   lldb::StackFrameRecognizerManagerUP m_frame_recognizer_manager_up;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to