wallace updated this revision to Diff 298052.
wallace marked 11 inline comments as done.
wallace added a comment.

Changes:

- Changed the callback signature of ForEachInstruction to receive an 
Expected<load_addr>
- Use Expected more ubiquitously in IntelPTDecoder
- Use MemoryBuffer to read the trace file
- Implemented ProcessTrace::DoReadMemory, which now is used by libipt instead 
of loading each object file inside libipt
- Made the decoding function a little bit more readable
- Small fixes here and there

Notes:

- I added a small test where a instruction can't be decoded due to missing 
memory mapping. I'll add later more robust tests.
- I'm also not fond of the ProcessTrace class being implemented in a plugin, 
however, it seems that all processes are implemented that way and the main 
method for creating processes Target::CreateProcess relies on plugins for 
creating the right instance. I could create an overload of that class that 
receives a callback that creates a specific process class, although it might 
create an way to unintendendly bypass the existing Target::CreateProcess. 
Another option is to move the class to lldb core and  register the plugin from 
there, even though it'd be the only class doing that in the codebase. What do 
you think?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D89283

Files:
  lldb/include/lldb/Target/Trace.h
  lldb/include/lldb/Target/TraceSessionFileParser.h
  lldb/include/lldb/Target/TraceThread.h
  lldb/include/lldb/lldb-forward.h
  lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
  lldb/source/Plugins/Process/Trace/ProcessTrace.h
  lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
  lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
  lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
  lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
  lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
  lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.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/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
  lldb/source/Target/CMakeLists.txt
  lldb/source/Target/Thread.cpp
  lldb/source/Target/Trace.cpp
  lldb/source/Target/TraceSessionFileParser.cpp
  lldb/source/Target/TraceThread.cpp
  lldb/test/API/commands/trace/TestTraceDumpInstructions.py
  lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
  lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json

Index: lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
@@ -0,0 +1,31 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 2123123,
+      "model": 12123123,
+      "stepping": 1231231
+    }
+  },
+  "processes": [
+    {
+      "pid": 1234,
+      "triple": "x86_64-*-linux",
+      "threads": [
+        {
+          "tid": 3842849,
+          "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/intelpt-trace/trace_bad_image.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
@@ -0,0 +1,31 @@
+{
+  "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"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000FFFFF0",
+          "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+        }
+      ]
+    }
+  ]
+}
Index: lldb/test/API/commands/trace/TestTraceDumpInstructions.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceDumpInstructions.py
+++ lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -39,18 +39,40 @@
             substrs=["intel-pt"])
 
         self.expect("thread trace dump instructions",
-            substrs=['thread #1: tid = 3842849, total instructions = 1000',
-                     'would print 20 instructions from position 0'])
+            substrs=['''thread #1: tid = 3842849, total instructions = 21
+  [ 0] 0x40052d
+  [ 1] 0x400529
+  [ 2] 0x400525
+  [ 3] 0x400521
+  [ 4] 0x40052d
+  [ 5] 0x400529
+  [ 6] 0x400525
+  [ 7] 0x400521
+  [ 8] 0x40052d
+  [ 9] 0x400529
+  [10] 0x400525
+  [11] 0x400521
+  [12] 0x40052d
+  [13] 0x400529
+  [14] 0x400525
+  [15] 0x400521
+  [16] 0x40052d
+  [17] 0x400529
+  [18] 0x40051f'''])
 
         # 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'])
+            substrs=['''thread #1: tid = 3842849, total instructions = 21
+  [10] 0x400525
+  [11] 0x400521
+  [12] 0x40052d
+  [13] 0x400529
+  [14] 0x400525'''])
 
         # 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'])
+            substrs=['''thread #1: tid = 3842849, total instructions = 21
+  [ 0] 0x40052d'''])
 
         # We check that we get an error when using an invalid thread index id
         self.expect("thread trace dump instructions 10", error=True,
@@ -61,32 +83,68 @@
         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'''])
+        self.expect("thread trace dump instructions 1 2 --count 2",
+            substrs=['''thread #1: tid = 3842849, total instructions = 21
+  [0] 0x40052d
+  [1] 0x400529
+thread #2: tid = 3842850, total instructions = 21
+  [0] 0x40052d
+  [1] 0x400529'''])
 
         # 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())
+        ci.HandleCommand("thread trace dump instructions 1 2 --count 4 --start-position 5", result, True)
+        self.assertIn('''thread #1: tid = 3842849, total instructions = 21
+  [5] 0x400529
+  [6] 0x400525
+  [7] 0x400521
+  [8] 0x40052d
+thread #2: tid = 3842850, total instructions = 21
+  [5] 0x400529
+  [6] 0x400525
+  [7] 0x400521
+  [8] 0x40052d''', 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())
+        self.assertIn('''thread #1: tid = 3842849, total instructions = 21
+  [ 9] 0x400529
+  [10] 0x400525
+  [11] 0x400521
+  [12] 0x40052d
+thread #2: tid = 3842850, total instructions = 21
+  [ 9] 0x400529
+  [10] 0x400525
+  [11] 0x400521
+  [12] 0x40052d''', result.GetOutput())
 
+        result = lldb.SBCommandReturnObject()
         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())
+        self.assertIn('''thread #1: tid = 3842849, total instructions = 21
+  [13] 0x400529
+  [14] 0x400525
+  [15] 0x400521
+  [16] 0x40052d
+thread #2: tid = 3842850, total instructions = 21
+  [13] 0x400529
+  [14] 0x400525
+  [15] 0x400521
+  [16] 0x40052d''', result.GetOutput())
+
+    def testWrongImage(self):
+        trace_definition_file = os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json")
+        self.expect("trace load " + trace_definition_file)
+        self.expect("thread trace dump instructions",
+            substrs=['''thread #1: tid = 3842849, total instructions = 3
+  [0] no memory mapped at this address'''])
+
+    def testWrongCPU(self):
+        trace_definition_file = os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")
+        self.expect("trace load " + trace_definition_file)
+        self.expect("thread trace dump instructions",
+            substrs=['''thread #1: tid = 3842849, total instructions = 0
+  Intel PT decoding error -27: 'unknown cpu'''])
Index: lldb/source/Target/TraceThread.cpp
===================================================================
--- lldb/source/Target/TraceThread.cpp
+++ lldb/source/Target/TraceThread.cpp
@@ -1,4 +1,4 @@
-//===-- ThreadIntelPT.cpp -------------------------------------------------===//
+//===-- TraceThread.cpp -------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "ThreadIntelPT.h"
+#include "lldb/Target/TraceThread.h"
 
 #include <memory>
 
