[Lldb-commits] [lldb] a19fcc2 - [trace][intelpt] Support system-wide tracing [14] - Decode per cpu

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Thu Jun 16 11:23:14 PDT 2022


Author: Walter Erquinigo
Date: 2022-06-16T11:23:01-07:00
New Revision: a19fcc2bec81989f90700cfc63b89a0dfd330197

URL: https://github.com/llvm/llvm-project/commit/a19fcc2bec81989f90700cfc63b89a0dfd330197
DIFF: https://github.com/llvm/llvm-project/commit/a19fcc2bec81989f90700cfc63b89a0dfd330197.diff

LOG: [trace][intelpt] Support system-wide tracing [14] - Decode per cpu

This is the final functional patch to support intel pt decoding per cpu.
It works by doing the following:

- First, all context switches are split by tid and sorted in order. This produces a list of continuous executes per thread per core.
- Then, all intel pt subtraces are split by PSB boundaries and assigned to individual thread continuous executions on the same core by doing simple TSC-based comparisons.
- With this, we have, per thread, a sorted list of continuous executions each one with a list of intel pt subtraces. Up to this point, this is really fast because no instructions were actually decoded.
- Then, each thread can be decoded by traversing their continuous executions and intel pt subtraces. An advantage of having these continuous executions is that we can identify if a continuous exexecution doesn't have intel pt data, and thus has a gap in it. We can later to more sofisticated comparisons to identify if within a continuous execution there are gaps.

I'm adding a test as well.

Differential Revision: https://reviews.llvm.org/D126394

Added: 
    lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
    lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.intelpt_trace
    lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.perf_context_switch_trace
    lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.intelpt_trace
    lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.perf_context_switch_trace
    lldb/test/API/commands/trace/intelpt-multi-core-trace/modules/m.out
    lldb/test/API/commands/trace/intelpt-multi-core-trace/multi_thread.cpp
    lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json

Modified: 
    lldb/include/lldb/Target/Trace.h
    lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
    lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
    lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
    lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h
    lldb/source/Target/Trace.cpp
    lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
    lldb/test/API/commands/trace/TestTraceLoad.py
    lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
    lldb/unittests/Process/Linux/PerfTests.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index 233efcbf5ddd6..89e129079393b 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -240,6 +240,9 @@ class Trace : public PluginInterface,
 
   using OnBinaryDataReadCallback =
       std::function<llvm::Error(llvm::ArrayRef<uint8_t> data)>;
+  using OnCoresBinaryDataReadCallback = std::function<llvm::Error(
+      const llvm::DenseMap<lldb::core_id_t, llvm::ArrayRef<uint8_t>>
+          &core_to_data)>;
 
   /// Fetch binary data associated with a thread, either live or postmortem, and
   /// pass it to the given callback. The reason of having a callback is to free
@@ -292,6 +295,12 @@ class Trace : public PluginInterface,
                                    llvm::StringRef kind,
                                    OnBinaryDataReadCallback callback);
 
+  /// Similar to \a OnCoreBinaryDataRead but this is able to fetch the same data
+  /// from multiple cores at once.
+  llvm::Error OnCoresBinaryDataRead(const std::set<lldb::core_id_t> core_ids,
+                                    llvm::StringRef kind,
+                                    OnCoresBinaryDataReadCallback callback);
+
   /// \return
   ///     All the currently traced processes.
   std::vector<Process *> GetAllProcesses();
@@ -518,7 +527,12 @@ class Trace : public PluginInterface,
 
   /// core id -> data kind -> size
   llvm::DenseMap<lldb::core_id_t, std::unordered_map<std::string, uint64_t>>
+      m_live_core_data_sizes;
+  /// core id -> data kind -> bytes
+  llvm::DenseMap<lldb::core_id_t,
+                 std::unordered_map<std::string, std::vector<uint8_t>>>
       m_live_core_data;
+
   /// data kind -> size
   std::unordered_map<std::string, uint64_t> m_live_process_data;
   /// \}

