[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