@@ -16,11 +16,10 @@
 
 using namespace lldb;
 using namespace lldb_private;
-using namespace lldb_private::trace_intel_pt;
 
-void ThreadIntelPT::RefreshStateAfterStop() {}
+void TraceThread::RefreshStateAfterStop() {}
 
-RegisterContextSP ThreadIntelPT::GetRegisterContext() {
+RegisterContextSP TraceThread::GetRegisterContext() {
   if (!m_reg_context_sp)
     m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
 
@@ -28,11 +27,13 @@
 }
 
 RegisterContextSP
-ThreadIntelPT::CreateRegisterContextForFrame(StackFrame *frame) {
+TraceThread::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 ThreadIntelPT::CalculateStopInfo() { return false; }
+bool TraceThread::CalculateStopInfo() { return false; }
+
+const FileSpec &TraceThread::GetTraceFile() const { return m_trace_file; }
Index: lldb/source/Target/TraceSessionFileParser.cpp
===================================================================
--- lldb/source/Target/TraceSessionFileParser.cpp
+++ lldb/source/Target/TraceSessionFileParser.cpp
@@ -10,8 +10,11 @@
 
 #include <sstream>
 
+#include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
+#include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
+#include "lldb/Target/TraceThread.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -34,7 +37,6 @@
   ModuleSpec module_spec;
   module_spec.GetFileSpec() = local_file_spec;
   module_spec.GetPlatformFileSpec() = system_file_spec;
-  module_spec.SetObjectOffset(module.load_address.value);
 
   if (module.uuid.hasValue())
     module_spec.GetUUID().SetFromStringRef(*module.uuid);
@@ -42,7 +44,14 @@
   Status error;
   ModuleSP module_sp =
       target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
-  return error.ToError();
+
+  if (error.Fail())
+    return error.ToError();
+
+  bool load_addr_changed = false;
+  module_sp->SetLoadAddress(*target_sp, module.load_address.value, false,
+                            load_addr_changed);
+  return llvm::Error::success();
 }
 
 Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root,
@@ -87,6 +96,55 @@
   return schema_builder.str();
 }
 
+void TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
+                                         const JSONThread &thread) {
+  lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
+
+  FileSpec trace_file(thread.trace_file);
+  NormalizePath(trace_file);
+
+  ThreadSP thread_sp =
+      std::make_shared<TraceThread>(*process_sp, tid, trace_file);
+  process_sp->GetThreadList().AddThread(thread_sp);
+}
+
+Expected<TargetSP>
+TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
+  TargetSP target_sp;
+  Status error = m_debugger.GetTargetList().CreateTarget(
+      m_debugger, /*user_exe_path*/ StringRef(), process.triple,
+      eLoadDependentsNo,
+      /*platform_options*/ nullptr, target_sp);
+
+  if (!target_sp)
+    return error.ToError();
+
+  m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
+
+  ProcessSP process_sp(target_sp->CreateProcess(
+      /*listener*/ nullptr, "trace",
+      /*crash_file*/ nullptr));
+  process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
+
+  for (const JSONThread &thread : process.threads)
+    ParseThread(process_sp, thread);
+
+  for (const JSONModule &module : process.modules) {
+    if (Error err = ParseModule(target_sp, module))
+      return std::move(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.
+  ArchSpec process_arch;
+  process_sp->DidAttach(process_arch);
+
+  return target_sp;
+}
+
 namespace llvm {
 namespace json {
 
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -8,8 +8,6 @@
 
 #include "lldb/Target/Trace.h"
 
-#include <sstream>
-
 #include "llvm/Support/Format.h"
 
 #include "lldb/Core/PluginManager.h"
@@ -79,10 +77,41 @@
   return createInvalidPlugInError(name);
 }
 
+static int GetNumberOfDigits(size_t num) {
+  int digits_count = 0;
+  do {
+    digits_count++;
+    num /= 10;
+  } while (num);
+  return digits_count;
+}
+
 void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
-                                  size_t start_position) const {
-  s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = 1000\n",
-           thread.GetIndexID(), thread.GetID());
-  s.Printf("  would print %zu instructions from position %zu\n", count,
-           start_position);
+                                  size_t start_position) {
+  size_t instruction_count = GetInstructionCount(thread);
+  s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n",
+           thread.GetIndexID(), thread.GetID(), instruction_count);
+
+  int digits_count =
+      GetNumberOfDigits(std::min(instruction_count, start_position + count));
+
+  if (Error err = GetTraceErrorStatus(thread))
+    s.Printf("  %s\n", llvm::toString(std::move(err)).c_str());
+
+  ForEachInstruction(
+      thread,
+      [&](size_t index, Expected<lldb::addr_t> load_address) -> bool {
+        if (index >= start_position + count)
+          return false;
+
+        s.Printf("  [%*zu]", digits_count, index);
+        if (load_address)
+          s.Printf(" 0x%" PRIx64, *load_address);
+        else
+          s.Printf(" %s", toString(load_address.takeError()).c_str());
+        s.Printf("\n");
+
+        return true;
+      },
+      start_position);
 }
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -42,6 +42,7 @@
 #include "lldb/Target/ThreadPlanStepThrough.h"
 #include "lldb/Target/ThreadPlanStepUntil.h"
 #include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/Trace.h"
 #include "lldb/Target/UnwindLLDB.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/RegularExpression.h"
