[Lldb-commits] [lldb] 6423b50 - [trace][intel pt] Create a class for the libipt decoder wrapper

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Thu Apr 7 16:06:54 PDT 2022


Author: Walter Erquinigo
Date: 2022-04-07T15:58:34-07:00
New Revision: 6423b50235212db4c3a2e673b2b59fa5f6e07ec0

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

LOG: [trace][intel pt] Create a class for the libipt decoder wrapper

As we soon will need to decode multiple raw traces for the same thread,
having a class that encapsulates the decoding of a single raw trace is
a stepping stone that will make the coming features easier to implement.

So, I'm creating a LibiptDecoder class with that purpose. I refactored
the code and it's now much more readable. Besides that, more comments
were added. With this new structure, it's also easier to implement unit
tests.

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

Added: 
    lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
    lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h

Modified: 
    lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
    lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h

Removed: 
    lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h


################################################################################
diff  --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
index 58f7f904814ae..78d64d509b8cc 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
+++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -16,7 +16,8 @@ lldb_tablegen(TraceIntelPTCommandOptions.inc -gen-lldb-option-defs
 add_lldb_library(lldbPluginTraceIntelPT PLUGIN
   CommandObjectTraceStartIntelPT.cpp
   DecodedThread.cpp
-  IntelPTDecoder.cpp
+  LibiptDecoder.cpp
+  ThreadDecoder.cpp
   TraceCursorIntelPT.cpp
   TraceIntelPT.cpp
   TraceIntelPTJSONStructs.cpp

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
index f1a5a78ef0d11..d08c50e60cdb7 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -9,10 +9,10 @@
 #include "DecodedThread.h"
 
 #include <intel-pt.h>
-#include <memory>
 
 #include "TraceCursorIntelPT.h"
-#include "lldb/Utility/StreamString.h"
+
+#include <memory>
 
 using namespace lldb;
 using namespace lldb_private;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
deleted file mode 100644
index 7af56f13cb250..0000000000000
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-//===-- IntelPTDecoder.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 "IntelPTDecoder.h"
-
-#include "llvm/Support/MemoryBuffer.h"
-
-#include "../common/ThreadPostMortemTrace.h"
-#include "DecodedThread.h"
-#include "TraceIntelPT.h"
-#include "lldb/Core/Module.h"
-#include "lldb/Core/Section.h"
-#include "lldb/Target/Target.h"
-#include "lldb/Utility/StringExtractor.h"
-#include <utility>
-
-using namespace lldb;
-using namespace lldb_private;
-using namespace lldb_private::trace_intel_pt;
-using namespace llvm;
-
-/// Move the decoder forward to the next synchronization point (i.e. next PSB
-/// packet).
-///
-/// Once the decoder is at that sync. point, it can start decoding instructions.
-///
-/// \return
-///   A negative number with the libipt error if we couldn't synchronize.
-///   Otherwise, a positive number with the synchronization status will be
-///   returned.
-static int FindNextSynchronizationPoint(pt_insn_decoder &decoder) {
-  // Try to sync the decoder. If it fails, then get
-  // the decoder_offset and try to sync again from
-  // the next synchronization point. If the
-  // new_decoder_offset is same as decoder_offset
-  // then we can't move to the next synchronization
-  // point. Otherwise, keep resyncing until either
-  // end of trace stream (eos) is reached or
-  // pt_insn_sync_forward() passes.
-  int errcode = pt_insn_sync_forward(&decoder);
-
-  if (errcode != -pte_eos && errcode < 0) {
-    uint64_t decoder_offset = 0;
-    int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
-    if (errcode_off >= 0) { // we could get the offset
-      while (true) {
-        errcode = pt_insn_sync_forward(&decoder);
-        if (errcode >= 0 || errcode == -pte_eos)
-          break;
-
-        uint64_t new_decoder_offset = 0;
-        errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
-        if (errcode_off < 0)
-          break; // We can't further synchronize.
-        else if (new_decoder_offset <= decoder_offset) {
-          // We tried resyncing the decoder and
-          // decoder didn't make any progress because
-          // the offset didn't change. We will not
-          // make any progress further. Hence,
-          // stopping in this situation.
-          break;
-        }
-        // We'll try again starting from a new offset.
-        decoder_offset = new_decoder_offset;
-      }
-    }
-  }
-
-  return errcode;
-}
-
-/// Before querying instructions, we need to query the events associated that
-/// instruction e.g. timing events like ptev_tick, or paging events like
-/// ptev_paging.
-///
-/// \return
-///   0 if there were no errors processing the events, or a negative libipt
-///   error code in case of errors.
-static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) {
-  while (errcode & pts_event_pending) {
-    pt_event event;
-    errcode = pt_insn_event(&decoder, &event, sizeof(event));
-    if (errcode < 0)
-      return errcode;
-  }
-  return 0;
-}
-
-// Simple struct used by the decoder to keep the state of the most
-// recent TSC and a flag indicating whether TSCs are enabled, not enabled
-// or we just don't yet.
-struct TscInfo {
-  uint64_t tsc = 0;
-  LazyBool has_tsc = eLazyBoolCalculate;
-
-  explicit operator bool() const { return has_tsc == eLazyBoolYes; }
-};
-
-/// Query the decoder for the most recent TSC timestamp and update
-/// tsc_info accordingly.
-void RefreshTscInfo(TscInfo &tsc_info, pt_insn_decoder &decoder,
-                    DecodedThread &decoded_thread) {
-  if (tsc_info.has_tsc == eLazyBoolNo)
-    return;
-
-  uint64_t new_tsc;
-  if (int tsc_error = pt_insn_time(&decoder, &new_tsc, nullptr, nullptr)) {
-    if (tsc_error == -pte_no_time) {
-      // We now know that the trace doesn't support TSC, so we won't try again.
-      // See
-      // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_time.3.md
-      tsc_info.has_tsc = eLazyBoolNo;
-    } else {
-      // We don't add TSC decoding errors in the decoded trace itself to prevent
-      // creating unnecessary gaps, but we can count how many of these errors
-      // happened. In this case we reuse the previous correct TSC we saw, as
-      // it's better than no TSC at all.
-      decoded_thread.RecordTscError(tsc_error);
-    }
-  } else {
-    tsc_info.tsc = new_tsc;
-    tsc_info.has_tsc = eLazyBoolYes;
-  }
-}
-
-static void AppendError(DecodedThread &decoded_thread, Error &&error,
-                        TscInfo &tsc_info) {
-  if (tsc_info)
-    decoded_thread.AppendError(std::move(error), tsc_info.tsc);
-  else
-    decoded_thread.AppendError(std::move(error));
-}
-
-static void AppendInstruction(DecodedThread &decoded_thread,
-                              const pt_insn &insn, TscInfo &tsc_info) {
-  if (tsc_info)
-    decoded_thread.AppendInstruction(insn, tsc_info.tsc);
-  else
-    decoded_thread.AppendInstruction(insn);
-}
-
-/// 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.
-///
-/// 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, e.g. whether there are events that need to be decoded or not
-///
-/// \param[in] decoder
-///   A configured libipt \a pt_insn_decoder.
-static void DecodeInstructions(pt_insn_decoder &decoder,
-                               DecodedThread &decoded_thread) {
-
-  TscInfo tsc_info;
-  // We have this "global" errcode because if it's positive, we'll need
-  // its bits later to process events.
-  int errcode;
-
-  while (true) {
-    if ((errcode = FindNextSynchronizationPoint(decoder)) < 0) {
-      // We signal a gap only if it's not "end of stream"
-      if (errcode != -pte_eos)
-        AppendError(decoded_thread, make_error<IntelPTError>(errcode),
-                    tsc_info);
-      break;
-    }
-
-    // We have synchronized, so we can start decoding
-    // instructions and events.
-    while (true) {
-      if ((errcode = ProcessPTEvents(decoder, errcode)) < 0) {
-        AppendError(decoded_thread, make_error<IntelPTError>(errcode),
-                    tsc_info);
-        break;
-      }
-
-      // We refresh the TSC that might have changed after processing the events.
-      // See
-      // https://github.com/intel/libipt/blob/master/doc/man/pt_evt_next.3.md
-      RefreshTscInfo(tsc_info, decoder, decoded_thread);
-
-      pt_insn insn;
-      if ((errcode = pt_insn_next(&decoder, &insn, sizeof(insn))) < 0) {
-        // We signal a gap only if it's not "end of stream"
-        if (errcode != -pte_eos)
-          AppendError(decoded_thread,
-                      make_error<IntelPTError>(errcode, insn.ip), tsc_info);
-        break;
-      }
-      AppendInstruction(decoded_thread, insn, tsc_info);
-    }
-  }
-}
-
-/// Callback used by libipt for reading the process memory.
-///
-/// More information can be found in
-/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
-static int ReadProcessMemory(uint8_t *buffer, size_t size,
-                             const pt_asid * /* unused */, uint64_t pc,
-                             void *context) {
-  Process *process = static_cast<Process *>(context);
-
-  Status error;
-  int bytes_read = process->ReadMemory(pc, buffer, size, error);
-  if (error.Fail())
-    return -pte_nomap;
-  return bytes_read;
-}
-
-static void DecodeInMemoryTrace(DecodedThreadSP &decoded_thread_sp,
-                                TraceIntelPT &trace_intel_pt,
-                                MutableArrayRef<uint8_t> buffer) {
-  Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
-  if (!cpu_info) {
-    return decoded_thread_sp->AppendError(cpu_info.takeError());
-  }
-
-  pt_config config;
-  pt_config_init(&config);
-  config.cpu = *cpu_info;
-
-  if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
-    return decoded_thread_sp->AppendError(make_error<IntelPTError>(errcode));
-
-  config.begin = buffer.data();
-  config.end = buffer.data() + buffer.size();
-
-  pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
-  if (!decoder)
-    return decoded_thread_sp->AppendError(make_error<IntelPTError>(-pte_nomem));
-
-  pt_image *image = pt_insn_get_image(decoder);
-
-  int errcode =
-      pt_image_set_callback(image, ReadProcessMemory,
-                            decoded_thread_sp->GetThread()->GetProcess().get());
-  assert(errcode == 0);
-  (void)errcode;
-
-  DecodeInstructions(*decoder, *decoded_thread_sp);
-  pt_insn_free_decoder(decoder);
-}
-// ---------------------------
-
-DecodedThreadSP ThreadDecoder::Decode() {
-  if (!m_decoded_thread.hasValue())
-    m_decoded_thread = DoDecode();
-  return *m_decoded_thread;
-}
-
-// LiveThreadDecoder ====================
-
-LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
-    : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
-
-DecodedThreadSP LiveThreadDecoder::DoDecode() {
-  DecodedThreadSP decoded_thread_sp =
-      std::make_shared<DecodedThread>(m_thread_sp);
-
-  Expected<std::vector<uint8_t>> buffer =
-      m_trace.GetLiveThreadBuffer(m_thread_sp->GetID());
-  if (!buffer) {
-    decoded_thread_sp->AppendError(buffer.takeError());
-    return decoded_thread_sp;
-  }
-
-  decoded_thread_sp->SetRawTraceSize(buffer->size());
-  DecodeInMemoryTrace(decoded_thread_sp, m_trace,
-                      MutableArrayRef<uint8_t>(*buffer));
-  return decoded_thread_sp;
-}
-
-// PostMortemThreadDecoder =======================
-
-PostMortemThreadDecoder::PostMortemThreadDecoder(
-    const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
-    : m_trace_thread(trace_thread), m_trace(trace) {}
-
-DecodedThreadSP PostMortemThreadDecoder::DoDecode() {
-  DecodedThreadSP decoded_thread_sp =
-      std::make_shared<DecodedThread>(m_trace_thread);
-
-  ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
-      MemoryBuffer::getFile(m_trace_thread->GetTraceFile().GetPath());
-  if (std::error_code err = trace_or_error.getError()) {
-    decoded_thread_sp->AppendError(errorCodeToError(err));
-    return decoded_thread_sp;
-  }
-
-  MemoryBuffer &trace = **trace_or_error;
-  MutableArrayRef<uint8_t> trace_data(
-      // The libipt library does not modify the trace buffer, hence the
-      // following cast is safe.
-      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
-      trace.getBufferSize());
-  decoded_thread_sp->SetRawTraceSize(trace_data.size());
-
-  DecodeInMemoryTrace(decoded_thread_sp, m_trace, trace_data);
-  return decoded_thread_sp;
-}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
new file mode 100644
index 0000000000000..713795cb7264b
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
@@ -0,0 +1,298 @@
+//===-- LibiptDecoder.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 "LibiptDecoder.h"
+
+#include "TraceIntelPT.h"
+
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+// Simple struct used by the decoder to keep the state of the most
+// recent TSC and a flag indicating whether TSCs are enabled, not enabled
+// or we just don't yet.
+struct TscInfo {
+  uint64_t tsc = 0;
+  LazyBool has_tsc = eLazyBoolCalculate;
+
+  explicit operator bool() const { return has_tsc == eLazyBoolYes; }
+};
+
+static inline bool IsLibiptError(int libipt_status) {
+  return libipt_status < 0;
+}
+
+static inline bool IsEndOfStream(int libipt_status) {
+  return libipt_status == -pte_eos;
+}
+
+static inline bool IsTscUnavailable(int libipt_status) {
+  return libipt_status == -pte_no_time;
+}
+
+/// Class that decodes a raw buffer for a single thread using the low level
+/// libipt library.
+///
+/// Throughout this code, the status of the decoder will be used to identify
+/// events needed to be processed or errors in the decoder. The values can be
+/// - negative: actual errors
+/// - positive or zero: not an error, but a list of bits signaling the status
+/// of the decoder, e.g. whether there are events that need to be decoded or
+/// not.
+class LibiptDecoder {
+public:
+  /// \param[in] decoder
+  ///     A well configured decoder. Using the current state of that decoder,
+  ///     decoding will start at its next valid PSB. It's not assumed that the
+  ///     decoder is already pointing at a valid PSB.
+  ///
+  /// \param[in] decoded_thread
+  ///     A \a DecodedThread object where the decoded instructions will be
+  ///     appended to. It might have already some instructions.
+  LibiptDecoder(pt_insn_decoder &decoder, DecodedThread &decoded_thread)
+      : m_decoder(decoder), m_decoded_thread(decoded_thread) {}
+
+  /// 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.
+  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.
+      DecodeInstructionsAndEvents(status);
+    }
+  }
+
+private:
+  /// Decode all the instructions and events until an error is found or the end
+  /// of the trace is reached.
+  ///
+  /// \param[in] status
+  ///   The status that was result of synchronizing to the most recent PSB.
+  void DecodeInstructionsAndEvents(int status) {
+    pt_insn insn;
+    while (ProcessPTEvents(status)) {
+      status = pt_insn_next(&m_decoder, &insn, sizeof(insn));
+      // The status returned by pt_insn_next will need to be processed by
+      // ProcessPTEvents in the next loop.
+      if (FoundErrors(status, insn.ip))
+        break;
+      AppendInstruction(insn);
+    }
+  }
+
+  /// Move the decoder forward to the next synchronization point (i.e. next PSB
+  /// packet).
+  ///
+  /// Once the decoder is at that synchronization point, it can start decoding
+  /// instructions.
+  ///
+  /// \return
+  ///   The libipt decoder status after moving to the next PSB. Negative if
+  ///   no PSB was found.
+  int FindNextSynchronizationPoint() {
+    // Try to sync the decoder. If it fails, then get the decoder_offset and
+    // try to sync again from the next synchronization point. If the
+    // new_decoder_offset is same as decoder_offset then we can't move to the
+    // next synchronization point. Otherwise, keep resyncing until either end
+    // of trace stream (eos) is reached or pt_insn_sync_forward() passes.
+    int 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.
+    FoundErrors(status);
+    return status;
+  }
+
+  /// Before querying instructions, we need to query the events associated that
+  /// instruction e.g. timing events like ptev_tick, or paging events like
+  /// ptev_paging.
+  ///
+  /// \return
+  ///   \b true if we could process the events, \b false if errors were found.
+  bool ProcessPTEvents(int status) {
+    while (status & pts_event_pending) {
+      pt_event event;
+      status = pt_insn_event(&m_decoder, &event, sizeof(event));
+      if (IsLibiptError(status))
+        break;
+    }
+
+    // We refresh the TSC that might have changed after processing the events.
+    // See
+    // https://github.com/intel/libipt/blob/master/doc/man/pt_evt_next.3.md
+    RefreshTscInfo();
+    return !FoundErrors(status);
+  }
+
+  /// Query the decoder for the most recent TSC timestamp and update
+  /// the inner tsc information accordingly.
+  void RefreshTscInfo() {
+    if (m_tsc_info.has_tsc == eLazyBoolNo)
+      return;
+
+    uint64_t new_tsc;
+    int tsc_status;
+    if (IsLibiptError(tsc_status = pt_insn_time(&m_decoder, &new_tsc, nullptr,
+                                                nullptr))) {
+      if (IsTscUnavailable(tsc_status)) {
+        // We now know that the trace doesn't support TSC, so we won't try
+        // again.
+        // See
+        // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_time.3.md
+        m_tsc_info.has_tsc = eLazyBoolNo;
+      } else {
+        // We don't add TSC decoding errors in the decoded trace itself to
+        // prevent creating unnecessary gaps, but we can count how many of
+        // these errors happened. In this case we reuse the previous correct
+        // TSC we saw, as it's better than no TSC at all.
+        m_decoded_thread.RecordTscError(tsc_status);
+      }
+    } else {
+      m_tsc_info.tsc = new_tsc;
+      m_tsc_info.has_tsc = eLazyBoolYes;
+    }
+  }
+
+  /// Check if the given libipt status signals any errors. If errors were found,
+  /// they will be recorded in the decoded trace.
+  ///
+  /// \param[in] ip
+  ///     An optional ip address can be passed if the error is associated with
+  ///     the decoding of a specific instruction.
+  ///
+  /// \return
+  ///     \b true if errors were found, \b false otherwise.
+  bool FoundErrors(int status, lldb::addr_t ip = LLDB_INVALID_ADDRESS) {
+    if (!IsLibiptError(status))
+      return false;
+
+    // We signal a gap only if it's not "end of stream", as that's not a proper
+    // error.
+    if (!IsEndOfStream(status)) {
+      if (m_tsc_info) {
+        m_decoded_thread.AppendError(make_error<IntelPTError>(status, ip),
+                                     m_tsc_info.tsc);
+      } else {
+        m_decoded_thread.AppendError(make_error<IntelPTError>(status, ip));
+      }
+    }
+    return true;
+  }
+
+  void AppendInstruction(const pt_insn &insn) {
+    if (m_tsc_info)
+      m_decoded_thread.AppendInstruction(insn, m_tsc_info.tsc);
+    else
+      m_decoded_thread.AppendInstruction(insn);
+  }
+
+private:
+  pt_insn_decoder &m_decoder;
+  DecodedThread &m_decoded_thread;
+  TscInfo m_tsc_info;
+};
+
+/// Callback used by libipt for reading the process memory.
+///
+/// More information can be found in
+/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
+static int ReadProcessMemory(uint8_t *buffer, size_t size,
+                             const pt_asid * /* unused */, uint64_t pc,
+                             void *context) {
+  Process *process = static_cast<Process *>(context);
+
+  Status error;
+  int bytes_read = process->ReadMemory(pc, buffer, size, error);
+  if (error.Fail())
+    return -pte_nomap;
+  return bytes_read;
+}
+
+// RAII deleter for libipt's decoder
+auto DecoderDeleter = [](pt_insn_decoder *decoder) {
+  pt_insn_free_decoder(decoder);
+};
+
+using PtInsnDecoderUP =
+    std::unique_ptr<pt_insn_decoder, decltype(DecoderDeleter)>;
+
+static Expected<PtInsnDecoderUP>
+CreateInstructionDecoder(DecodedThread &decoded_thread,
+                         TraceIntelPT &trace_intel_pt,
+                         MutableArrayRef<uint8_t> buffer) {
+  Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
+  if (!cpu_info)
+    return cpu_info.takeError();
+
+  pt_config config;
+  pt_config_init(&config);
+  config.cpu = *cpu_info;
+  int status = pte_ok;
+
+  if (IsLibiptError(status = pt_cpu_errata(&config.errata, &config.cpu)))
+    return make_error<IntelPTError>(status);
+
+  config.begin = buffer.data();
+  config.end = buffer.data() + buffer.size();
+
+  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();
+
+  if (IsLibiptError(
+          status = pt_image_set_callback(image, ReadProcessMemory, process)))
+    return make_error<IntelPTError>(status);
+  return decoder_up;
+}
+
+void lldb_private::trace_intel_pt::DecodeTrace(
+    DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+    MutableArrayRef<uint8_t> buffer) {
+  Expected<PtInsnDecoderUP> decoder_up =
+      CreateInstructionDecoder(decoded_thread, trace_intel_pt, buffer);
+  if (!decoder_up)
+    return decoded_thread.AppendError(decoder_up.takeError());
+
+  LibiptDecoder libipt_decoder(*decoder_up.get(), decoded_thread);
+  libipt_decoder.DecodeUntilEndOfTrace();
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
new file mode 100644
index 0000000000000..c8c33b6a2eefc
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
@@ -0,0 +1,29 @@
+//===-- LibiptDecoder.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_LIBIPT_DECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H
+
+#include "intel-pt.h"
+
+#include "DecodedThread.h"
+#include "forward-declarations.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// 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::MutableArrayRef<uint8_t> buffer);
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H

diff  --git a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
new file mode 100644
index 0000000000000..2e6d13b2fa6e2
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
@@ -0,0 +1,79 @@
+//===-- ThreadDecoder.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 "ThreadDecoder.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "../common/ThreadPostMortemTrace.h"
+#include "LibiptDecoder.h"
+#include "TraceIntelPT.h"
+
+#include <utility>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+// ThreadDecoder ====================
+
+DecodedThreadSP ThreadDecoder::Decode() {
+  if (!m_decoded_thread.hasValue())
+    m_decoded_thread = DoDecode();
+  return *m_decoded_thread;
+}
+
+// LiveThreadDecoder ====================
+
+LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
+    : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
+
+DecodedThreadSP LiveThreadDecoder::DoDecode() {
+  DecodedThreadSP decoded_thread_sp =
+      std::make_shared<DecodedThread>(m_thread_sp);
+
+  Expected<std::vector<uint8_t>> buffer =
+      m_trace.GetLiveThreadBuffer(m_thread_sp->GetID());
+  if (!buffer) {
+    decoded_thread_sp->AppendError(buffer.takeError());
+    return decoded_thread_sp;
+  }
+
+  decoded_thread_sp->SetRawTraceSize(buffer->size());
+  DecodeTrace(*decoded_thread_sp, m_trace, MutableArrayRef<uint8_t>(*buffer));
+  return decoded_thread_sp;
+}
+
+// PostMortemThreadDecoder =======================
+
+PostMortemThreadDecoder::PostMortemThreadDecoder(
+    const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
+    : m_trace_thread(trace_thread), m_trace(trace) {}
+
+DecodedThreadSP PostMortemThreadDecoder::DoDecode() {
+  DecodedThreadSP decoded_thread_sp =
+      std::make_shared<DecodedThread>(m_trace_thread);
+
+  ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
+      MemoryBuffer::getFile(m_trace_thread->GetTraceFile().GetPath());
+  if (std::error_code err = trace_or_error.getError()) {
+    decoded_thread_sp->AppendError(errorCodeToError(err));
+    return decoded_thread_sp;
+  }
+
+  MemoryBuffer &trace = **trace_or_error;
+  MutableArrayRef<uint8_t> trace_data(
+      // The libipt library does not modify the trace buffer, hence the
+      // following cast is safe.
+      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
+      trace.getBufferSize());
+  decoded_thread_sp->SetRawTraceSize(trace_data.size());
+
+  DecodeTrace(*decoded_thread_sp, m_trace, trace_data);
+  return decoded_thread_sp;
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h
similarity index 90%
rename from lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
rename to lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h
index e969db579e52a..b487667c34b1c 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h
@@ -1,4 +1,4 @@
-//===-- IntelPTDecoder.h --======--------------------------------*- C++ -*-===//
+//===-- ThreadDecoder.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.
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
-#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_THREAD_DECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_THREAD_DECODER_H
 
 #include "intel-pt.h"
 
@@ -84,4 +84,4 @@ class LiveThreadDecoder : public ThreadDecoder {
 } // namespace trace_intel_pt
 } // namespace lldb_private
 
-#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
+#endif // LLDB_SOURCE_PLUGINS_TRACE_THREAD_DECODER_H

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
index 7cec257793fb2..80e00a832b72d 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -9,7 +9,7 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
 
-#include "IntelPTDecoder.h"
+#include "ThreadDecoder.h"
 #include "TraceIntelPTSessionFileParser.h"
 
 namespace lldb_private {

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index a6ecf6f906b2c..a8d51f4fa5e7a 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -9,7 +9,7 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
 
-#include "IntelPTDecoder.h"
+#include "ThreadDecoder.h"
 #include "TraceIntelPTSessionFileParser.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/lldb-types.h"


        


More information about the lldb-commits mailing list