aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp215
1 files changed, 215 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
new file mode 100644
index 000000000000..b6e8ae808632
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -0,0 +1,215 @@
+//===-- IntelPTDecoder.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IntelPTDecoder.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadTrace.h"
+
+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;
+};
+
+/// 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
+///
+/// \param[in] decoder
+/// A configured libipt \a pt_insn_decoder.
+///
+/// \return
+/// The decoded instructions.
+static std::vector<IntelPTInstruction>
+DecodeInstructions(pt_insn_decoder &decoder) {
+ std::vector<IntelPTInstruction> instructions;
+
+ while (true) {
+ int errcode = FindNextSynchronizationPoint(decoder);
+ if (errcode == -pte_eos)
+ break;
+
+ if (errcode < 0) {
+ instructions.emplace_back(make_error<IntelPTError>(errcode));
+ break;
+ }
+
+ // We have synchronized, so we can start decoding
+ // instructions and events.
+ while (true) {
+ errcode = ProcessPTEvents(decoder, errcode);
+ if (errcode < 0) {
+ instructions.emplace_back(make_error<IntelPTError>(errcode));
+ break;
+ }
+ pt_insn insn;
+
+ errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
+ if (errcode == -pte_eos)
+ break;
+
+ if (errcode < 0) {
+ instructions.emplace_back(make_error<IntelPTError>(errcode, insn.ip));
+ break;
+ }
+
+ instructions.emplace_back(insn);
+ }
+ }
+
+ return instructions;
+}
+
+/// 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 std::vector<IntelPTInstruction> makeInstructionListFromError(Error err) {
+ std::vector<IntelPTInstruction> instructions;
+ instructions.emplace_back(std::move(err));
+ return instructions;
+}
+
+static std::vector<IntelPTInstruction>
+CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+ const FileSpec &trace_file) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
+ MemoryBuffer::getFile(trace_file.GetPath());
+ if (std::error_code err = trace_or_error.getError())
+ return makeInstructionListFromError(errorCodeToError(err));
+
+ MemoryBuffer &trace = **trace_or_error;
+
+ pt_config config;
+ pt_config_init(&config);
+ config.cpu = pt_cpu;
+
+ if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
+ return makeInstructionListFromError(make_error<IntelPTError>(errcode));
+
+ // The libipt library does not modify the trace buffer, hence the following
+ // cast is safe.
+ config.begin =
+ reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart()));
+ config.end =
+ reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd()));
+
+ pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
+ if (!decoder)
+ return makeInstructionListFromError(make_error<IntelPTError>(-pte_nomem));
+
+ pt_image *image = pt_insn_get_image(decoder);
+
+ int errcode = pt_image_set_callback(image, ReadProcessMemory, &process);
+ assert(errcode == 0);
+ (void)errcode;
+
+ std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder);
+
+ pt_insn_free_decoder(decoder);
+ return instructions;
+}
+
+const DecodedThread &ThreadTraceDecoder::Decode() {
+ if (!m_decoded_thread.hasValue()) {
+ m_decoded_thread = DecodedThread(
+ CreateDecoderAndDecode(*m_trace_thread->GetProcess(), m_pt_cpu,
+ m_trace_thread->GetTraceFile()));
+ }
+
+ return *m_decoded_thread;
+}