@@ -51,6 +52,7 @@
 #include "lldb/lldb-enumerations.h"
 
 #include <memory>
+#include <sstream>
 
 using namespace lldb;
 using namespace lldb_private;
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -67,6 +67,7 @@
   ThreadSpec.cpp
   Trace.cpp
   TraceSessionFileParser.cpp
+  TraceThread.cpp
   UnixSignals.cpp
   UnwindAssembly.cpp
   UnwindLLDB.cpp
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -9,8 +9,6 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
 
-#include "intel-pt.h"
-
 #include "TraceIntelPT.h"
 #include "lldb/Target/TraceSessionFileParser.h"
 
@@ -38,8 +36,8 @@
   TraceIntelPTSessionFileParser(Debugger &debugger,
                                 const llvm::json::Value &trace_session_file,
                                 llvm::StringRef session_file_dir)
-      : TraceSessionFileParser(session_file_dir, GetSchema()),
-        m_debugger(debugger), m_trace_session_file(trace_session_file) {}
+      : TraceSessionFileParser(debugger, session_file_dir, GetSchema()),
+        m_trace_session_file(trace_session_file) {}
 
   /// \return
   ///   The JSON schema for the session data.
@@ -53,24 +51,14 @@
   ///   errors, return a null pointer.
   llvm::Expected<lldb::TraceSP> Parse();
 
-private:
-  llvm::Error ParseImpl();
-
-  llvm::Error ParseProcess(const TraceSessionFileParser::JSONProcess &process);
+  lldb::TraceSP
+  CreateTraceIntelPTInstance(const pt_cpu &pt_cpu,
+                             std::vector<lldb::TargetSP> &targets);
 
-  void ParseThread(lldb::ProcessSP &process_sp,
-                   const TraceSessionFileParser::JSONThread &thread);
-
-  void ParsePTCPU(const JSONPTCPU &pt_cpu);
+private:
+  pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu);
 
-  Debugger &m_debugger;
   const llvm::json::Value &m_trace_session_file;
-
-  /// Objects created as product of the parsing
-  /// \{
-  pt_cpu m_pt_cpu;
-  std::vector<lldb::TargetSP> m_targets;
-  /// \}
 };
 
 } // namespace trace_intel_pt
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
@@ -8,10 +8,10 @@
 
 #include "TraceIntelPTSessionFileParser.h"
 
-#include "ThreadIntelPT.h"
 #include "lldb/Core/Debugger.h"
-#include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Target/TraceThread.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -34,88 +34,54 @@
   return schema;
 }
 
-void TraceIntelPTSessionFileParser::ParseThread(
-    ProcessSP &process_sp, const TraceSessionFileParser::JSONThread &thread) {
-  lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
-
-  FileSpec trace_file(thread.trace_file);
-  NormalizePath(trace_file);
-
-  ThreadSP thread_sp =
-      std::make_shared<ThreadIntelPT>(*process_sp, tid, trace_file);
-  process_sp->GetThreadList().AddThread(thread_sp);
+pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
+  return {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
+          static_cast<uint16_t>(pt_cpu.family),
+          static_cast<uint8_t>(pt_cpu.model),
+          static_cast<uint8_t>(pt_cpu.stepping)};
 }
 
-Error TraceIntelPTSessionFileParser::ParseProcess(
-    const TraceSessionFileParser::JSONProcess &process) {
-  TargetSP target_sp;
-  Status error = m_debugger.GetTargetList().CreateTarget(
-      m_debugger, /*user_exe_path*/ StringRef(), process.triple,
-      eLoadDependentsNo,
-      /*platform_options*/ nullptr, target_sp);
-
-  if (!target_sp)
-    return error.ToError();
-
-  m_targets.push_back(target_sp);
-  m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
-
-  ProcessSP process_sp(target_sp->CreateProcess(
-      /*listener*/ nullptr, "trace",
-      /*crash_file*/ nullptr));
-  process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
-
-  for (const TraceSessionFileParser::JSONThread &thread : process.threads)
-    ParseThread(process_sp, thread);
-
-  for (const TraceSessionFileParser::JSONModule &module : process.modules) {
-    if (Error err = ParseModule(target_sp, module))
-      return err;
+TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
+    const pt_cpu &pt_cpu, std::vector<TargetSP> &targets) {
+  std::vector<std::shared_ptr<TraceThread>> threads;
+  for (TargetSP &target_sp : targets) {
+    ThreadList &thread_list = target_sp->GetProcessSP()->GetThreadList();
+    for (size_t i = 0; i < thread_list.GetSize(); i++) {
+      // The top-level parser creates TraceThreads, so this is safe
+      threads.push_back(std::static_pointer_cast<TraceThread>(
+          thread_list.GetThreadAtIndex(i)));
+    }
   }
 
-  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();
-}
+  TraceSP trace_instance(new TraceIntelPT(pt_cpu, threads));
+  for (const TargetSP &target_sp : targets)
+    target_sp->SetTrace(trace_instance);
 
-void TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
-  m_pt_cpu = {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
-              static_cast<uint16_t>(pt_cpu.family),
-              static_cast<uint8_t>(pt_cpu.model),
-              static_cast<uint8_t>(pt_cpu.stepping)};
+  return trace_instance;
 }
 
