From 6423b50235212db4c3a2e673b2b59fa5f6e07ec0 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Fri, 1 Apr 2022 11:44:51 -0700 Subject: [PATCH] [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 --- lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt | 3 +- .../Plugins/Trace/intel-pt/DecodedThread.cpp | 4 +- .../Plugins/Trace/intel-pt/IntelPTDecoder.cpp | 308 --------------------- .../Plugins/Trace/intel-pt/LibiptDecoder.cpp | 298 ++++++++++++++++++++ lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h | 29 ++ .../Plugins/Trace/intel-pt/ThreadDecoder.cpp | 79 ++++++ .../intel-pt/{IntelPTDecoder.h => ThreadDecoder.h} | 8 +- .../Plugins/Trace/intel-pt/TraceCursorIntelPT.h | 2 +- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h | 2 +- 9 files changed, 416 insertions(+), 317 deletions(-) delete mode 100644 lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp create mode 100644 lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp create mode 100644 lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h create mode 100644 lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp rename lldb/source/Plugins/Trace/intel-pt/{IntelPTDecoder.h => ThreadDecoder.h} (90%) diff --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt index 58f7f90..78d64d5 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 f1a5a78..d08c50e 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 -#include #include "TraceCursorIntelPT.h" -#include "lldb/Utility/StreamString.h" + +#include 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 7af56f1..0000000 --- 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 - -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(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(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(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(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 buffer) { - Expected 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(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(-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(m_thread_sp); - - Expected> 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(*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(m_trace_thread); - - ErrorOr> 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 trace_data( - // The libipt library does not modify the trace buffer, hence the - // following cast is safe. - reinterpret_cast(const_cast(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 0000000..713795c --- /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(status, ip), + m_tsc_info.tsc); + } else { + m_decoded_thread.AppendError(make_error(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(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; + +static Expected +CreateInstructionDecoder(DecodedThread &decoded_thread, + TraceIntelPT &trace_intel_pt, + MutableArrayRef buffer) { + Expected 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(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(-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(status); + return decoder_up; +} + +void lldb_private::trace_intel_pt::DecodeTrace( + DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, + MutableArrayRef buffer) { + Expected 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 0000000..c8c33b6 --- /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 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 0000000..2e6d13b --- /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 + +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(m_thread_sp); + + Expected> 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(*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(m_trace_thread); + + ErrorOr> 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 trace_data( + // The libipt library does not modify the trace buffer, hence the + // following cast is safe. + reinterpret_cast(const_cast(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 e969db5..b487667 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 @@ private: } // 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 7cec257..80e00a8 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 a6ecf6f..a8d51f4 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" -- 2.7.4