diff  --git a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
index 23030f6e78e84..b6acd741747aa 100644
--- a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
@@ -80,9 +80,9 @@ struct LinuxPerfZeroTscConversion {
   ///
   /// \return
   ///   Nanosecond wall time.
-  std::chrono::nanoseconds ToNanos(uint64_t tsc) const;
+  uint64_t ToNanos(uint64_t tsc) const;
 
-  uint64_t ToTSC(std::chrono::nanoseconds nanos) const;
+  uint64_t ToTSC(uint64_t nanos) const;
 
   uint32_t time_mult;
   uint16_t time_shift;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
index 104f29a85f28f..4d55521648bc8 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
+++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -18,6 +18,7 @@ add_lldb_library(lldbPluginTraceIntelPT PLUGIN
   DecodedThread.cpp
   TaskTimer.cpp
   LibiptDecoder.cpp
+  PerfContextSwitchDecoder.cpp
   ThreadDecoder.cpp
   TraceCursorIntelPT.cpp
   TraceIntelPT.cpp

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
index d7654181f3650..3899c4337e15a 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -243,10 +243,10 @@ class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
 
   lldb::ThreadSP GetThread();
 
-private:
   /// Append a decoding error given an llvm::Error.
   void AppendError(llvm::Error &&error);
 
+private:
   /// Notify this class that the last added instruction or error has
   /// an associated TSC.
   void RecordTscForLastInstruction(uint64_t tsc);

diff  --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
index 913db5eefc4ea..500e545e0c1e6 100644
--- a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
@@ -50,18 +50,33 @@ class LibiptDecoder {
 
   /// Decode all the instructions until the end of the trace.
   /// 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.
+  /// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop.
   void DecodeUntilEndOfTrace() {
-    int status = pte_ok;
-    while (!IsLibiptError(status = FindNextSynchronizationPoint())) {
-      // We have synchronized, so we can start decoding instructions and
-      // events.
-      // Multiple loops indicate gaps in the trace.
+    // Multiple loops indicate gaps in the trace, which are found by the inner
+    // call to DecodeInstructionsAndEvents.
+    while (true) {
+      int status = pt_insn_sync_forward(&m_decoder);
+
+      if (IsLibiptError(status)) {
+        m_decoded_thread.Append(DecodedInstruction(status));
+        break;
+      }
+
       DecodeInstructionsAndEvents(status);
     }
   }
 
+  /// Decode all the instructions that belong to the same PSB packet given its
+  /// offset.
+  void DecodePSB(uint64_t psb_offset) {
+    int status = pt_insn_sync_set(&m_decoder, psb_offset);
+    if (IsLibiptError(status)) {
+      m_decoded_thread.Append(DecodedInstruction(status));
+      return;
+    }
+    DecodeInstructionsAndEvents(status, /*stop_on_psb_change=*/true);
+  }
+
 private:
   /// Invoke the low level function \a pt_insn_next and store the decoded
   /// instruction in the given \a DecodedInstruction.
@@ -75,13 +90,37 @@ class LibiptDecoder {
     return pt_insn_next(&m_decoder, &insn.pt_insn, sizeof(insn.pt_insn));
   }
 
-  /// Decode all the instructions and events until an error is found or the end
-  /// of the trace is reached.
+  /// Decode all the instructions and events until an error is found, the end
+  /// of the trace is reached, or optionally a new PSB is reached.
   ///
   /// \param[in] status
   ///   The status that was result of synchronizing to the most recent PSB.
-  void DecodeInstructionsAndEvents(int status) {
-    while (DecodedInstruction insn = ProcessPTEvents(status)) {
+  ///
+  /// \param[in] stop_on_psb_change
+  ///   If \b true, decoding
+  ///   An optional offset to a given PSB. Decoding stops if a 
diff erent PSB is
+  ///   reached.
+  void DecodeInstructionsAndEvents(int status,
+                                   bool stop_on_psb_change = false) {
+    uint64_t psb_offset;
+    pt_insn_get_sync_offset(&m_decoder,
+                            &psb_offset); // this can't fail because we got here
+
+    while (true) {
+      DecodedInstruction insn = ProcessPTEvents(status);
+      if (!insn) {
+        m_decoded_thread.Append(insn);
+        break;
+      }
+
+      if (stop_on_psb_change) {
+        uint64_t cur_psb_offset;
+        pt_insn_get_sync_offset(
+            &m_decoder, &cur_psb_offset); // this can't fail because we got here
+        if (cur_psb_offset != psb_offset)
+          break;
+      }
+
       // The status returned by DecodeNextInstruction will need to be processed
       // by ProcessPTEvents in the next loop if it is not an error.
       if (IsLibiptError(status = DecodeNextInstruction(insn))) {
@@ -112,31 +151,6 @@ class LibiptDecoder {
     // of trace stream (eos) is reached or pt_insn_sync_forward() passes.
     int status = pt_insn_sync_forward(&m_decoder);
 
-    if (!IsEndOfStream(status) && IsLibiptError(status)) {
-      uint64_t decoder_offset = 0;
-      int errcode_off = pt_insn_get_offset(&m_decoder, &decoder_offset);
-      if (!IsLibiptError(errcode_off)) { // we could get the offset
-        while (true) {
-          status = pt_insn_sync_forward(&m_decoder);
-          if (!IsLibiptError(status) || IsEndOfStream(status))
-            break;
-
-          uint64_t new_decoder_offset = 0;
-          errcode_off = pt_insn_get_offset(&m_decoder, &new_decoder_offset);
-          if (IsLibiptError(errcode_off))
-            break; // We can't further synchronize.
-          else if (new_decoder_offset <= decoder_offset) {
-            // We tried resyncing the decoder and it didn't make any progress
-            // because the offset didn't change. We will not make any further
-            // progress. Hence, we stop in this situation.
-            break;
-          }
-          // We'll try again starting from a new offset.
-          decoder_offset = new_decoder_offset;
-        }
-      }
-    }
-
     // We make this call to record any synchronization errors.
     if (IsLibiptError(status))
       m_decoded_thread.Append(DecodedInstruction(status));
@@ -148,8 +162,6 @@ class LibiptDecoder {
   /// instruction e.g. timing events like ptev_tick, or paging events like
   /// ptev_paging.
   ///
-  /// If an error is found, it will be appended to the trace.
-  ///
   /// \param[in] status
   ///   The status gotten from the previous instruction decoding or PSB
   ///   synchronization.
@@ -199,8 +211,6 @@ class LibiptDecoder {
     RefreshTscInfo();
     if (m_tsc_info)
       insn.tsc = m_tsc_info.tsc;
-    if (!insn)
-      m_decoded_thread.Append(insn);
     return insn;
   }
 
@@ -264,8 +274,7 @@ using PtInsnDecoderUP =
     std::unique_ptr<pt_insn_decoder, decltype(DecoderDeleter)>;
 
 static Expected<PtInsnDecoderUP>
-CreateInstructionDecoder(DecodedThread &decoded_thread,
-                         TraceIntelPT &trace_intel_pt,
+CreateInstructionDecoder(TraceIntelPT &trace_intel_pt,
                          ArrayRef<uint8_t> buffer) {
   Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
   if (!cpu_info)
@@ -287,25 +296,151 @@ CreateInstructionDecoder(DecodedThread &decoded_thread,
   pt_insn_decoder *decoder_ptr = pt_insn_alloc_decoder(&config);
   if (!decoder_ptr)
     return make_error<IntelPTError>(-pte_nomem);
-  PtInsnDecoderUP decoder_up(decoder_ptr, DecoderDeleter);
 
-  pt_image *image = pt_insn_get_image(decoder_ptr);
-  Process *process = decoded_thread.GetThread()->GetProcess().get();
+  return PtInsnDecoderUP(decoder_ptr, DecoderDeleter);
+}
+
+static Error SetupMemoryImage(PtInsnDecoderUP &decoder_up, Process &process) {
+  pt_image *image = pt_insn_get_image(decoder_up.get());
 
+  int status = pte_ok;
   if (IsLibiptError(
-          status = pt_image_set_callback(image, ReadProcessMemory, process)))
+          status = pt_image_set_callback(image, ReadProcessMemory, &process)))
     return make_error<IntelPTError>(status);
-  return decoder_up;
+  return Error::success();
 }
 
 void lldb_private::trace_intel_pt::DecodeTrace(DecodedThread &decoded_thread,
                                                TraceIntelPT &trace_intel_pt,
                                                ArrayRef<uint8_t> buffer) {
   Expected<PtInsnDecoderUP> decoder_up =
-      CreateInstructionDecoder(decoded_thread, trace_intel_pt, buffer);
+      CreateInstructionDecoder(trace_intel_pt, buffer);
   if (!decoder_up)
     return decoded_thread.SetAsFailed(decoder_up.takeError());
 
+  if (Error err = SetupMemoryImage(*decoder_up,
+                                   *decoded_thread.GetThread()->GetProcess()))
+    return decoded_thread.SetAsFailed(std::move(err));
+
   LibiptDecoder libipt_decoder(*decoder_up.get(), decoded_thread);
   libipt_decoder.DecodeUntilEndOfTrace();
 }
+
+void lldb_private::trace_intel_pt::DecodeTrace(
+    DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+    const DenseMap<lldb::core_id_t, llvm::ArrayRef<uint8_t>> &buffers,
+    const std::vector<IntelPTThreadContinousExecution> &executions) {
+  DenseMap<lldb::core_id_t, LibiptDecoder> decoders;
+  for (auto &core_id_buffer : buffers) {
+    Expected<PtInsnDecoderUP> decoder_up =
+        CreateInstructionDecoder(trace_intel_pt, core_id_buffer.second);
+    if (!decoder_up)
+      return decoded_thread.SetAsFailed(decoder_up.takeError());
+
+    if (Error err = SetupMemoryImage(*decoder_up,
+                                     *decoded_thread.GetThread()->GetProcess()))
+      return decoded_thread.SetAsFailed(std::move(err));
+
+    decoders.try_emplace(core_id_buffer.first,
+                         LibiptDecoder(*decoder_up->release(), decoded_thread));
+  }
+
+  bool has_seen_psbs = false;
+  for (size_t i = 0; i < executions.size(); i++) {
+    const IntelPTThreadContinousExecution &execution = executions[i];
+
+    auto variant = execution.thread_execution.variant;
+    // If we haven't seen a PSB yet, then it's fine not to show errors
+    if (has_seen_psbs) {
+      if (execution.intelpt_subtraces.empty()) {
+        decoded_thread.AppendError(createStringError(
+            inconvertibleErrorCode(),
+            formatv("Unable to find intel pt data for thread execution with "
+                    "tsc = {0} on core id = {1}",
+                    execution.thread_execution.GetLowestKnownTSC(),
+                    execution.thread_execution.core_id)));
+      }
+
+      // If the first execution is incomplete because it doesn't have a previous
+      // context switch in its cpu, all good.
+      if (variant == ThreadContinuousExecution::Variant::OnlyEnd ||
+          variant == ThreadContinuousExecution::Variant::HintedStart) {
+        decoded_thread.AppendError(createStringError(
+            inconvertibleErrorCode(),
+            formatv("Thread execution starting at tsc = {0} on core id = {1} "
+                    "doesn't have a matching context switch in.",
+                    execution.thread_execution.GetLowestKnownTSC(),
+                    execution.thread_execution.core_id)));
+      }
+    }
+
+    LibiptDecoder &decoder =
+        decoders.find(execution.thread_execution.core_id)->second;
+    for (const IntelPTThreadSubtrace &intel_pt_execution :
+         execution.intelpt_subtraces) {
+      has_seen_psbs = true;
+      decoder.DecodePSB(intel_pt_execution.psb_offset);
+    }
+
+    // If we haven't seen a PSB yet, then it's fine not to show errors
+    if (has_seen_psbs) {
+      // If the last execution is incomplete because it doesn't have a following
+      // context switch in its cpu, all good.
+      if ((variant == ThreadContinuousExecution::Variant::OnlyStart &&
+           i + 1 != executions.size()) ||
+          variant == ThreadContinuousExecution::Variant::HintedEnd) {
+        decoded_thread.AppendError(createStringError(
+            inconvertibleErrorCode(),
+            formatv("Thread execution starting at tsc = {0} on core id = {1} "
+                    "doesn't have a matching context switch out",
+                    execution.thread_execution.GetLowestKnownTSC(),
+                    execution.thread_execution.core_id)));
+      }
+    }
+  }
+}
+
+bool IntelPTThreadContinousExecution::operator<(
+    const IntelPTThreadContinousExecution &o) const {
+  // As the context switch might be incomplete, we look first for the first real
+  // PSB packet, which is a valid TSC. Otherwise, We query the thread execution
+  // itself for some tsc.
+  auto get_tsc = [](const IntelPTThreadContinousExecution &exec) {
+    return exec.intelpt_subtraces.empty()
+               ? exec.thread_execution.GetLowestKnownTSC()
+               : exec.intelpt_subtraces.front().tsc;
+  };
+
+  return get_tsc(*this) < get_tsc(o);
+}
+
+Expected<std::vector<IntelPTThreadSubtrace>>
+lldb_private::trace_intel_pt::SplitTraceInContinuousExecutions(
+    TraceIntelPT &trace_intel_pt, llvm::ArrayRef<uint8_t> buffer) {
+  Expected<PtInsnDecoderUP> decoder_up =
+      CreateInstructionDecoder(trace_intel_pt, buffer);
+  if (!decoder_up)
+    return decoder_up.takeError();
+
+  pt_insn_decoder *decoder = decoder_up.get().get();
+
+  std::vector<IntelPTThreadSubtrace> executions;
+
+  int status = pte_ok;
+  while (!IsLibiptError(status = pt_insn_sync_forward(decoder))) {
+    uint64_t tsc;
+    if (IsLibiptError(pt_insn_time(decoder, &tsc, nullptr, nullptr)))
+      return createStringError(inconvertibleErrorCode(),
+                               "intel pt trace doesn't have TSC timestamps");
+
+    uint64_t psb_offset;
+    pt_insn_get_sync_offset(decoder,
+                            &psb_offset); // this can't fail because we got here
+
+    executions.push_back({
+        tsc,
+        psb_offset,
+    });
+  }
+  return executions;
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
index 21e7bf3085eda..ffef5c6fcf8f6 100644
--- a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
+++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
@@ -9,20 +9,50 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H
 #define LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H
 
-#include "intel-pt.h"
-
 #include "DecodedThread.h"
+#include "PerfContextSwitchDecoder.h"
 #include "forward-declarations.h"
 
+#include "intel-pt.h"
+
 namespace lldb_private {
 namespace trace_intel_pt {
 
+struct IntelPTThreadSubtrace {
+  uint64_t tsc;
+  uint64_t psb_offset;
+};
+
+/// This struct represents a continuous execution of a thread in a core,
+/// delimited by a context switch in and out, and a list of Intel PT subtraces
+/// that belong to this execution.
+struct IntelPTThreadContinousExecution {
+  ThreadContinuousExecution thread_execution;
+  std::vector<IntelPTThreadSubtrace> intelpt_subtraces;
+
+  IntelPTThreadContinousExecution(
+      const ThreadContinuousExecution &thread_execution)
+      : thread_execution(thread_execution) {}
+
+  /// Comparator by time
+  bool operator<(const IntelPTThreadContinousExecution &o) const;
+};
+
 /// Decode a raw Intel PT trace given in \p buffer and append the decoded
 /// instructions and errors in \p decoded_thread. It uses the low level libipt
 /// library underneath.
 void DecodeTrace(DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
                  llvm::ArrayRef<uint8_t> buffer);
 
+void DecodeTrace(
+    DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+    const llvm::DenseMap<lldb::core_id_t, llvm::ArrayRef<uint8_t>> &buffers,
+    const std::vector<IntelPTThreadContinousExecution> &executions);
+
+llvm::Expected<std::vector<IntelPTThreadSubtrace>>
+SplitTraceInContinuousExecutions(TraceIntelPT &trace_intel_pt,
+                                 llvm::ArrayRef<uint8_t> buffer);
+
 } // namespace trace_intel_pt
 } // namespace lldb_private
 

diff  --git a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
new file mode 100644
index 0000000000000..b186f0b26de26
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
@@ -0,0 +1,281 @@
+//===-- PerfContextSwitchDecoder.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 "PerfContextSwitchDecoder.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+/// Copied from <linux/perf_event.h> to avoid depending on perf_event.h on
+/// non-linux platforms.
+/// \{
+struct perf_event_header {
+  uint32_t type;
+  uint16_t misc;
+  uint16_t size;
+};
+
+#define PERF_RECORD_MISC_SWITCH_OUT (1 << 13)
+#define PERF_RECORD_MAX 19
+#define PERF_RECORD_SWITCH_CPU_WIDE 15
+/// \}
+
+/// Record found in the perf_event context switch traces. It might contain
+/// additional fields in memory, but header.size should have the actual size
+/// of the record.
+struct PerfContextSwitchRecord {
+  struct perf_event_header header;
+  uint32_t next_prev_pid;
+  uint32_t next_prev_tid;
+  uint32_t pid, tid;
+  uint64_t time_in_nanos;
+
+  bool IsOut() const { return header.misc & PERF_RECORD_MISC_SWITCH_OUT; }
+
+  bool IsContextSwitchRecord() const {
+    return header.type == PERF_RECORD_SWITCH_CPU_WIDE;
+  }
+
+  /// \return
+  ///   An \a llvm::Error if the record looks obviously wrong, or \a
+  ///   llvm::Error::success() otherwise.
+  Error SanityCheck() const {
+    // A record of too many uint64_t's or more should mean that the data is
+    // wrong
+    if (header.size == 0 || header.size > sizeof(uint64_t) * 1000)
+      return createStringError(
+          inconvertibleErrorCode(),
+          formatv("A record of {0} bytes was found.", header.size));
+
+    // We add some numbers to PERF_RECORD_MAX because some systems might have
+    // custom records. In any case, we are looking only for abnormal data.
+    if (header.type >= PERF_RECORD_MAX + 100)
+      return createStringError(
+          inconvertibleErrorCode(),
+          formatv("Invalid record type {0} was found.", header.type));
+    return Error::success();
+  }
+};
+
+/// Record produced after parsing the raw context switch trace produce by
+/// perf_event. A major 
diff erence between this struct and
+/// PerfContextSwitchRecord is that this one uses tsc instead of nanos.
+struct ContextSwitchRecord {
+  uint64_t tsc;
+  /// Whether the switch is in or out
+  bool is_out;
+  /// pid = 0 and tid = 0 indicate the swapper or idle process, which normally
+  /// runs after a context switch out of a normal user thread.
+  lldb::pid_t pid;
+  lldb::tid_t tid;
+
+  bool IsOut() const { return is_out; }
+
+  bool IsIn() const { return !is_out; }
+};
+
+uint64_t ThreadContinuousExecution::GetLowestKnownTSC() const {
+  switch (variant) {
+  case Variant::Complete:
+    return tscs.complete.start;
+  case Variant::OnlyStart:
+    return tscs.only_start.start;
+  case Variant::OnlyEnd:
+    return tscs.only_end.end;
+  case Variant::HintedEnd:
+    return tscs.hinted_end.start;
+  case Variant::HintedStart:
+    return tscs.hinted_start.end;
+  }
+}
+
+uint64_t ThreadContinuousExecution::GetStartTSC() const {
+  switch (variant) {
+  case Variant::Complete:
+    return tscs.complete.start;
+  case Variant::OnlyStart:
+    return tscs.only_start.start;
+  case Variant::OnlyEnd:
+    return 0;
+  case Variant::HintedEnd:
+    return tscs.hinted_end.start;
+  case Variant::HintedStart:
+    return tscs.hinted_start.hinted_start;
+  }
+}
+
+uint64_t ThreadContinuousExecution::GetEndTSC() const {
+  switch (variant) {
+  case Variant::Complete:
+    return tscs.complete.end;
+  case Variant::OnlyStart:
+    return std::numeric_limits<uint64_t>::max();
+  case Variant::OnlyEnd:
+    return tscs.only_end.end;
+  case Variant::HintedEnd:
+    return tscs.hinted_end.hinted_end;
+  case Variant::HintedStart:
+    return tscs.hinted_start.end;
+  }
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateCompleteExecution(
+    lldb::core_id_t core_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
+    uint64_t end) {
+  ThreadContinuousExecution o(core_id, tid, pid);
+  o.variant = Variant::Complete;
+  o.tscs.complete.start = start;
+  o.tscs.complete.end = end;
+  return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateHintedStartExecution(
+    lldb::core_id_t core_id, lldb::tid_t tid, lldb::pid_t pid,
+    uint64_t hinted_start, uint64_t end) {
+  ThreadContinuousExecution o(core_id, tid, pid);
+  o.variant = Variant::HintedStart;
+  o.tscs.hinted_start.hinted_start = hinted_start;
+  o.tscs.hinted_start.end = end;
+  return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateHintedEndExecution(
+    lldb::core_id_t core_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
+    uint64_t hinted_end) {
+  ThreadContinuousExecution o(core_id, tid, pid);
+  o.variant = Variant::HintedEnd;
+  o.tscs.hinted_end.start = start;
+  o.tscs.hinted_end.hinted_end = hinted_end;
+  return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyEndExecution(
+    lldb::core_id_t core_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t end) {
+  ThreadContinuousExecution o(core_id, tid, pid);
+  o.variant = Variant::OnlyEnd;
+  o.tscs.only_end.end = end;
+  return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyStartExecution(
+    lldb::core_id_t core_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start) {
+  ThreadContinuousExecution o(core_id, tid, pid);
+  o.variant = Variant::OnlyStart;
+  o.tscs.only_start.start = start;
+  return o;
+}
+
+static Error RecoverExecutionsFromConsecutiveRecords(
+    core_id_t core_id, const LinuxPerfZeroTscConversion &tsc_conversion,
+    const ContextSwitchRecord &current_record,
+    const Optional<ContextSwitchRecord> &prev_record,
+    std::function<void(const ThreadContinuousExecution &execution)>
+        on_new_execution) {
+  if (!prev_record) {
+    if (current_record.IsOut()) {
+      on_new_execution(ThreadContinuousExecution::CreateOnlyEndExecution(
+          core_id, current_record.tid, current_record.pid, current_record.tsc));
+    }
+    // The 'in' case will be handled later when we try to look for its end
+    return Error::success();
+  }
+
+  const ContextSwitchRecord &prev = *prev_record;
+  if (prev.tsc >= current_record.tsc)
+    return createStringError(
+        inconvertibleErrorCode(),
+        formatv("A context switch record doesn't happen after the previous "
+                "record. Previous TSC= {0}, current TSC = {1}.",
+                prev.tsc, current_record.tsc));
+
+  if (current_record.IsIn() && prev.IsIn()) {
+    // We found two consecutive ins, which means that we didn't capture
+    // the end of the previous execution.
+    on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
+        core_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
+  } else if (current_record.IsOut() && prev.IsOut()) {
+    // We found two consecutive outs, that means that we didn't capture
+    // the beginning of the current execution.
+    on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
+        core_id, current_record.tid, current_record.pid, prev.tsc + 1,
+        current_record.tsc));
+  } else if (current_record.IsOut() && prev.IsIn()) {
+    if (current_record.pid == prev.pid && current_record.tid == prev.tid) {
+      /// A complete execution
+      on_new_execution(ThreadContinuousExecution::CreateCompleteExecution(
+          core_id, current_record.tid, current_record.pid, prev.tsc,
+          current_record.tsc));
+    } else {
+      // An out after the in of a 
diff erent thread. The first one doesn't
+      // have an end, and the second one doesn't have a start.
+      on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
+          core_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
+      on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
+          core_id, current_record.tid, current_record.pid, prev.tsc + 1,
+          current_record.tsc));
+    }
+  }
+  return Error::success();
+}
+
+#include <fstream>
+
+Expected<std::vector<ThreadContinuousExecution>>
+lldb_private::trace_intel_pt::DecodePerfContextSwitchTrace(
+    ArrayRef<uint8_t> data, core_id_t core_id,
+    const LinuxPerfZeroTscConversion &tsc_conversion) {
+
+  std::vector<ThreadContinuousExecution> executions;
+
+  // This offset is used to create the error message in case of failures.
+  size_t offset = 0;
+
+  auto do_decode = [&]() -> Error {
+    Optional<ContextSwitchRecord> prev_record;
+    while (offset < data.size()) {
+      const PerfContextSwitchRecord &perf_record =
+          *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
+                                                             offset);
+      if (Error err = perf_record.SanityCheck())
+        return err;
+
+      if (perf_record.IsContextSwitchRecord()) {
+        ContextSwitchRecord record{
+            tsc_conversion.ToTSC(perf_record.time_in_nanos),
+            perf_record.IsOut(), static_cast<lldb::pid_t>(perf_record.pid),
+            static_cast<lldb::tid_t>(perf_record.tid)};
+
+        if (Error err = RecoverExecutionsFromConsecutiveRecords(
+                core_id, tsc_conversion, record, prev_record,
+                [&](const ThreadContinuousExecution &execution) {
+                  executions.push_back(execution);
+                }))
+          return err;
+
+        prev_record = record;
+      }
+      offset += perf_record.header.size;
+    }
+
+    // We might have an incomplete last record
+    if (prev_record && prev_record->IsIn())
+      executions.push_back(ThreadContinuousExecution::CreateOnlyStartExecution(
+          core_id, prev_record->tid, prev_record->pid, prev_record->tsc));
+    return Error::success();
+  };
+
+  if (Error err = do_decode())
+    return createStringError(inconvertibleErrorCode(),
+                             formatv("Malformed perf context switch trace for "
+                                     "cpu {0} at offset {1}. {2}",
+                                     core_id, offset,
+                                     toString(std::move(err))));
+
+  return executions;
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
new file mode 100644
index 0000000000000..56ddf53d33a33
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
@@ -0,0 +1,146 @@
+//===-- PerfContextSwitchDecoder.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_PERFCONTEXTSWITCHDECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_PERFCONTEXTSWITCHDECODER_H
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "lldb/lldb-types.h"
+
+#include "llvm/Support/Error.h"
+
+#include <vector>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// This class indicates the time interval in which a thread was running
+/// continuously on a cpu core.
+///
+/// Note: we use the terms CPU and cores interchangeably.
+struct ThreadContinuousExecution {
+
+  /// In most cases both the start and end of a continuous execution can be
+  /// accurately recovered from the context switch trace, but in some cases one
+  /// of these endpoints might be guessed or not known at all, due to contention
+  /// problems in the trace or because tracing was interrupted, e.g. with ioctl
+  /// calls, which causes gaps in the trace. Because of that, we identify which
+  /// situation we fall into with the following variants.
+  enum class Variant {
+    /// Both endpoints are known.
+    Complete,
+    /// The end is known and we have a lower bound for the start, i.e. the
+    /// previous execution in the same core happens strictly before the hinted
+    /// start.
+    HintedStart,
+    /// The start is known and we have an upper bound for the end, i.e. the next
+    /// execution  in the same core happens strictly after the hinted end.
+    HintedEnd,
+    /// We only know the start. This might be the last entry of a core trace.
+    OnlyStart,
+    /// We only know the end. This might be the first entry or a core trace.
+    OnlyEnd,
+  } variant;
+
+  /// \return
+  ///   The lowest tsc that we are sure of, i.e. not hinted.
+  uint64_t GetLowestKnownTSC() const;
+
+  /// \return
+  ///   The known or hinted start tsc, or 0 if the variant is \a OnlyEnd.
+  uint64_t GetStartTSC() const;
+
+  /// \return
+  ///   The known or hinted end tsc, or max \a uint64_t if the variant is \a
+  ///   OnlyStart.
+  uint64_t GetEndTSC() const;
+
+  /// Constructors for the 
diff erent variants of this object
+  ///
+  /// \{
+  static ThreadContinuousExecution
+  CreateCompleteExecution(lldb::core_id_t core_id, lldb::tid_t tid,
+                          lldb::pid_t pid, uint64_t start, uint64_t end);
+
+  static ThreadContinuousExecution
+  CreateHintedStartExecution(lldb::core_id_t core_id, lldb::tid_t tid,
+                             lldb::pid_t pid, uint64_t hinted_start,
+                             uint64_t end);
+
+  static ThreadContinuousExecution
+  CreateHintedEndExecution(lldb::core_id_t core_id, lldb::tid_t tid,
+                           lldb::pid_t pid, uint64_t start,
+                           uint64_t hinted_end);
+
+  static ThreadContinuousExecution
+  CreateOnlyEndExecution(lldb::core_id_t core_id, lldb::tid_t tid,
+                         lldb::pid_t pid, uint64_t end);
+
+  static ThreadContinuousExecution
+  CreateOnlyStartExecution(lldb::core_id_t core_id, lldb::tid_t tid,
+                           lldb::pid_t pid, uint64_t start);
+  /// \}
+
+  union {
+    struct {
+      uint64_t start;
+      uint64_t end;
+    } complete;
+    struct {
+      uint64_t start;
+    } only_start;
+    struct {
+      uint64_t end;
+    } only_end;
+    /// The following 'hinted' structures are useful when there are contention
+    /// problems in the trace
+    struct {
+      uint64_t hinted_start;
+      uint64_t end;
+    } hinted_start;
+    struct {
+      uint64_t start;
+      uint64_t hinted_end;
+    } hinted_end;
+  } tscs;
+
+  lldb::core_id_t core_id;
+  lldb::tid_t tid;
+  lldb::pid_t pid;
+
+private:
+  /// We keep this constructor private to force the usage of the static named
+  /// constructors.
+  ThreadContinuousExecution(lldb::core_id_t core_id, lldb::tid_t tid,
+                            lldb::pid_t pid)
+      : core_id(core_id), tid(tid), pid(pid) {}
+};
+
+/// Decodes a context switch trace gotten with perf_event_open.
+///
+/// \param[in] data
+///   The context switch trace in binary format.
+///
+/// \param[i] core_id
+///   The core_id where the trace were gotten from.
+///
+/// \param[in] tsc_conversion
+///   The conversion values used to confert nanoseconds to TSC.
+///
+/// \return
+///   A list of continuous executions recovered from the raw trace sorted by
+///   time, or an \a llvm::Error if the data is malformed.
+llvm::Expected<std::vector<ThreadContinuousExecution>>
+DecodePerfContextSwitchTrace(llvm::ArrayRef<uint8_t> data,
+                             lldb::core_id_t core_id,
+                             const LinuxPerfZeroTscConversion &tsc_conversion);
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_PERFCONTEXTSWITCHDECODER_H

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
index 33d31fd7b7cff..45b50e15f4391 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -49,8 +49,10 @@ bool fromJSON(const json::Value &value, JSONModule &module, Path path) {
 }
 
 json::Value toJSON(const JSONThread &thread) {
-  return json::Object{{"tid", thread.tid},
-                      {"traceBuffer", thread.trace_buffer}};
+  json::Object obj{{"tid", thread.tid}};
+  if (thread.trace_buffer)
+    obj["traceBuffer"] = *thread.trace_buffer;
+  return obj;
 }
 
 bool fromJSON(const json::Value &value, JSONThread &thread, Path path) {

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp
index fa8fd55a11f42..96b41e3f8e2d7 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp
@@ -12,225 +12,11 @@
 
 #include "llvm/Support/Error.h"
 
-#include <linux/perf_event.h>
-
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
 
-struct PerfContextSwitchRecord {
-  struct perf_event_header header;
-  uint32_t next_prev_pid;
-  uint32_t next_prev_tid;
-  uint32_t pid, tid;
-  uint64_t time_in_nanos;
-
-  bool IsOut() const { return header.misc & PERF_RECORD_MISC_SWITCH_OUT; }
-};
-
-struct ContextSwitchRecord {
-  uint64_t tsc;
-  bool is_out;
-  /// A pid of 0 indicates an execution in the kernel
-  lldb::pid_t pid;
-  lldb::tid_t tid;
-
-  bool IsOut() const { return is_out; }
-
-  bool IsIn() const { return !is_out; }
-};
-
-uint64_t ThreadContinuousExecution::GetErrorFreeTSC() const {
-  switch (variant) {
-  case Variant::Complete:
-    return tscs.complete.start; // end would also work
-  case Variant::HintedStart:
-    return tscs.hinted_start.end;
-  case Variant::HintedEnd:
-    return tscs.hinted_end.start;
-  case Variant::OnlyEnd:
-    return tscs.only_end.end;
-  case Variant::OnlyStart:
-    return tscs.only_start.start;
-  }
-}
-
-ThreadContinuousExecution ThreadContinuousExecution::CreateCompleteExecution(
-    lldb::core_id_t core_id, lldb::tid_t tid, uint64_t start, uint64_t end) {
-  ThreadContinuousExecution o(core_id, tid);
-  o.variant = Variant::Complete;
-  o.tscs.complete.start = start;
-  o.tscs.complete.end = end;
-  return o;
-}
-
-ThreadContinuousExecution ThreadContinuousExecution::CreateHintedStartExecution(
-    lldb::core_id_t core_id, lldb::tid_t tid, uint64_t hinted_start,
-    uint64_t end) {
-  ThreadContinuousExecution o(core_id, tid);
-  o.variant = Variant::HintedStart;
-  o.tscs.hinted_start.hinted_start = hinted_start;
-  o.tscs.hinted_start.end = end;
-  return o;
-}
-
-ThreadContinuousExecution ThreadContinuousExecution::CreateHintedEndExecution(
-    lldb::core_id_t core_id, lldb::tid_t tid, uint64_t start,
-    uint64_t hinted_end) {
-  ThreadContinuousExecution o(core_id, tid);
-  o.variant = Variant::HintedEnd;
-  o.tscs.hinted_end.start = start;
-  o.tscs.hinted_end.hinted_end = hinted_end;
-  return o;
-}
-
-ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyEndExecution(
-    lldb::core_id_t core_id, lldb::tid_t tid, uint64_t end) {
-  ThreadContinuousExecution o(core_id, tid);
-  o.variant = Variant::OnlyEnd;
-  o.tscs.only_end.end = end;
-  return o;
-}
-
-ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyStartExecution(
-    lldb::core_id_t core_id, lldb::tid_t tid, uint64_t start) {
-  ThreadContinuousExecution o(core_id, tid);
-  o.variant = Variant::OnlyStart;
-  o.tscs.only_start.start = start;
-  return o;
-}
-
-bool ThreadContinuousExecution::operator<(
-    const ThreadContinuousExecution &o) const {
-  // We can compare by GetErrorFreeTSC because context switches across CPUs can
-  // be sorted by any of its TSC.
-  return GetErrorFreeTSC() < o.GetErrorFreeTSC();
-}
-
-/// Tries to recover a continuous execution by analyzing two consecutive context
-/// switch records.
-static Error
-HandleContextSwitch(core_id_t core_id,
-                    const LinuxPerfZeroTscConversion &tsc_conversion,
-                    const ContextSwitchRecord &record,
-                    const Optional<ContextSwitchRecord> &prev_record,
-                    std::function<void(ThreadContinuousExecution &&execution)>
-                        on_new_thread_execution) {
-  if (!prev_record) {
-    if (record.IsOut())
-      on_new_thread_execution(ThreadContinuousExecution::CreateOnlyEndExecution(
-          core_id, record.tid, record.tsc));
-    // The 'in' case will be handled later when we try to look for its end
-    return Error::success();
-  }
-
-  const ContextSwitchRecord &prev = *prev_record;
-  if (prev.tsc > record.tsc)
-    return createStringError(
-        inconvertibleErrorCode(),
-        formatv("A context switch record out doesn't happen after the previous "
-                "record. Previous TSC= {0}, current TSC = {1}.",
-                prev.tsc, record.tsc));
-
-  if (record.IsIn() && prev.IsIn()) {
-    // We found two consecutive ins, which means that we didn't capture
-    // the end of the previous execution.
-    on_new_thread_execution(ThreadContinuousExecution::CreateHintedEndExecution(
-        core_id, prev.tid, prev.tsc, record.tsc - 1));
-  } else if (record.IsOut() && prev.IsOut()) {
-    // We found two consecutive outs, that means that we didn't capture
-    // the beginning of the current execution.
-    on_new_thread_execution(
-        ThreadContinuousExecution::CreateHintedStartExecution(
-            core_id, record.tid, prev.tsc + 1, record.tsc));
-  } else if (record.IsOut() && prev.IsIn()) {
-    if (record.pid == prev.pid && record.tid == prev.tid) {
-      /// A complete execution
-      on_new_thread_execution(
-          ThreadContinuousExecution::CreateCompleteExecution(
-              core_id, record.tid, prev.tsc, record.tsc));
-    } else {
-      // An out after the in of a 
diff erent thread. The first one doesn't
-      // have an end, and the second one doesn't have a start.
-      on_new_thread_execution(
-          ThreadContinuousExecution::CreateHintedEndExecution(
-              core_id, prev.tid, prev.tsc, record.tsc - 1));
-      on_new_thread_execution(
-          ThreadContinuousExecution::CreateHintedStartExecution(
-              core_id, record.tid, prev.tsc + 1, record.tsc));
-    }
-  }
-  return Error::success();
-}
-
-/// Decodes a context switch trace gotten with perf_event_open.
-///
-/// \param[in] data
-///   The context switch trace in binary format.
-///
-/// \param[i] core_id
-///   The core_id where the trace were gotten from.
-///
-/// \param[in] tsc_conversion
-///   The conversion values used to confert nanoseconds to TSC.
-///
-/// \param[in] on_new_thread_execution
-///   Callback to be invoked whenever a continuous execution is recovered from
-///   the trace.
-static Error DecodePerfContextSwitchTrace(
-    ArrayRef<uint8_t> data, core_id_t core_id,
-    const LinuxPerfZeroTscConversion &tsc_conversion,
-    std::function<void(ThreadContinuousExecution &&execution)>
-        on_new_thread_execution) {
-  auto CreateError = [&](size_t offset, auto error) -> Error {
-    return createStringError(inconvertibleErrorCode(),
-                             formatv("Malformed perf context switch trace for "
-                                     "cpu {0} at offset {1}. {2}",
-                                     core_id, offset, error));
-  };
-
-  Optional<ContextSwitchRecord> prev_record;
-  for (size_t offset = 0; offset < data.size();) {
-    const PerfContextSwitchRecord &perf_record =
-        *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
-                                                           offset);
-    // A record of 1000 uint64_t's or more should mean that the data is wrong
-    if (perf_record.header.size == 0 ||
-        perf_record.header.size > sizeof(uint64_t) * 1000)
-      return CreateError(offset, formatv("A record of {0} bytes was found.",
-                                         perf_record.header.size));
-
-    // We add + 100 to this record because some systems might have custom
-    // records. In any case, we are looking only for abnormal data.
-    if (perf_record.header.type >= PERF_RECORD_MAX + 100)
-      return CreateError(offset, formatv("Invalid record type {0} was found.",
-                                         perf_record.header.type));
-
-    if (perf_record.header.type == PERF_RECORD_SWITCH_CPU_WIDE) {
-      ContextSwitchRecord record{tsc_conversion.ToTSC(std::chrono::nanoseconds(
-                                     perf_record.time_in_nanos)),
-                                 perf_record.IsOut(),
-                                 static_cast<lldb::pid_t>(perf_record.pid),
-                                 static_cast<lldb::tid_t>(perf_record.tid)};
-
-      if (Error err = HandleContextSwitch(core_id, tsc_conversion, record,
-                                          prev_record, on_new_thread_execution))
-        return CreateError(offset, toString(std::move(err)));
-
-      prev_record = record;
-    }
-    offset += perf_record.header.size;
-  }
-
-  // We might have an incomplete last record
-  if (prev_record && prev_record->IsIn())
-    on_new_thread_execution(ThreadContinuousExecution::CreateOnlyStartExecution(
-        core_id, prev_record->tid, prev_record->tsc));
-
-  return Error::success();
-}
-
 TraceIntelPTMultiCoreDecoder::TraceIntelPTMultiCoreDecoder(
     TraceIntelPT &trace, ArrayRef<core_id_t> core_ids, ArrayRef<tid_t> tids,
     const LinuxPerfZeroTscConversion &tsc_conversion)
@@ -245,10 +31,91 @@ DecodedThreadSP TraceIntelPTMultiCoreDecoder::Decode(Thread &thread) {
   if (Error err = DecodeContextSwitchTraces())
     return std::make_shared<DecodedThread>(thread.shared_from_this(),
                                            std::move(err));
+  auto it = m_decoded_threads.find(thread.GetID());
+  if (it != m_decoded_threads.end())
+    return it->second;
+
+  DecodedThreadSP decoded_thread_sp =
+      std::make_shared<DecodedThread>(thread.shared_from_this());
+
+  Error err = m_trace.OnCoresBinaryDataRead(
+      m_cores, IntelPTDataKinds::kTraceBuffer,
+      [&](const DenseMap<core_id_t, ArrayRef<uint8_t>> buffers) -> Error {
+        auto it = m_continuous_executions_per_thread->find(thread.GetID());
+        if (it != m_continuous_executions_per_thread->end())
+          DecodeTrace(*decoded_thread_sp, m_trace, buffers, it->second);
+
+        return Error::success();
+      });
+  if (err)
+    decoded_thread_sp->SetAsFailed(std::move(err));
+
+  m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp);
+  return decoded_thread_sp;
+}
 
-  return std::make_shared<DecodedThread>(
-      thread.shared_from_this(),
-      createStringError(inconvertibleErrorCode(), "unimplemented"));
+llvm::Expected<
+    llvm::DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
+TraceIntelPTMultiCoreDecoder::CorrelateContextSwitchesAndIntelPtTraces() {
+  llvm::DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>
+      continuous_executions_per_thread;
+
+  for (core_id_t core_id : m_cores) {
+    std::vector<IntelPTThreadSubtrace> intel_pt_executions;
+
+    Error err = m_trace.OnCoreBinaryDataRead(
+        core_id, IntelPTDataKinds::kTraceBuffer,
+        [&](ArrayRef<uint8_t> data) -> Error {
+          Expected<std::vector<IntelPTThreadSubtrace>> split_trace =
+              SplitTraceInContinuousExecutions(m_trace, data);
+          if (!split_trace)
+            return split_trace.takeError();
+
+          intel_pt_executions = std::move(*split_trace);
+          return Error::success();
+        });
+    if (err)
+      return std::move(err);
+
+    // We'll be iterating through the thread continuous executions and the intel
+    // pt subtraces sorted by time.
+    auto it = intel_pt_executions.begin();
+    auto on_new_thread_execution =
+        [&](ThreadContinuousExecution thread_execution) {
+          IntelPTThreadContinousExecution execution(thread_execution);
+
+          for (; it != intel_pt_executions.end() &&
+                 it->tsc < thread_execution.GetEndTSC();
+               it++) {
+            if (it->tsc > thread_execution.GetStartTSC()) {
+              execution.intelpt_subtraces.push_back(*it);
+            } else {
+              m_unattributed_intelpt_subtraces++;
+            }
+          }
+          continuous_executions_per_thread[thread_execution.tid].push_back(
+              execution);
+        };
+    err = m_trace.OnCoreBinaryDataRead(
+        core_id, IntelPTDataKinds::kPerfContextSwitchTrace,
+        [&](ArrayRef<uint8_t> data) -> Error {
+          Expected<std::vector<ThreadContinuousExecution>> executions =
+              DecodePerfContextSwitchTrace(data, core_id, m_tsc_conversion);
+          if (!executions)
+            return executions.takeError();
+          for (const ThreadContinuousExecution &exec : *executions)
+            on_new_thread_execution(exec);
+          return Error::success();
+        });
+    if (err)
+      return std::move(err);
+  }
+  // We now sort the executions of each thread to have them ready for
+  // instruction decoding
+  for (auto &tid_executions : continuous_executions_per_thread)
+    std::sort(tid_executions.second.begin(), tid_executions.second.end());
+
+  return continuous_executions_per_thread;
 }
 
 Error TraceIntelPTMultiCoreDecoder::DecodeContextSwitchTraces() {
@@ -258,38 +125,20 @@ Error TraceIntelPTMultiCoreDecoder::DecodeContextSwitchTraces() {
   if (m_continuous_executions_per_thread)
     return Error::success();
 
-  m_continuous_executions_per_thread.emplace();
-
-  auto do_decode = [&]() -> Error {
-    // We'll decode all context switch traces, identify continuous executions
-    // and group them by thread.
-    for (core_id_t core_id : m_cores) {
-      Error err = m_trace.OnCoreBinaryDataRead(
-          core_id, IntelPTDataKinds::kPerfContextSwitchTrace,
-          [&](ArrayRef<uint8_t> data) -> Error {
-            return DecodePerfContextSwitchTrace(
-                data, core_id, m_tsc_conversion,
-                [&](const ThreadContinuousExecution &execution) {
-                  (*m_continuous_executions_per_thread)[execution.tid]
-                      .push_back(execution);
-                });
-          });
-      if (err) {
-        m_setup_error = toString(std::move(err));
-        return createStringError(inconvertibleErrorCode(),
-                                 m_setup_error->c_str());
-      }
-    }
-    // We now sort the executions of each to have them ready for instruction
-    // decoding
-    for (auto &tid_executions : *m_continuous_executions_per_thread)
-      std::sort(tid_executions.second.begin(), tid_executions.second.end());
-
-    return Error::success();
-  };
-
-  return m_trace.GetTimer().ForGlobal().TimeTask<Error>(
-      "Context switch trace decoding", do_decode);
+  Error err = m_trace.GetTimer().ForGlobal().TimeTask<Error>(
+      "Context switch and Intel PT traces correlation", [&]() -> Error {
+        if (auto correlation = CorrelateContextSwitchesAndIntelPtTraces()) {
+          m_continuous_executions_per_thread.emplace(std::move(*correlation));
+          return Error::success();
+        } else {
+          return correlation.takeError();
+        }
+      });
+  if (err) {
+    m_setup_error = toString(std::move(err));
+    return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
+  }
+  return Error::success();
 }
 
 size_t TraceIntelPTMultiCoreDecoder::GetNumContinuousExecutionsForThread(

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h
index a6ec5207a8ebc..2e01aa638a716 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h
@@ -9,108 +9,27 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTMULTICOREDECODER_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTMULTICOREDECODER_H
 
+#include "LibiptDecoder.h"
+#include "PerfContextSwitchDecoder.h"
 #include "ThreadDecoder.h"
 
 namespace lldb_private {
 namespace trace_intel_pt {
 
-/// This class indicates the time interval in which a thread was running
-/// continuously on a cpu core.
-///
-/// In most cases both endpoints of the intervals can be accurately recovered
-/// from a context switch trace, but in some cases one of these endpoints might
-/// be guessed or not known at all, due to contention problems in the trace or
-/// because tracing was interrupted.
-///
-/// Note: we use the terms CPU and cores interchangeably.
-struct ThreadContinuousExecution {
-  enum class Variant {
-    /// Both endpoints are known
-    Complete,
-    /// The end is known and we have a guess for the start
-    HintedStart,
-    /// The start is known and we have a guess for the end
-    HintedEnd,
-    /// We only know the start. This might be the last entry of a core trace.
-    OnlyStart,
-    /// We only know the end. This might be the first entry or a core trace.
-    OnlyEnd,
-  } variant;
-
-  union {
-    struct {
-      uint64_t start;
-      uint64_t end;
-    } complete;
-    struct {
-      uint64_t start;
-    } only_start;
-    struct {
-      uint64_t end;
-    } only_end;
-    /// The following 'hinted' structures are useful when there are contention
-    /// problems in the trace
-    struct {
-      uint64_t hinted_start;
-      uint64_t end;
-    } hinted_start;
-    struct {
-      uint64_t start;
-      uint64_t hinted_end;
-    } hinted_end;
-  } tscs;
-
-  lldb::core_id_t core_id;
-  lldb::tid_t tid;
-
-  /// \return
-  ///   A tsc that we are certain of, either the start or the end.
-  uint64_t GetErrorFreeTSC() const;
-
-  /// Constructors for the 
diff erent variants of this object
-  ///
-  /// \{
-  static ThreadContinuousExecution
-  CreateCompleteExecution(lldb::core_id_t core_id, lldb::tid_t tid,
-                          uint64_t start, uint64_t end);
-
-  static ThreadContinuousExecution
-  CreateHintedStartExecution(lldb::core_id_t core_id, lldb::tid_t tid,
-                             uint64_t hinted_start, uint64_t end);
-
-  static ThreadContinuousExecution
-  CreateHintedEndExecution(lldb::core_id_t core_id, lldb::tid_t tid,
-                           uint64_t start, uint64_t hinted_end);
-
-  static ThreadContinuousExecution
-  CreateOnlyEndExecution(lldb::core_id_t core_id, lldb::tid_t tid,
-                         uint64_t end);
-
-  static ThreadContinuousExecution
-  CreateOnlyStartExecution(lldb::core_id_t core_id, lldb::tid_t tid,
-                           uint64_t start);
-  /// \}
-
-  /// Comparator by TSCs
-  bool operator<(const ThreadContinuousExecution &o) const;
-
-private:
-  ThreadContinuousExecution(lldb::core_id_t core_id, lldb::tid_t tid)
-      : core_id(core_id), tid(tid) {}
-};
-
 /// Class used to decode a multi-core Intel PT trace. It assumes that each
 /// thread could have potentially been executed on 
diff erent cores. It uses a
 /// context switch trace per CPU with timestamps to identify which thread owns
 /// each Intel PT decoded instruction and in which order. It also assumes that
 /// the Intel PT data and context switches might have gaps in their traces due
-/// to contention or race conditions.
+/// to contention or race conditions. Finally, it assumes that a tid is not
+/// repeated twice for two 
diff erent threads because of the shortness of the
+/// intel pt trace.
 class TraceIntelPTMultiCoreDecoder {
 public:
   /// \param[in] core_ids
   ///   The list of cores where the traced programs were running on.
   ///
-  /// \param[in] tid
+  /// \param[in] tids
   ///   The full list of tids that were traced.
   ///
   /// \param[in] tsc_conversion
@@ -143,16 +62,24 @@ class TraceIntelPTMultiCoreDecoder {
   /// by thread.
   llvm::Error DecodeContextSwitchTraces();
 
+  /// Produce a mapping from thread ids to the list of continuos executions with
+  /// their associated intel pt subtraces.
+  llvm::Expected<
+      llvm::DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
+  CorrelateContextSwitchesAndIntelPtTraces();
+
   TraceIntelPT &m_trace;
   std::set<lldb::core_id_t> m_cores;
   std::set<lldb::tid_t> m_tids;
   llvm::Optional<
-      llvm::DenseMap<lldb::tid_t, std::vector<ThreadContinuousExecution>>>
+      llvm::DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
       m_continuous_executions_per_thread;
+  llvm::DenseMap<lldb::tid_t, DecodedThreadSP> m_decoded_threads;
   LinuxPerfZeroTscConversion m_tsc_conversion;
   /// This variable will be non-None if a severe error happened during the setup
   /// of the decoder.
   llvm::Optional<std::string> m_setup_error;
+  uint64_t m_unattributed_intelpt_subtraces;
 };
 
 } // namespace trace_intel_pt

diff  --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index 7f361abb4b121..3112060a6301c 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -128,8 +128,8 @@ Optional<uint64_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
 
 Optional<uint64_t> Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id,
                                                     llvm::StringRef kind) {
-  auto it = m_live_core_data.find(core_id);
-  if (it == m_live_core_data.end())
+  auto it = m_live_core_data_sizes.find(core_id);
+  if (it == m_live_core_data_sizes.end())
     return None;
   std::unordered_map<std::string, uint64_t> &single_core_data = it->second;
   auto single_thread_data_it = single_core_data.find(kind.str());
@@ -211,6 +211,8 @@ const char *Trace::RefreshLiveProcessState() {
   m_stop_id = new_stop_id;
   m_live_thread_data.clear();
   m_live_refresh_error.reset();
+  m_live_core_data_sizes.clear();
+  m_live_core_data.clear();
   m_cores.reset();
 
   auto HandleError = [&](Error &&err) -> const char * {
@@ -246,7 +248,7 @@ const char *Trace::RefreshLiveProcessState() {
     for (const TraceCoreState &core_state : *live_process_state->cores) {
       m_cores->push_back(core_state.core_id);
       for (const TraceBinaryData &item : core_state.binary_data)
-        m_live_core_data[core_state.core_id][item.kind] = item.size;
+        m_live_core_data_sizes[core_state.core_id][item.kind] = item.size;
     }
     LLDB_LOG(log, "== Found {0} cpu cores being traced",
             live_process_state->cores->size());
@@ -353,10 +355,18 @@ Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
 llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id,
                                             llvm::StringRef kind,
                                             OnBinaryDataReadCallback callback) {
+  auto core_data_entries = m_live_core_data.find(core_id);
+  if (core_data_entries != m_live_core_data.end()) {
+    auto core_data = core_data_entries->second.find(kind.str());
+    if (core_data != core_data_entries->second.end())
+      return callback(core_data->second);
+  }
+
   Expected<std::vector<uint8_t>> data = GetLiveCoreBinaryData(core_id, kind);
   if (!data)
     return data.takeError();
-  return callback(*data);
+  auto it = m_live_core_data[core_id].insert({kind.str(), std::move(*data)});
+  return callback(it.first->second);
 }
 
 llvm::Error Trace::OnDataFileRead(FileSpec file,
@@ -401,6 +411,28 @@ llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
     return OnPostMortemThreadBinaryDataRead(tid, kind, callback);
 }
 
+llvm::Error
+Trace::OnCoresBinaryDataRead(const std::set<lldb::core_id_t> core_ids,
+                             llvm::StringRef kind,
+                             OnCoresBinaryDataReadCallback callback) {
+  DenseMap<core_id_t, ArrayRef<uint8_t>> buffers;
+
+  std::function<Error(std::set<core_id_t>::iterator)> process_core =
+      [&](std::set<core_id_t>::iterator core_id) -> Error {
+    if (core_id == core_ids.end())
+      return callback(buffers);
+
+    return OnCoreBinaryDataRead(*core_id, kind,
+                                [&](ArrayRef<uint8_t> data) -> Error {
+                                  buffers.try_emplace(*core_id, data);
+                                  auto next_id = core_id;
+                                  next_id++;
+                                  return process_core(next_id);
+                                });
+  };
+  return process_core(core_ids.begin());
+}
+
 llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id,
                                         llvm::StringRef kind,
                                         OnBinaryDataReadCallback callback) {

diff  --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
index 019a2b960ef56..985480a08a42e 100644
--- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -50,18 +50,15 @@ json::Value toJSON(const TraceIntelPTStartRequest &packet) {
   return base;
 }
 
-std::chrono::nanoseconds
-LinuxPerfZeroTscConversion::ToNanos(uint64_t tsc) const {
+uint64_t LinuxPerfZeroTscConversion::ToNanos(uint64_t tsc) const {
   uint64_t quot = tsc >> time_shift;
   uint64_t rem_flag = (((uint64_t)1 << time_shift) - 1);
   uint64_t rem = tsc & rem_flag;
-  return std::chrono::nanoseconds{time_zero + quot * time_mult +
-                                  ((rem * time_mult) >> time_shift)};
+  return time_zero + quot * time_mult + ((rem * time_mult) >> time_shift);
 }
 
-uint64_t
-LinuxPerfZeroTscConversion::ToTSC(std::chrono::nanoseconds nanos) const {
-  uint64_t time = nanos.count() - time_zero;
+uint64_t LinuxPerfZeroTscConversion::ToTSC(uint64_t nanos) const {
+  uint64_t time = nanos - time_zero;
   uint64_t quot = time / time_mult;
   uint64_t rem = time % time_mult;
   return (quot << time_shift) + (rem << time_shift) / time_mult;

diff  --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index d44660177e012..6f6994bfaa8b1 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -9,6 +9,18 @@ class TestTraceLoad(TraceIntelPTTestCaseBase):
     mydir = TestBase.compute_mydir(__file__)
     NO_DEBUG_INFO_TESTCASE = True
 
+    def testLoadMultiCoreTrace(self):
+        src_dir = self.getSourceDir()
+        trace_definition_file = os.path.join(src_dir, "intelpt-multi-core-trace", "trace.json")
+        self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"])
+        self.expect("thread trace dump instructions 2 -t",
+          substrs=["19521: [tsc=0x008fb5211c143fd8] error: expected tracing enabled event",
+                   "m.out`foo() + 65 at multi_thread.cpp:12:21",
+                   "19520: [tsc=0x008fb5211bfbc69e] 0x0000000000400ba7    jg     0x400bb3"])
+        self.expect("thread trace dump instructions 3 -t",
+          substrs=["67910: [tsc=0x008fb5211bfdf270] 0x0000000000400bd7    addl   $0x1, -0x4(%rbp)",
+                   "m.out`bar() + 26 at multi_thread.cpp:20:6"])
+
     def testLoadTrace(self):
         src_dir = self.getSourceDir()
         trace_definition_file = os.path.join(src_dir, "intelpt-trace", "trace.json")

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.intelpt_trace b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.intelpt_trace
new file mode 100644
index 0000000000000..c0eea630e2a70
Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.intelpt_trace 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.perf_context_switch_trace b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.perf_context_switch_trace
new file mode 100644
index 0000000000000..1edc908a35a52
Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/45.perf_context_switch_trace 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.intelpt_trace b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.intelpt_trace
new file mode 100644
index 0000000000000..00318e50e2200
Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.intelpt_trace 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.perf_context_switch_trace b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.perf_context_switch_trace
new file mode 100644
index 0000000000000..354ff2cf73864
Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-multi-core-trace/cores/51.perf_context_switch_trace 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/modules/m.out b/lldb/test/API/commands/trace/intelpt-multi-core-trace/modules/m.out
new file mode 100644
index 0000000000000..46ae51c3f1dc6
Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-multi-core-trace/modules/m.out 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/multi_thread.cpp b/lldb/test/API/commands/trace/intelpt-multi-core-trace/multi_thread.cpp
new file mode 100644
index 0000000000000..cdbfbc2343b91
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-multi-core-trace/multi_thread.cpp
@@ -0,0 +1,34 @@
+#include <thread>
+#include <cstring>
+#include <unistd.h>
+using namespace std;
+
+bool done = false;
+void foo() {
+  int x = 0;
+  for (int i = 0; i < 10000; i++)
+    x++;
+  sleep(1);
+  for (int i = 0; i < 10000; i++)
+    x++;
+  done = true;
+}
+
+void bar() {
+  int y = 0;
+  while (!done) {
+    y++;
+  }
+  printf("bar %d\n", y);
+}
+
+int main() {
+  std::thread first(foo);
+  std::thread second(bar);
+  first.join();
+  second.join();
+
+  printf("complete\n");
+  return 0;
+
+}

diff  --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json
new file mode 100644
index 0000000000000..54397ee79b3cc
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json
@@ -0,0 +1,51 @@
+{
+  "cores": [
+    {
+      "contextSwitchTrace": "/tmp/trace8/cores/45.perf_context_switch_trace",
+      "coreId": 45,
+      "traceBuffer": "/tmp/trace8/cores/45.intelpt_trace"
+    },
+    {
+      "contextSwitchTrace": "/tmp/trace8/cores/51.perf_context_switch_trace",
+      "coreId": 51,
+      "traceBuffer": "/tmp/trace8/cores/51.intelpt_trace"
+    }
+  ],
+  "cpuInfo": {
+    "family": 6,
+    "model": 85,
+    "stepping": 4,
+    "vendor": "GenuineIntel"
+  },
+  "processes": [
+    {
+      "modules": [
+        {
+          "file": "modules/m.out",
+          "systemPath": "/tmp/m.out",
+          "loadAddress": 4194304,
+          "uuid": "AEFB0D59-233F-80FF-6D3C-4DED498534CF-11017B3B"
+        }
+      ],
+      "pid": 3497234,
+      "threads": [
+        {
+          "tid": 3497234
+        },
+        {
+          "tid": 3497496
+        },
+        {
+          "tid": 3497497
+        }
+      ],
+      "triple": "x86_64-unknown-linux-gnu"
+    }
+  ],
+  "tscPerfZeroConversion": {
+    "timeMult": 1076264588,
+    "timeShift": 31,
+    "timeZero": 18433473881008870804
+  },
+  "type": "intel-pt"
+}

diff  --git a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
index 3698c0fef1ac6..f33822ac4cadf 100644
--- a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
+++ b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
@@ -233,6 +233,6 @@ def testStartPerCoreSession(self):
         # We must have captured the context switch of when the target resumed
         self.assertTrue(found_non_empty_context_switch)
 
-        self.expect("thread trace dump instructions", substrs=['unimplemented'])
+        self.expect("thread trace dump instructions")
 
         self.traceStopProcess()

diff  --git a/lldb/unittests/Process/Linux/PerfTests.cpp b/lldb/unittests/Process/Linux/PerfTests.cpp
index 6332cd97676c9..10f0b2c0f6281 100644
--- a/lldb/unittests/Process/Linux/PerfTests.cpp
+++ b/lldb/unittests/Process/Linux/PerfTests.cpp
@@ -76,14 +76,14 @@ TEST(Perf, TscConversion) {
   if (!tsc_after_sleep)
     GTEST_SKIP() << toString(tsc_after_sleep.takeError());
 
-  std::chrono::nanoseconds converted_tsc_
diff  =
+  uint64_t converted_tsc_
diff  =
       params->ToNanos(*tsc_after_sleep) - params->ToNanos(*tsc_before_sleep);
 
   std::chrono::microseconds acceptable_overhead(500);
 
-  ASSERT_GE(converted_tsc_
diff .count(), SLEEP_NANOS.count());
-  ASSERT_LT(converted_tsc_
diff .count(),
-            (SLEEP_NANOS + acceptable_overhead).count());
+  ASSERT_GE(converted_tsc_
diff , static_cast<uint64_t>(SLEEP_NANOS.count()));
+  ASSERT_LT(converted_tsc_
diff ,
+            static_cast<uint64_t>((SLEEP_NANOS + acceptable_overhead).count()));
 }
 
 #endif // __x86_64__


        


More information about the lldb-commits mailing list