-Error TraceIntelPTSessionFileParser::ParseImpl() {
+Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
   json::Path::Root root("traceSession");
   TraceSessionFileParser::JSONTraceSession<JSONTraceIntelPTSettings> session;
-  if (!json::fromJSON(m_trace_session_file, session, root)) {
+  if (!json::fromJSON(m_trace_session_file, session, root))
     return CreateJSONError(root, m_trace_session_file);
-  }
-
-  ParsePTCPU(session.trace.pt_cpu);
-  for (const TraceSessionFileParser::JSONProcess &process : session.processes) {
-    if (Error err = ParseProcess(process))
-      return err;
-  }
-  return Error::success();
-}
 
-Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
-  if (Error err = ParseImpl()) {
-    // Delete all targets that were created
-    for (auto target_sp : m_targets)
+  std::vector<TargetSP> targets;
+  auto onError = [this, &targets]() {
+    // Delete all targets that were created so far in case of failures
+    for (TargetSP &target_sp : targets)
       m_debugger.GetTargetList().DeleteTarget(target_sp);
-    m_targets.clear();
-    return std::move(err);
-  }
+  };
 
-  return TraceIntelPT::CreateInstance(m_pt_cpu, m_targets);
+  for (const TraceSessionFileParser::JSONProcess &process : session.processes) {
+    if (Expected<TargetSP> target_sp = ParseProcess(process))
+      targets.push_back(*target_sp);
+    else {
+      onError();
+      return target_sp.takeError();
+    }
+  }
+  return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.pt_cpu), 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
@@ -9,12 +9,8 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
 
-#include "intel-pt.h"
-#include "llvm/ADT/Optional.h"
-
+#include "IntelPTDecoder.h"
 #include "TraceIntelPTSessionFileParser.h"
-#include "lldb/Target/Trace.h"
-#include "lldb/lldb-private.h"
 
 namespace lldb_private {
 namespace trace_intel_pt {
@@ -52,21 +48,6 @@
   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;
@@ -74,15 +55,43 @@
 
   llvm::StringRef GetSchema() override;
 
+  const pt_cpu &GetIntelPTCPU();
+
+  void ForEachInstruction(
+      const Thread &thread,
+      std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
+          callback,
+      size_t from = 0) override;
+
+  size_t GetInstructionCount(const Thread &thread) override;
+
+  llvm::Error GetTraceErrorStatus(const Thread &thread) override;
+
 private:
-  TraceIntelPT(const pt_cpu &pt_cpu, const std::vector<lldb::TargetSP> &targets)
-      : m_pt_cpu(pt_cpu) {
-    for (const lldb::TargetSP &target_sp : targets)
-      m_targets.push_back(target_sp);
-  }
+  friend class TraceIntelPTSessionFileParser;
+
+  /// \param[in] trace_threads
+  ///     TraceThread instances, which are not live-processes and whose trace
+  ///     files are fixed.
+  TraceIntelPT(const pt_cpu &pt_cpu,
+               const std::vector<std::shared_ptr<TraceThread>> &traced_threads);
+
+  /// Decode the trace of the given thread that, i.e. recontruct the traced
+  /// instructions. That trace must be managed by this class.
+  ///
+  /// \param[in] thread
+  ///     If \a thread is a \a TraceThread, then its internal trace file will be
+  ///     decoded. Live threads are not currently supported.
+  ///
+  /// \return
+  ///   A \a DecodedThread instance if decoding was successful, or an error if
+  ///   the thread's trace is not managed by this class or if the trace couldn't
+  ///   be decoded.
+  llvm::Expected<const DecodedThread &> Decode(const Thread &thread);
 
   pt_cpu m_pt_cpu;
-  std::vector<std::weak_ptr<Target>> m_targets;
+  std::map<std::pair<lldb::pid_t, lldb::tid_t>, TraceThreadDecoder>
+      m_trace_threads;
 };
 
 } // namespace trace_intel_pt
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
@@ -11,6 +11,7 @@
 #include "TraceIntelPTSessionFileParser.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Target/Target.h"
+#include "lldb/Target/TraceThread.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -56,11 +57,58 @@
       .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);
+TraceIntelPT::TraceIntelPT(
+    const pt_cpu &pt_cpu,
+    const std::vector<std::shared_ptr<TraceThread>> &traced_threads)
+    : m_pt_cpu(pt_cpu) {
+  for (const std::shared_ptr<TraceThread> &thread : traced_threads)
+    m_trace_threads.emplace(
+        std::piecewise_construct,
+        std::forward_as_tuple(
+            std::make_pair(thread->GetProcess()->GetID(), thread->GetID())),
+        std::forward_as_tuple(thread, pt_cpu));
+}
+
+const pt_cpu &TraceIntelPT::GetIntelPTCPU() { return m_pt_cpu; }
+
+Expected<const DecodedThread &> TraceIntelPT::Decode(const Thread &thread) {
+  auto it = m_trace_threads.find(
+      std::make_pair(thread.GetProcess()->GetID(), thread.GetID()));
+  if (it == m_trace_threads.end())
+    return createStringError(std::errc::invalid_argument,
+                             "The thread %" PRIu64 " is not traced.",
+                             thread.GetID());
+
+  return it->second.Decode();
+}
+
+void TraceIntelPT::ForEachInstruction(
+    const Thread &thread,
+    std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
+        callback,
+    size_t from) {
+  Expected<const DecodedThread &> decoded_thread = Decode(thread);
+  if (!decoded_thread)
+    return;
+
+  const std::vector<IntelPTInstruction> &instructions =
+      decoded_thread->GetInstructions();
+  for (size_t i = from; i < instructions.size(); i++) {
+    if (!callback(i, instructions[i].GetLoadAddress()))
+      break;
+  }
+}
+
+size_t TraceIntelPT::GetInstructionCount(const Thread &thread) {
+  if (Expected<const DecodedThread &> decoded_thread = Decode(thread))
+    return decoded_thread->GetInstructions().size();
+  else
+    return 0;
+}
 
-  return trace_instance;
+Error TraceIntelPT::GetTraceErrorStatus(const Thread &thread) {
+  if (Expected<const DecodedThread &> decoded_thread = Decode(thread))
+    return Error::success();
+  else
+    return decoded_thread.takeError();
 }
