wallace updated this revision to Diff 296106.
wallace added a comment.
Herald added a subscriber: dexonsmith.

Address comments:

- Created a basic ThreadTrace class instead of using HistoryThread. I'll modify 
this class later when I add the decoding functionality and refactor the json 
file parsing logic.
- Renamed offset to start_position, and added a clearer documentation about it. 
We'll have a current_position in each thread, and we'll print instructions in 
reverse order chronologically starting at current_position, unless 
start_position is specified. This matches gdb's implementation and works well 
with repeat commands, e.g. the user types "thread trace dump instructions" 
repeatedly showing a continous list of instructions from most recent to oldest.
- Now using DidAttach instead of LoadCore for setting the newly created 
procesess state to stopped.
- Moved the dump implementation from Target to Thread.
- Fixed the CanDebug function.
- Added a test for "thread trace dump instructions <invalid_thread_index>" and 
now using the long options instead of the short ones in the tests.

Some notes:

- I think that TraceSettingsParser should be used only when constructing a 
trace from a json file. In the case of a live process, we can skip it and 
directly create the trace objects accordingly. So the json parser can assume 
that the process is defunct.
- I haven't thought of making "thread trace dump" do anything. I'm thinking of 
having

thread trace info (for general information, including trace size, for example)
thread trace dump instructions
thread trace dump functions


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/Process/Trace/ThreadTrace.cpp
  lldb/source/Plugins/Process/Trace/ThreadTrace.h
  lldb/source/Target/Target.cpp
  lldb/source/Target/Thread.cpp
  lldb/source/Target/Trace.cpp
  lldb/source/Target/TraceSettingsParser.cpp
  lldb/test/API/commands/trace/TestTraceDumpInstructions.py
  lldb/test/API/commands/trace/TestTraceLoad.py

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,56 @@
+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',
+                     'would print 10 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',
+                     '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',
+                     'would print 10 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"'])
Index: lldb/source/Target/TraceSettingsParser.cpp
===================================================================
--- lldb/source/Target/TraceSettingsParser.cpp
+++ lldb/source/Target/TraceSettingsParser.cpp
@@ -10,7 +10,7 @@
 
 #include <sstream>
 
-#include "Plugins/Process/Utility/HistoryThread.h"
+#include "Plugins/Process/Trace/ThreadTrace.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Target/Process.h"
 
@@ -65,7 +65,7 @@
   NormalizePath(spec);
   m_thread_to_trace_file_map[process_sp->GetID()][tid] = spec;
 
-  ThreadSP thread_sp(new HistoryThread(*process_sp, tid, /*callstack*/ {}));
+  ThreadSP thread_sp(new ThreadTrace(*process_sp, tid));
   process_sp->GetThreadList().AddThread(thread_sp);
 }
 
@@ -107,8 +107,7 @@
   debugger.GetTargetList().SetSelectedTarget(target_sp.get());
 
   ProcessSP process_sp(target_sp->CreateProcess(
-      /*listener*/ nullptr, /*plugin_name*/ llvm::StringRef(),
-      /*crash_file*/ nullptr));
+      /*listener*/ nullptr, /*plugin_name*/ "trace", /*crash_file*/ nullptr));
   process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
 
   for (const JSONThread &thread : process.threads)
@@ -118,6 +117,14 @@
     if (llvm::Error err = ParseModule(target_sp, module))
       return err;
   }
+  if (!process.threads.empty())
+    process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+  // We invoke DidAttach to create a correct stopped state for the process and
+  // its threads.
+  lldb_private::ArchSpec process_arch;
+  process_sp->DidAttach(process_arch);
+
   return llvm::Error::success();
 }
 
@@ -166,10 +173,10 @@
     return err;
   }
 
-  m_trace.m_settings = *raw_settings.getAsObject();
-  m_trace.m_settings_dir = m_settings_dir;
+  for (auto target_sp : m_targets)
+    target_sp->SetTrace(m_trace.shared_from_this());
+
   m_trace.m_thread_to_trace_file_map = m_thread_to_trace_file_map;
-  m_trace.m_targets = m_targets;
 
   return llvm::Error::success();
 }
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -85,11 +85,7 @@
 llvm::Error Trace::ParseSettings(Debugger &debugger,
                                  const llvm::json::Value &settings,
                                  llvm::StringRef settings_dir) {
-  if (llvm::Error err =
-          CreateParser()->ParseSettings(debugger, settings, settings_dir))
-    return err;
-
-  return llvm::Error::success();
+  return CreateParser()->ParseSettings(debugger, settings, settings_dir);
 }
 
 llvm::StringRef Trace::GetSchema() { return CreateParser()->GetSchema(); }
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -1614,6 +1614,17 @@
   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 "\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 lldb::TraceSP &trace_sp) { m_trace_sp = trace_sp; }
