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

nits


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/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,41 @@
             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 = 22
+  [ 0] 0x40052d
+  [ 1] 0x40052d
+  [ 2] 0x400529
+  [ 3] 0x400525
+  [ 4] 0x400521
+  [ 5] 0x40052d
+  [ 6] 0x400529
+  [ 7] 0x400525
+  [ 8] 0x400521
+  [ 9] 0x40052d
+  [10] 0x400529
+  [11] 0x400525
+  [12] 0x400521
+  [13] 0x40052d
+  [14] 0x400529
+  [15] 0x400525
+  [16] 0x400521
+  [17] 0x40052d
+  [18] 0x400529
+  [19] 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 = 22
+  [10] 0x400529
+  [11] 0x400525
+  [12] 0x400521
+  [13] 0x40052d
+  [14] 0x400529'''])
 
         # 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 = 22
+  [ 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 +84,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 = 22
+  [0] 0x40052d
+  [1] 0x40052d
+thread #2: tid = 3842850, total instructions = 22
+  [0] 0x40052d
+  [1] 0x40052d'''])
 
         # 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 = 22
+  [5] 0x40052d
+  [6] 0x400529
+  [7] 0x400525
+  [8] 0x400521
+thread #2: tid = 3842850, total instructions = 22
+  [5] 0x40052d
+  [6] 0x400529
+  [7] 0x400525
+  [8] 0x400521''', 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 = 22
+  [ 9] 0x40052d
+  [10] 0x400529
+  [11] 0x400525
+  [12] 0x400521
+thread #2: tid = 3842850, total instructions = 22
+  [ 9] 0x40052d
+  [10] 0x400529
+  [11] 0x400525
+  [12] 0x400521''', 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 = 22
+  [13] 0x40052d
+  [14] 0x400529
+  [15] 0x400525
+  [16] 0x400521
+thread #2: tid = 3842850, total instructions = 22
+  [13] 0x40052d
+  [14] 0x400529
+  [15] 0x400525
+  [16] 0x400521''', 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,40 @@
   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 = IsTraceFailed(thread))
+    s.Printf("  %s\n", llvm::toString(std::move(err)).c_str());
+
+  ForEachInstruction(thread,
+                     [&](size_t index, const Trace::Instruction &insn) -> bool {
+                       if (index >= start_position + count)
+                         return false;
+
+                       s.Printf("  [%*zu]", digits_count, index);
+                       if (insn.IsError())
+                         s.Printf(" %s", insn.GetErrorMessage().data());
+                       else
+                         s.Printf(" 0x%" PRIx64, insn.GetLoadAddress());
+                       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,42 @@
 
   llvm::StringRef GetSchema() override;
 
+  const pt_cpu &GetIntelPTCPU();
+
+  void ForEachInstruction(
+      const Thread &thread,
+      std::function<bool(size_t index, const Instruction &insn)> callback,
+      size_t from = 0) override;
+
+  size_t GetInstructionCount(const Thread &thread) override;
+
+  llvm::Error IsTraceFailed(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,57 @@
       .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, const Instruction &insn)> 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]))
+      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::IsTraceFailed(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,244 @@
+//===-- 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 <fstream>
+
+#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;
+
+/// Read the bytes from a trace file
+static std::vector<uint8_t> ReadTraceFile(const FileSpec &trace_file) {
+  char file_path[PATH_MAX];
+  trace_file.GetPath(file_path, sizeof(file_path));
+  std::ifstream src(file_path, std::ios::in | std::ios_base::binary);
+  std::vector<uint8_t> trace((std::istreambuf_iterator<char>(src)),
+                             std::istreambuf_iterator<char>());
+  return trace;
+}
+
+/// Get a list of the sections of the given process that are Read or Exec,
+/// which are the only sections that could correspond to instructions traced.
+std::vector<lldb::SectionSP> static GetReadExecSections(Process &process) {
+  Target &target = process.GetTarget();
+
+  ModuleList &module_list = target.GetImages();
+  std::vector<SectionSP> sections;
+  for (size_t i = 0; i < module_list.GetSize(); i++) {
+    ModuleSP module_sp = module_list.GetModuleAtIndex(i);
+    SectionList *section_list = module_sp->GetSectionList();
+    for (size_t j = 0; j < section_list->GetSize(); j++) {
+      SectionSP section_sp = section_list->GetSectionAtIndex(j);
+      if (!section_sp)
+        continue;
+
+      uint32_t permissions = section_sp->GetPermissions();
+      if ((permissions & Permissions::ePermissionsReadable) &&
+          (permissions & Permissions::ePermissionsExecutable)) {
+        sections.push_back(section_sp);
+      }
+    }
+  }
+  return sections;
+}
+
+/// 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.
+///
+/// \param[out] instructions
+///   The instructions decoded.
+void 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.push_back(IntelPTInstruction(-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.push_back(IntelPTInstruction(-pte_noip));
+      }
+      // Other events don't signal stream errors
+    }
+    return 0;
+  };
+
+  int errcode = 0;
+  while (true) {
+    // 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.
+    errcode = pt_insn_sync_forward(&decoder);
+    if (errcode == -pte_eos)
+      return;
+
+    if (errcode < 0) {
+      // There's a gap in the trace because we
+      // couldn't synchronize.
+      instructions.push_back(IntelPTInstruction(errcode));
+
+      uint64_t decoder_offset = 0;
+      int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
+      if (errcode_off < 0) {
+        // We can't further synchronize.
+        return;
+      }
+
+      while (true) {
+        errcode = pt_insn_sync_forward(&decoder);
+        if (errcode >= 0)
+          break;
+
+        if (errcode == -pte_eos)
+          return;
+
+        uint64_t new_decoder_offset = 0;
+        errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
+        if (errcode_off < 0) {
+          // We can't further synchronize.
+          return;
+        } 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,
+          // returning in this situation.
+          return;
+        }
+        // We'll try again starting from a new offset.
+        decoder_offset = new_decoder_offset;
+      }
+    }
+
+    // We have synchronized, so we can start decoding
+    // instructions and events.
+    while (true) {
+      errcode = handle_pt_events(errcode);
+      if (errcode < 0) {
+        instructions.push_back(IntelPTInstruction(errcode));
+        break;
+      }
+      pt_insn insn;
+      errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
+      if (insn.iclass != ptic_error)
+        instructions.push_back(IntelPTInstruction(insn));
+
+      if (errcode == -pte_eos)
+        return;
+
+      if (errcode < 0) {
+        instructions.push_back(IntelPTInstruction(errcode));
+        break;
+      }
+    }
+  }
+}
+
+/// \param[out] instructions
+///     Container were decoded instructions will be stored.
+///
+/// \return
+///     A libipt error code in case of failure or 0 otherwise.
+int CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+                           std::vector<uint8_t> &trace,
+                           std::vector<IntelPTInstruction> &instructions) {
+  pt_config config;
+  pt_config_init(&config);
+  config.cpu = pt_cpu;
+
+  if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
+    return errcode;
+
+  config.begin = trace.data();
+  config.end = trace.data() + trace.size();
+
+  pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
+  if (decoder == nullptr)
+    return pte_nomem;
+
+  pt_image *image = pt_insn_get_image(decoder);
+  for (SectionSP &section_sp : GetReadExecSections(process)) {
+    char path[PATH_MAX];
+    section_sp->GetModule()->GetPlatformFileSpec().GetPath(path, sizeof(path));
+    if (int errcode = pt_image_add_file(
+            image, path, section_sp->GetFileOffset(), section_sp->GetByteSize(),
+            nullptr, section_sp->GetLoadBaseAddress(&process.GetTarget()))) {
+      pt_insn_free_decoder(decoder);
+      return errcode;
+    }
+  }
+  DecodeInstructions(*decoder, instructions);
+  // 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 pte_ok;
+}
+
+Expected<DecodedThread>
+IntelPTDecoder::DecodeSingleThreadTraceFile(const FileSpec &trace_file) {
+  std::vector<IntelPTInstruction> instructions;
+  std::vector<uint8_t> trace = ReadTraceFile(trace_file);
+  if (int errcode =
+          CreateDecoderAndDecode(m_process, m_pt_cpu, trace, instructions))
+    return createStringError(std::errc::invalid_argument,
+                             "Intel PT decoding error %d: '%s'", errcode,
+                             pt_errstr(pt_errcode(errcode)));
+  return DecodedThread(std::move(instructions));
+}
+
+Expected<const DecodedThread &> TraceThreadDecoder::Decode() {
+  if (!m_decoded_thread.hasValue() && !m_error_message.hasValue()) {
+    if (llvm::Expected<DecodedThread> decoded_thread =
+            IntelPTDecoder(*m_trace_thread->GetProcess(), m_pt_cpu)
+                .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 Trace::Instruction {
+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 override;
+
+  /// Get the instruction pointer if this is not an error.
+  ///
+  /// \return
+  ///     The instruction pointer address.
+  lldb::addr_t GetLoadAddress() const override;
+
+  /// 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 override;
+
+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(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.
+  const std::vector<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,26 @@
+//===-- 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;
+
+bool IntelPTInstruction::IsError() const { return m_libipt_error_code < 0; }
+
+lldb::addr_t IntelPTInstruction::GetLoadAddress() const { 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()));
+}
+
+const std::vector<IntelPTInstruction> &DecodedThread::GetInstructions() const {
+  return 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/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
@@ -35,6 +35,29 @@
 class Trace : public PluginInterface,
               public std::enable_shared_from_this<Trace> {
 public:
+  /// Basic instruction gotten from a trace.
+  ///
+  /// Some instructions might represent errors (i.e. gaps) in the trace, as
+  /// there can be collection or reconstruction problems. Thus, users of this
+  /// class should always check if the instruction corresponds to an error or
+  /// not.
+  struct Instruction {
+    virtual ~Instruction() = default;
+
+    /// \return
+    ///   \b true if this instruction corresponds to an error, or \b false
+    ///   otherwise.
+    virtual bool IsError() const = 0;
+
+    /// \return
+    ///   An error message if this instructions corresponds to an error.
+    virtual llvm::StringRef GetErrorMessage() const = 0;
+
+    /// \return
+    ///   The load address of this instruction if it is not an error.
+    virtual lldb::addr_t GetLoadAddress() const = 0;
+  };
+
   /// Dump the trace data that this plug-in has access to.
   ///
   /// This function will dump all of the trace data for all threads in a user
@@ -117,7 +140,57 @@
   /// \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, const Instruction &insn)> 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 IsTraceFailed(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