Index: lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//===-- ThreadIntelPT.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_TRACE_INTEL_PT_THREADINTELPT_H
-#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H
-
-#include "lldb/Target/Thread.h"
-
-namespace lldb_private {
-namespace trace_intel_pt {
-
-class ThreadIntelPT : public Thread {
-public:
-  /// Create an Intel PT-traced thread.
-  ///
-  /// \param[in] process
-  ///     The process that owns this thread.
-  ///
-  /// \param[in] tid
-  ///     The thread id of this thread.
-  ///
-  /// \param[in] trace_file
-  ///     The trace file for this thread.
-  ///
-  /// \param[in] pt_cpu
-  ///     The Intel CPU information required to decode the \a trace_file.
-  ThreadIntelPT(Process &process, lldb::tid_t tid, const FileSpec &trace_file)
-      : Thread(process, tid), m_trace_file(trace_file) {}
-
-  void RefreshStateAfterStop() override;
-
-  lldb::RegisterContextSP GetRegisterContext() override;
-
-  lldb::RegisterContextSP
-  CreateRegisterContextForFrame(StackFrame *frame) override;
-
-protected:
-  bool CalculateStopInfo() override;
-
-  lldb::RegisterContextSP m_thread_reg_ctx_sp;
-
-private:
-  FileSpec m_trace_file;
-};
-
-} // namespace trace_intel_pt
-} // namespace lldb_private
-
-#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -0,0 +1,78 @@
+//===-- IntelPTDecoder.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_TRACE_INTEL_PT_DECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
+
+#include "intel-pt.h"
+
+#include "DecodedThread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/FileSpec.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class IntelPTDecoder {
+public:
+  /// \param[in] process
+  ///     The process associated with the traces to decode.
+  ///
+  /// \param[in] pt_cpu
+  ///     The libipt \a pt_cpu information of the CPU where the traces were
+  ///     recorded.
+  IntelPTDecoder(Process &process, const pt_cpu &pt_cpu)
+      : m_process(process), m_pt_cpu(pt_cpu) {}
+
+  /// Decode a single-thread trace file.
+  ///
+  /// \param[in] trace_file
+  ///     The binary trace file obtained from tracing a thread.
+  ///
+  /// \return
+  ///     A \a DecodedTrace instance, or an error if decoding failed.
+  llvm::Expected<DecodedThread>
+  DecodeSingleThreadTraceFile(const FileSpec &trace_file);
+
+private:
+  Process &m_process;
+  pt_cpu m_pt_cpu;
+};
+
+/// \a lldb_private::TraceThread decoder that stores the output from decoding,
+/// avoiding recomputations, as decoding is expensive.
+class TraceThreadDecoder {
+public:
+  /// \param[in] trace_thread
+  ///     The thread whose trace file will be decoded.
+  ///
+  /// \param[in] pt_cpu
+  ///     The libipt cpu used when recording the trace.
+  TraceThreadDecoder(const std::shared_ptr<TraceThread> &trace_thread,
+                     const pt_cpu &pt_cpu)
+      : m_trace_thread(trace_thread), m_pt_cpu(pt_cpu), m_decoded_thread() {}
+
+  /// Decode the thread and store the result internally.
+  ///
+  /// \return
+  ///     A \a DecodedThread instance or an error in case of failures.
+  llvm::Expected<const DecodedThread &> Decode();
+
+private:
+  TraceThreadDecoder(const TraceThreadDecoder &other) = delete;
+
+  std::shared_ptr<TraceThread> m_trace_thread;
+  pt_cpu m_pt_cpu;
+  llvm::Optional<DecodedThread> m_decoded_thread;
+  llvm::Optional<std::string> m_error_message;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -0,0 +1,231 @@
+//===-- IntelPTDecoder.cpp --------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "IntelPTDecoder.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TraceThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+/// \return
+///   A negative number with the libipt error if we couldn't synchronize.
+///   Otherwise, a positive number with the synchronization status will be
+///   returned.
+int FindNextSynchronizationPoint(pt_insn_decoder &decoder) {
+  // Try to sync the decoder. If it fails, then get
+  // the decoder_offset and try to sync again from
+  // the next synchronization point. If the
+  // new_decoder_offset is same as decoder_offset
+  // then we can't move to the next synchronization
+  // point. Otherwise, keep resyncing until either
+  // end of trace stream (eos) is reached or
+  // pt_insn_sync_forward() passes.
+  int errcode = pt_insn_sync_forward(&decoder);
+
+  if (errcode != -pte_eos && errcode < 0) {
+    uint64_t decoder_offset = 0;
+    int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
+    if (errcode_off >= 0) { // we could get the offset
+      while (true) {
+        errcode = pt_insn_sync_forward(&decoder);
+        if (errcode >= 0 || errcode == -pte_eos)
+          break;
+
+        uint64_t new_decoder_offset = 0;
+        errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
+        if (errcode_off < 0)
+          break; // We can't further synchronize.
+        else if (new_decoder_offset <= decoder_offset) {
+          // We tried resyncing the decoder and
+          // decoder didn't make any progress because
+          // the offset didn't change. We will not
+          // make any progress further. Hence,
+          // stopping in this situation.
+          break;
+        }
+        // We'll try again starting from a new offset.
+        decoder_offset = new_decoder_offset;
+      }
+    }
+  }
+
+  return errcode;
+}
+
+/// Decode all the instructions from a configured decoder.
+/// The decoding flow is based on
+/// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop
+/// but with some relaxation to allow for gaps in the trace.
+///
+/// \param[in] decoder
+///   A configured libipt \a pt_insn_decoder.
+///
+/// \return
+///   The decoded instructions.
+static std::vector<IntelPTInstruction>
+DecodeInstructions(pt_insn_decoder &decoder) {
+  std::vector<IntelPTInstruction> instructions;
+
+  // Error codes returned by libipt while decoding are:
+  // - negative: actual errors
+  // - positive or zero: not an error, but a list of bits signaling the status
+  // of the decoder
+  auto handle_pt_events = [&](int errcode) -> int {
+    while (errcode & pts_event_pending) {
+      pt_event event;
+      errcode = pt_insn_event(&decoder, &event, sizeof(event));
+      if (errcode < 0)
+        return errcode;
+
+      // The list of events are in
+      // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_event.3.md
+      if (event.type == ptev_overflow)
+        instructions.emplace_back(-pte_overflow);
+      else if (event.type == ptev_enabled &&
+               event.variant.enabled.resumed == 0 && !instructions.empty()) {
+        /// This means that tracing is enabled from a different IP where it
+        /// stopped before
+        instructions.emplace_back(-pte_noip);
+      }
+      // Other events don't signal stream errors
+    }
+    return 0;
+  };
+
+  while (true) {
+    int errcode = FindNextSynchronizationPoint(decoder);
+    if (errcode == -pte_eos)
+      break;
+
+    if (errcode < 0) {
+      instructions.emplace_back(errcode);
+      break;
+    }
+
+    // We have synchronized, so we can start decoding
+    // instructions and events.
+    while (true) {
+      errcode = handle_pt_events(errcode);
+      if (errcode < 0) {
+        instructions.emplace_back(errcode);
+        break;
+      }
+      pt_insn insn;
+      errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
+      if (insn.iclass != ptic_error)
+        instructions.emplace_back(insn);
+
+      if (errcode == -pte_eos)
+        break;
+
+      if (errcode < 0) {
+        instructions.emplace_back(errcode);
+        break;
+      }
+    }
+  }
+
+  return instructions;
+}
+
+static Error CreateLibiptError(int errcode) {
+  return createStringError(std::errc::invalid_argument,
+                           "Intel PT decoding error %d: '%s'", errcode,
+                           pt_errstr(pt_errcode(errcode)));
+}
+
+/// Callback used by libipt for reading the process memory.
+///
+/// More information can be found in
+/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
+static int ReadProcessMemory(uint8_t *buffer, size_t size,
+                             const pt_asid * /* unused */, uint64_t pc,
+                             void *context) {
+  Process *process = reinterpret_cast<Process *>(context);
+
+  Status error;
+  int bytes_read = process->ReadMemory(pc, buffer, size, error);
+  if (error.Fail())
+    return -pte_nomap;
+  return bytes_read;
+}
+
+static Expected<std::vector<IntelPTInstruction>>
+CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+                       MemoryBuffer &trace) {
+  pt_config config;
+  pt_config_init(&config);
+  config.cpu = pt_cpu;
+
+  if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
+    return CreateLibiptError(errcode);
+
+  config.begin =
+      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart()));
+  config.end =
+      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd()));
+
+  pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
+  if (!decoder)
+    return CreateLibiptError(pte_nomem);
+
+  pt_image *image = pt_insn_get_image(decoder);
+  if (int error = pt_image_set_callback(image, ReadProcessMemory, &process))
+    return CreateLibiptError(error);
+
+  std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder);
+  // We'll need the instructions in reverse order chronologically, so we
+  // reverse them now
+  std::reverse(instructions.begin(), instructions.end());
+
+  pt_insn_free_decoder(decoder);
+  return instructions;
+}
+
+Expected<DecodedThread>
+IntelPTDecoder::DecodeSingleThreadTraceFile(const FileSpec &trace_file) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> buffer_or_error =
+      MemoryBuffer::getFile(trace_file.GetPath());
+  if (std::error_code err = buffer_or_error.getError())
+    return errorCodeToError(err);
+
+  if (Expected<std::vector<IntelPTInstruction>> instructions =
+          CreateDecoderAndDecode(m_process, m_pt_cpu, **buffer_or_error))
+    return DecodedThread(std::move(*instructions));
+  else
+    return instructions.takeError();
+}
+
+Expected<const DecodedThread &> TraceThreadDecoder::Decode() {
+  if (!m_decoded_thread.hasValue() && !m_error_message.hasValue()) {
+    IntelPTDecoder decoder(*m_trace_thread->GetProcess(), m_pt_cpu);
+
+    if (llvm::Expected<DecodedThread> decoded_thread =
+            decoder.DecodeSingleThreadTraceFile(m_trace_thread->GetTraceFile()))
+      m_decoded_thread = *decoded_thread;
+    else
+      // We create a copy of the error message, as we'll create a new instance
+      // of llvm::Error whenever this function is invoked. We have to do this
+      // because llvm::Error is only movable, not copyable.
+      m_error_message = llvm::toString(decoded_thread.takeError());
+  }
+
+  if (m_decoded_thread.hasValue())
+    return *m_decoded_thread;
+  else
+    return llvm::createStringError(std::errc::invalid_argument,
+                                   m_error_message->c_str());
+}
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -0,0 +1,93 @@
+//===-- DecodedThread.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_TRACE_INTEL_PT_DECODEDTHREAD_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
+
+#include <vector>
+
+#include "lldb/Target/Trace.h"
+
+#include "intel-pt.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// \class IntelPTInstruction
+/// An instruction obtained from decoding a trace. It is either an actual
+/// instruction or an error indicating a gap in the trace.
+///
+/// Gaps in the trace can come in a few flavors:
+///   - tracing gaps (e.g. tracing was paused and then resumed)
+///   - tracing errors (e.g. buffer overflow)
+///   - decoding errors (e.g. some memory region couldn't be decoded)
+/// As mentioned, any gap is represented as an error in this class.
+class IntelPTInstruction {
+public:
+  IntelPTInstruction() = delete;
+
+  IntelPTInstruction(const pt_insn &pt_insn)
+      : m_pt_insn(pt_insn), m_libipt_error_code(0) {}
+
+  IntelPTInstruction(int libipt_error_code)
+      : m_libipt_error_code(libipt_error_code) {}
+
+  /// Check if this object represents an error (i.e. a gap).
+  ///
+  /// \return
+  ///     Whether this object represents an error.
+  bool IsError() const;
+
+  /// Get the instruction pointer if this is not an error.
+  ///
+  /// \return
+  ///     The instruction pointer address.
+  llvm::Expected<lldb::addr_t> GetLoadAddress() const;
+
+  /// Return the libipt error code.
+  ///
+  /// libipt error codes are negative numbers.
+  ///
+  /// \return
+  ///     0 if it is not an error, or the error value otherwise.
+  int GetErrorCode() const;
+
+  /// Return a descriptive error message if this is an error.
+  ///
+  /// \return
+  ///    The error message.
+  llvm::StringRef GetErrorMessage() const;
+
+private:
+  pt_insn m_pt_insn;
+  int m_libipt_error_code;
+};
+
+/// \class DecodedThread
+/// Class holding the instructions and function call hierarchy obtained from
+/// decoding a trace.
+class DecodedThread {
+public:
+  DecodedThread(std::vector<IntelPTInstruction> &&instructions)
+      : m_instructions(std::move(instructions)) {}
+
+  /// Get the instructions from the decoded trace. Some of them might indicate
+  /// errors (i.e. gaps) in the trace.
+  ///
+  /// \return
+  ///   The instructions of the trace.
+  llvm::ArrayRef<IntelPTInstruction> GetInstructions() const;
+
+private:
+  std::vector<IntelPTInstruction> m_instructions;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -0,0 +1,32 @@
+//===-- DecodedThread.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 "DecodedThread.h"
+
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+bool IntelPTInstruction::IsError() const { return m_libipt_error_code < 0; }
+
+Expected<lldb::addr_t> IntelPTInstruction::GetLoadAddress() const {
+  if (IsError())
+    return createStringError(std::errc::invalid_argument,
+                             GetErrorMessage().data());
+  return m_pt_insn.ip;
+}
+
+int IntelPTInstruction::GetErrorCode() const { return m_libipt_error_code; }
+
+llvm::StringRef IntelPTInstruction::GetErrorMessage() const {
+  return pt_errstr(pt_errcode(GetErrorCode()));
+}
+
+ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
+  return makeArrayRef(m_instructions);
+}
Index: lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
+++ lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -10,9 +10,10 @@
 find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH} REQUIRED)
 
 add_lldb_library(lldbPluginTraceIntelPT PLUGIN
+  DecodedThread.cpp
+  IntelPTDecoder.cpp
   TraceIntelPT.cpp
   TraceIntelPTSessionFileParser.cpp
-  ThreadIntelPT.cpp
 
   LINK_LIBS
     lldbCore
Index: lldb/source/Plugins/Process/Trace/ProcessTrace.h
===================================================================
--- lldb/source/Plugins/Process/Trace/ProcessTrace.h
+++ lldb/source/Plugins/Process/Trace/ProcessTrace.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
 #define LLDB_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
 
+#include "lldb/Target/MemoryRegionInfo.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/Status.h"
@@ -78,6 +79,13 @@
 
   bool UpdateThreadList(ThreadList &old_thread_list,
                         ThreadList &new_thread_list) override;
+
+private:
+  /// Create a map from virtual memory ranges to the sections that contain them.
+  const std::map<MemoryRegionInfo::RangeType, lldb::SectionSP> &GetSectionMap();
+
+  llvm::Optional<std::map<MemoryRegionInfo::RangeType, lldb::SectionSP>>
+      m_section_map;
 };
 
 } // namespace process_trace