+
+lldb::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/Process/Trace/ThreadTrace.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/Trace/ThreadTrace.h
@@ -0,0 +1,33 @@
+//===-- ThreadTrace.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_THREADTRACE_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_TRACE_THREADTRACE_H
+
+#include "lldb/Target/Thread.h"
+
+class ThreadTrace : public lldb_private::Thread {
+public:
+  ThreadTrace(lldb_private::Process &process, lldb::tid_t tid);
+
+  ~ThreadTrace() override;
+
+  void RefreshStateAfterStop() override;
+
+  lldb::RegisterContextSP GetRegisterContext() override;
+
+  lldb::RegisterContextSP
+  CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+protected:
+  bool CalculateStopInfo() override;
+
+  lldb::RegisterContextSP m_thread_reg_ctx_sp;
+};
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_TRACE_THREADTRACE_H
Index: lldb/source/Plugins/Process/Trace/ThreadTrace.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/Trace/ThreadTrace.cpp
@@ -0,0 +1,42 @@
+//===-- ThreadTrace.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 "ThreadTrace.h"
+
+#include <memory>
+
+#include "Plugins/Process/Utility/RegisterContextHistory.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadTrace::ThreadTrace(Process &process, lldb::tid_t tid)
+    : Thread(process, tid) {}
+
+ThreadTrace::~ThreadTrace() {}
+
+void ThreadTrace::RefreshStateAfterStop() {}
+
+RegisterContextSP ThreadTrace::GetRegisterContext() {
+  if (!m_reg_context_sp) {
+    m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+  }
+  return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadTrace::CreateRegisterContextForFrame(StackFrame *frame) {
+  // Eventually this will calculate the register context based on the current
+  // trace position.
+  return std::make_shared<RegisterContextHistory>(
+      *this, 0, GetProcess()->GetAddressByteSize(), LLDB_INVALID_ADDRESS);
+}
+
+bool ThreadTrace::CalculateStopInfo() { return false; }
Index: lldb/source/Plugins/Process/Trace/ProcessTrace.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Process/Trace/ProcessTrace.h
@@ -0,0 +1,82 @@
+//===-- 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"
+
+struct ThreadData;
+
+class ProcessTrace : public lldb_private::Process {
+public:
+  static lldb::ProcessSP
+  CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+                 const lldb_private::FileSpec *crash_file_path);
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static lldb_private::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(lldb_private::ArchSpec &process_arch) override;
+
+  lldb_private::DynamicLoader *GetDynamicLoader() override { return nullptr; }
+
+  lldb_private::SystemRuntime *GetSystemRuntime() override { return nullptr; }
+
+  lldb_private::ConstString GetPluginName() override;
+
+  uint32_t GetPluginVersion() override;
+
+  lldb_private::Status DoDestroy() override;
+
+  void RefreshStateAfterStop() override;
+
+  lldb_private::Status WillResume() override {
+    lldb_private::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,
+                    lldb_private::Status &error) override;
+
+  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                      lldb_private::Status &error) override;
+
+  lldb_private::ArchSpec GetArchitecture();
+
+  bool GetProcessInfo(lldb_private::ProcessInstanceInfo &info) override;
+
+protected:
+  void Clear();
+
+  bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+                        lldb_private::ThreadList &new_thread_list) override;
+};
+
+#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,129 @@
+//===-- 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;
+
+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);
+}
+
+lldb::ProcessSP ProcessTrace::CreateInstance(lldb::TargetSP target_sp,
+                                             lldb::ListenerSP listener_sp,
+                                             const FileSpec *crash_file) {
+  return std::make_shared<ProcessTrace>(target_sp, listener_sp);
+}
+
+bool ProcessTrace::CanDebug(lldb::TargetSP target_sp,
+                            bool plugin_specified_by_name) {
+  return plugin_specified_by_name;
+}
+
+ProcessTrace::ProcessTrace(lldb::TargetSP target_sp,
+                           lldb::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(lldb::eStateStopped);
+
+  lldb::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(lldb::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());
+  lldb::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(lldb::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,14 @@
+add_lldb_library(lldbPluginProcessTrace PLUGIN
+  ProcessTrace.cpp
+  ThreadTrace.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. 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
@@ -2165,6 +2165,130 @@
   ~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 = 10;
+      m_start_position = 0;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_thread_trace_dump_instructions_options);
+    }
+
+    // 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() {}
+
+  ~CommandObjectTraceDumpInstructions() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+protected:
+  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,
+                                     m_options.m_start_position);
+    return true;
+  }
+
+  CommandOptions m_options;
+};
+
+// 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 +2364,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
@@ -33,7 +33,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.
   ///
@@ -128,14 +129,9 @@
 
 protected:
   friend class TraceSettingsParser;
-  /// JSON object that holds all settings for this trace session.
-  llvm::json::Object m_settings;
-  /// The directory that contains the settings file.
-  std::string m_settings_dir;
 
   std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>>
       m_thread_to_trace_file_map;
-  std::vector<lldb::TargetSP> m_targets;
 };
 
 } // namespace lldb_private
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