Index: lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
===================================================================
--- lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
+++ lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
@@ -12,6 +12,8 @@
 
 #include "lldb/Core/Module.h"
 #include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 
 using namespace lldb;
@@ -109,6 +111,33 @@
   return GetTarget().GetArchitecture();
 }
 
+const std::map<MemoryRegionInfo::RangeType, SectionSP> &
+ProcessTrace::GetSectionMap() {
+  if (m_section_map)
+    return *m_section_map;
+
+  m_section_map.emplace();
+
+  ModuleList &modules = GetTarget().GetImages();
+  SectionLoadList &load_list = GetTarget().GetSectionLoadList();
+  modules.ForEach([&](const ModuleSP &module_sp) {
+    SectionList *sections = module_sp->GetSectionList();
+    for (size_t i = 0; i < sections->GetSize(); i++) {
+      SectionSP section_sp = sections->GetSectionAtIndex(i);
+
+      addr_t load_addr = load_list.GetSectionLoadAddress(section_sp);
+      if (load_addr == LLDB_INVALID_ADDRESS)
+        continue;
+      MemoryRegionInfo::RangeType section_range(load_addr,
+                                                section_sp->GetByteSize());
+      m_section_map->insert(std::make_pair(section_range, section_sp));
+    }
+    return true;
+  });
+
+  return *m_section_map;
+}
+
 bool ProcessTrace::GetProcessInfo(ProcessInstanceInfo &info) {
   info.Clear();
   info.SetProcessID(GetID());
@@ -124,5 +153,25 @@
 
 size_t ProcessTrace::DoReadMemory(addr_t addr, void *buf, size_t size,
                                   Status &error) {
+  const std::map<MemoryRegionInfo::RangeType, SectionSP> &section_map =
+      GetSectionMap();
+
+  MemoryRegionInfo::RangeType address_range(addr, size);
+  // Doing (upper_bound - 1) gives us the maximum range whose base address is <=
+  // addr
+  auto it = section_map.upper_bound(address_range);
+  if (it != section_map.begin()) {
+    --it;
+    const SectionSP &section = it->second;
+    const MemoryRegionInfo::RangeType &section_range = it->first;
+
+    if (section_range.Contains(address_range) && section->GetObjectFile()) {
+      section->GetObjectFile()->CopyData(addr - section_range.GetRangeBase(),
+                                         size, buf);
+      return size;
+    }
+  }
+
+  error.SetErrorString("could not parse memory info");
   return 0;
 }
Index: lldb/include/lldb/lldb-forward.h
===================================================================
--- lldb/include/lldb/lldb-forward.h
+++ lldb/include/lldb/lldb-forward.h
@@ -228,6 +228,7 @@
 class ThreadSpec;
 class Trace;
 class TraceSessionFileParser;
+class TraceThread;
 class TraceOptions;
 class Type;
 class TypeAndOrName;
Index: lldb/include/lldb/Target/TraceThread.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/TraceThread.h
@@ -0,0 +1,59 @@
+//===-- TraceThread.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_TARGET_TRACETHREAD_H
+#define LLDB_TARGET_TRACETHREAD_H
+
+#include "lldb/Target/Thread.h"
+
+namespace lldb_private {
+
+/// \class TraceThread TraceThread.h
+///
+/// Thread implementation used for representing threads gotten from trace
+/// session files, which are similar to threads from core files.
+///
+/// See \a TraceSessionFileParser for more information regarding trace session
+/// files.
+class TraceThread : public Thread {
+public:
+  /// \param[in] process
+  ///     The process who owns this thread.
+  ///
+  /// \param[in] tid
+  ///     The tid of this thread.
+  ///
+  /// \param[in] trace_file.
+  ///     The file that contains the list of instructions that were traced when
+  ///     this thread was being executed.
+  TraceThread(Process &process, lldb::tid_t tid, const FileSpec trace_file)
+      : Thread(process, tid), m_trace_file(trace_file) {}
+
+  void RefreshStateAfterStop() override;
+
+  lldb::RegisterContextSP GetRegisterContext() override;
+
+  lldb::RegisterContextSP
+  CreateRegisterContextForFrame(StackFrame *frame) override;
+
+  /// \return
+  ///   The trace file of this thread.
+  const FileSpec &GetTraceFile() const;
+
+protected:
+  bool CalculateStopInfo() override;
+
+  lldb::RegisterContextSP m_thread_reg_ctx_sp;
+
+private:
+  FileSpec m_trace_file;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACETHREAD_H
Index: lldb/include/lldb/Target/TraceSessionFileParser.h
===================================================================
--- lldb/include/lldb/Target/TraceSessionFileParser.h
+++ lldb/include/lldb/Target/TraceSessionFileParser.h
@@ -61,9 +61,10 @@
   };
   /// \}
 
-  TraceSessionFileParser(llvm::StringRef session_file_dir,
+  TraceSessionFileParser(Debugger &debugger, llvm::StringRef session_file_dir,
                          llvm::StringRef schema)
-      : m_session_file_dir(session_file_dir), m_schema(schema) {}
+      : m_debugger(debugger), m_session_file_dir(session_file_dir),
+        m_schema(schema) {}
 
   /// Build the full schema for a Trace plug-in.
   ///
@@ -80,6 +81,10 @@
   /// modifies the given file_spec.
   void NormalizePath(lldb_private::FileSpec &file_spec);
 
+  void ParseThread(lldb::ProcessSP &process_sp, const JSONThread &thread);
+
+  llvm::Expected<lldb::TargetSP> ParseProcess(const JSONProcess &process);
+
   llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module);
 
   /// Create a user-friendly error message upon a JSON-parsing failure using the
@@ -96,6 +101,7 @@
   llvm::Error CreateJSONError(llvm::json::Path::Root &root,
                               const llvm::json::Value &value);
 
+  Debugger &m_debugger;
   std::string m_session_file_dir;
   llvm::StringRef m_schema;
 };
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -117,7 +117,58 @@
   /// \param[in] start_position
   ///     The position of the first instruction to print.
   void DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
-                             size_t start_position) const;
+                             size_t start_position);
+
+  /// Run the provided callback on the instructions of the trace of the given
+  /// thread.
+  ///
+  /// The instructions will be traversed starting at the \a from position
+  /// sequentially until the callback returns \b false, in which case no more
+  /// instructions are inspected.
+  ///
+  /// The purpose of this method is to allow inspecting traced instructions
+  /// without exposing the internal representation of how they are stored on
+  /// memory.
+  ///
+  /// \param[in] thread
+  ///     The thread whose trace will be traversed.
+  ///
+  /// \param[in] callback
+  ///     The callback to execute on each instruction. If it returns \b true for
+  ///     the \a i-th instruction, then the instruction with index \a i + 1 will
+  ///     be inspected. If it returns \b false, no more instructions will be
+  ///     inspected.
+  ///
+  /// \param[in] from
+  ///     The first index to execute the callback on.
+  virtual void ForEachInstruction(
+      const Thread &thread,
+      std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
+          callback,
+      size_t from = 0) = 0;
+
+  /// Get the number of instructions of the trace of the given thread.
+  ///
+  /// \param[in] thread
+  ///     The thread whose trace will be inspected.
+  ///
+  /// \return
+  ///     The total number of instructions of the trace.
+  virtual size_t GetInstructionCount(const Thread &thread) = 0;
+
+  /// Get any errors in case the trace of the given thread can't be
+  /// reconstructed.
+  ///
+  /// If this returns an actual error, then no instructions are available in the
+  /// trace.
+  ///
+  /// \param[in] thread
+  ///     The thread whose trace will be inspected.
+  ///
+  /// \return
+  ///     An \a llvm::Error::success instance if the trace can be reconstructed,
+  ///     or an actual Error in case of failures.
+  virtual llvm::Error GetTraceErrorStatus(const Thread &thread) = 0;
 };
 
 } // 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