Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
39645 views
//===-- LibiptDecoder.cpp --======-----------------------------------------===//1// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.2// See https://llvm.org/LICENSE.txt for license information.3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception4//5//===----------------------------------------------------------------------===//67#include "LibiptDecoder.h"8#include "TraceIntelPT.h"9#include "lldb/Target/Process.h"10#include <optional>1112using namespace lldb;13using namespace lldb_private;14using namespace lldb_private::trace_intel_pt;15using namespace llvm;1617bool IsLibiptError(int status) { return status < 0; }1819bool IsEndOfStream(int status) {20assert(status >= 0 && "We can't check if we reached the end of the stream if "21"we got a failed status");22return status & pts_eos;23}2425bool HasEvents(int status) {26assert(status >= 0 && "We can't check for events if we got a failed status");27return status & pts_event_pending;28}2930// RAII deleter for libipt's decoders31auto InsnDecoderDeleter = [](pt_insn_decoder *decoder) {32pt_insn_free_decoder(decoder);33};3435auto QueryDecoderDeleter = [](pt_query_decoder *decoder) {36pt_qry_free_decoder(decoder);37};3839using PtInsnDecoderUP =40std::unique_ptr<pt_insn_decoder, decltype(InsnDecoderDeleter)>;4142using PtQueryDecoderUP =43std::unique_ptr<pt_query_decoder, decltype(QueryDecoderDeleter)>;4445/// Create a basic configuration object limited to a given buffer that can be46/// used for many different decoders.47static Expected<pt_config> CreateBasicLibiptConfig(TraceIntelPT &trace_intel_pt,48ArrayRef<uint8_t> buffer) {49Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();50if (!cpu_info)51return cpu_info.takeError();5253pt_config config;54pt_config_init(&config);55config.cpu = *cpu_info;5657int status = pt_cpu_errata(&config.errata, &config.cpu);58if (IsLibiptError(status))59return make_error<IntelPTError>(status);6061// The libipt library does not modify the trace buffer, hence the62// following casts are safe.63config.begin = const_cast<uint8_t *>(buffer.data());64config.end = const_cast<uint8_t *>(buffer.data() + buffer.size());65return config;66}6768/// Callback used by libipt for reading the process memory.69///70/// More information can be found in71/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md72static int ReadProcessMemory(uint8_t *buffer, size_t size,73const pt_asid * /* unused */, uint64_t pc,74void *context) {75Process *process = static_cast<Process *>(context);7677Status error;78int bytes_read = process->ReadMemory(pc, buffer, size, error);79if (error.Fail())80return -pte_nomap;81return bytes_read;82}8384/// Set up the memory image callback for the given decoder.85static Error SetupMemoryImage(pt_insn_decoder *decoder, Process &process) {86pt_image *image = pt_insn_get_image(decoder);8788int status = pt_image_set_callback(image, ReadProcessMemory, &process);89if (IsLibiptError(status))90return make_error<IntelPTError>(status);91return Error::success();92}9394/// Create an instruction decoder for the given buffer and the given process.95static Expected<PtInsnDecoderUP>96CreateInstructionDecoder(TraceIntelPT &trace_intel_pt, ArrayRef<uint8_t> buffer,97Process &process) {98Expected<pt_config> config = CreateBasicLibiptConfig(trace_intel_pt, buffer);99if (!config)100return config.takeError();101102pt_insn_decoder *decoder_ptr = pt_insn_alloc_decoder(&*config);103if (!decoder_ptr)104return make_error<IntelPTError>(-pte_nomem);105106PtInsnDecoderUP decoder_up(decoder_ptr, InsnDecoderDeleter);107108if (Error err = SetupMemoryImage(decoder_ptr, process))109return std::move(err);110111return decoder_up;112}113114/// Create a query decoder for the given buffer. The query decoder is the115/// highest level decoder that operates directly on packets and doesn't perform116/// actual instruction decoding. That's why it can be useful for inspecting a117/// raw trace without pinning it to a particular process.118static Expected<PtQueryDecoderUP>119CreateQueryDecoder(TraceIntelPT &trace_intel_pt, ArrayRef<uint8_t> buffer) {120Expected<pt_config> config = CreateBasicLibiptConfig(trace_intel_pt, buffer);121if (!config)122return config.takeError();123124pt_query_decoder *decoder_ptr = pt_qry_alloc_decoder(&*config);125if (!decoder_ptr)126return make_error<IntelPTError>(-pte_nomem);127128return PtQueryDecoderUP(decoder_ptr, QueryDecoderDeleter);129}130131/// Class used to identify anomalies in traces, which should often indicate a132/// fatal error in the trace.133class PSBBlockAnomalyDetector {134public:135PSBBlockAnomalyDetector(pt_insn_decoder &decoder,136TraceIntelPT &trace_intel_pt,137DecodedThread &decoded_thread)138: m_decoder(decoder), m_decoded_thread(decoded_thread) {139m_infinite_decoding_loop_threshold =140trace_intel_pt.GetGlobalProperties()141.GetInfiniteDecodingLoopVerificationThreshold();142m_extremely_large_decoding_threshold =143trace_intel_pt.GetGlobalProperties()144.GetExtremelyLargeDecodingThreshold();145m_next_infinite_decoding_loop_threshold =146m_infinite_decoding_loop_threshold;147}148149/// \return150/// An \a llvm::Error if an anomaly that includes the last instruction item151/// in the trace, or \a llvm::Error::success otherwise.152Error DetectAnomaly() {153RefreshPacketOffset();154uint64_t insn_added_since_last_packet_offset =155m_decoded_thread.GetTotalInstructionCount() -156m_insn_count_at_last_packet_offset;157158// We want to check if we might have fallen in an infinite loop. As this159// check is not a no-op, we want to do it when we have a strong suggestion160// that things went wrong. First, we check how many instructions we have161// decoded since we processed an Intel PT packet for the last time. This162// number should be low, because at some point we should see branches, jumps163// or interrupts that require a new packet to be processed. Once we reach164// certain threshold we start analyzing the trace.165//166// We use the number of decoded instructions since the last Intel PT packet167// as a proxy because, in fact, we don't expect a single packet to give,168// say, 100k instructions. That would mean that there are 100k sequential169// instructions without any single branch, which is highly unlikely, or that170// we found an infinite loop using direct jumps, e.g.171//172// 0x0A: nop or pause173// 0x0C: jump to 0x0A174//175// which is indeed code that is found in the kernel. I presume we reach176// this kind of code in the decoder because we don't handle self-modified177// code in post-mortem kernel traces.178//179// We are right now only signaling the anomaly as a trace error, but it180// would be more conservative to also discard all the trace items found in181// this PSB. I prefer not to do that for the time being to give more182// exposure to this kind of anomalies and help debugging. Discarding the183// trace items would just make investigation harded.184//185// Finally, if the user wants to see if a specific thread has an anomaly,186// it's enough to run the `thread trace dump info` command and look for the187// count of this kind of errors.188189if (insn_added_since_last_packet_offset >=190m_extremely_large_decoding_threshold) {191// In this case, we have decoded a massive amount of sequential192// instructions that don't loop. Honestly I wonder if this will ever193// happen, but better safe than sorry.194return createStringError(195inconvertibleErrorCode(),196"anomalous trace: possible infinite trace detected");197}198if (insn_added_since_last_packet_offset ==199m_next_infinite_decoding_loop_threshold) {200if (std::optional<uint64_t> loop_size = TryIdentifyInfiniteLoop()) {201return createStringError(202inconvertibleErrorCode(),203"anomalous trace: possible infinite loop detected of size %" PRIu64,204*loop_size);205}206m_next_infinite_decoding_loop_threshold *= 2;207}208return Error::success();209}210211private:212std::optional<uint64_t> TryIdentifyInfiniteLoop() {213// The infinite decoding loops we'll encounter are due to sequential214// instructions that repeat themselves due to direct jumps, therefore in a215// cycle each individual address will only appear once. We use this216// information to detect cycles by finding the last 2 ocurrences of the last217// instruction added to the trace. Then we traverse the trace making sure218// that these two instructions where the ends of a repeating loop.219220// This is a utility that returns the most recent instruction index given a221// position in the trace. If the given position is an instruction, that222// position is returned. It skips non-instruction items.223auto most_recent_insn_index =224[&](uint64_t item_index) -> std::optional<uint64_t> {225while (true) {226if (m_decoded_thread.GetItemKindByIndex(item_index) ==227lldb::eTraceItemKindInstruction) {228return item_index;229}230if (item_index == 0)231return std::nullopt;232item_index--;233}234return std::nullopt;235};236// Similar to most_recent_insn_index but skips the starting position.237auto prev_insn_index = [&](uint64_t item_index) -> std::optional<uint64_t> {238if (item_index == 0)239return std::nullopt;240return most_recent_insn_index(item_index - 1);241};242243// We first find the most recent instruction.244std::optional<uint64_t> last_insn_index_opt =245*prev_insn_index(m_decoded_thread.GetItemsCount());246if (!last_insn_index_opt)247return std::nullopt;248uint64_t last_insn_index = *last_insn_index_opt;249250// We then find the most recent previous occurrence of that last251// instruction.252std::optional<uint64_t> last_insn_copy_index =253prev_insn_index(last_insn_index);254uint64_t loop_size = 1;255while (last_insn_copy_index &&256m_decoded_thread.GetInstructionLoadAddress(*last_insn_copy_index) !=257m_decoded_thread.GetInstructionLoadAddress(last_insn_index)) {258last_insn_copy_index = prev_insn_index(*last_insn_copy_index);259loop_size++;260}261if (!last_insn_copy_index)262return std::nullopt;263264// Now we check if the segment between these last positions of the last265// instruction address is in fact a repeating loop.266uint64_t loop_elements_visited = 1;267uint64_t insn_index_a = last_insn_index,268insn_index_b = *last_insn_copy_index;269while (loop_elements_visited < loop_size) {270if (std::optional<uint64_t> prev = prev_insn_index(insn_index_a))271insn_index_a = *prev;272else273return std::nullopt;274if (std::optional<uint64_t> prev = prev_insn_index(insn_index_b))275insn_index_b = *prev;276else277return std::nullopt;278if (m_decoded_thread.GetInstructionLoadAddress(insn_index_a) !=279m_decoded_thread.GetInstructionLoadAddress(insn_index_b))280return std::nullopt;281loop_elements_visited++;282}283return loop_size;284}285286// Refresh the internal counters if a new packet offset has been visited287void RefreshPacketOffset() {288lldb::addr_t new_packet_offset;289if (!IsLibiptError(pt_insn_get_offset(&m_decoder, &new_packet_offset)) &&290new_packet_offset != m_last_packet_offset) {291m_last_packet_offset = new_packet_offset;292m_next_infinite_decoding_loop_threshold =293m_infinite_decoding_loop_threshold;294m_insn_count_at_last_packet_offset =295m_decoded_thread.GetTotalInstructionCount();296}297}298299pt_insn_decoder &m_decoder;300DecodedThread &m_decoded_thread;301lldb::addr_t m_last_packet_offset = LLDB_INVALID_ADDRESS;302uint64_t m_insn_count_at_last_packet_offset = 0;303uint64_t m_infinite_decoding_loop_threshold;304uint64_t m_next_infinite_decoding_loop_threshold;305uint64_t m_extremely_large_decoding_threshold;306};307308/// Class that decodes a raw buffer for a single PSB block using the low level309/// libipt library. It assumes that kernel and user mode instructions are not310/// mixed in the same PSB block.311///312/// Throughout this code, the status of the decoder will be used to identify313/// events needed to be processed or errors in the decoder. The values can be314/// - negative: actual errors315/// - positive or zero: not an error, but a list of bits signaling the status316/// of the decoder, e.g. whether there are events that need to be decoded or317/// not.318class PSBBlockDecoder {319public:320/// \param[in] decoder321/// A decoder configured to start and end within the boundaries of the322/// given \p psb_block.323///324/// \param[in] psb_block325/// The PSB block to decode.326///327/// \param[in] next_block_ip328/// The starting ip at the next PSB block of the same thread if available.329///330/// \param[in] decoded_thread331/// A \a DecodedThread object where the decoded instructions will be332/// appended to. It might have already some instructions.333///334/// \param[in] tsc_upper_bound335/// Maximum allowed value of TSCs decoded from this PSB block.336/// Any of this PSB's data occurring after this TSC will be excluded.337PSBBlockDecoder(PtInsnDecoderUP &&decoder_up, const PSBBlock &psb_block,338std::optional<lldb::addr_t> next_block_ip,339DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,340std::optional<DecodedThread::TSC> tsc_upper_bound)341: m_decoder_up(std::move(decoder_up)), m_psb_block(psb_block),342m_next_block_ip(next_block_ip), m_decoded_thread(decoded_thread),343m_anomaly_detector(*m_decoder_up, trace_intel_pt, decoded_thread),344m_tsc_upper_bound(tsc_upper_bound) {}345346/// \param[in] trace_intel_pt347/// The main Trace object that own the PSB block.348///349/// \param[in] decoder350/// A decoder configured to start and end within the boundaries of the351/// given \p psb_block.352///353/// \param[in] psb_block354/// The PSB block to decode.355///356/// \param[in] buffer357/// The raw intel pt trace for this block.358///359/// \param[in] process360/// The process to decode. It provides the memory image to use for361/// decoding.362///363/// \param[in] next_block_ip364/// The starting ip at the next PSB block of the same thread if available.365///366/// \param[in] decoded_thread367/// A \a DecodedThread object where the decoded instructions will be368/// appended to. It might have already some instructions.369static Expected<PSBBlockDecoder>370Create(TraceIntelPT &trace_intel_pt, const PSBBlock &psb_block,371ArrayRef<uint8_t> buffer, Process &process,372std::optional<lldb::addr_t> next_block_ip,373DecodedThread &decoded_thread,374std::optional<DecodedThread::TSC> tsc_upper_bound) {375Expected<PtInsnDecoderUP> decoder_up =376CreateInstructionDecoder(trace_intel_pt, buffer, process);377if (!decoder_up)378return decoder_up.takeError();379380return PSBBlockDecoder(std::move(*decoder_up), psb_block, next_block_ip,381decoded_thread, trace_intel_pt, tsc_upper_bound);382}383384void DecodePSBBlock() {385int status = pt_insn_sync_forward(m_decoder_up.get());386assert(status >= 0 &&387"Synchronization shouldn't fail because this PSB was previously "388"decoded correctly.");389390// We emit a TSC before a sync event to more easily associate a timestamp to391// the sync event. If present, the current block's TSC would be the first392// TSC we'll see when processing events.393if (m_psb_block.tsc)394m_decoded_thread.NotifyTsc(*m_psb_block.tsc);395396m_decoded_thread.NotifySyncPoint(m_psb_block.psb_offset);397398DecodeInstructionsAndEvents(status);399}400401private:402/// Append an instruction and return \b false if and only if a serious anomaly403/// has been detected.404bool AppendInstructionAndDetectAnomalies(const pt_insn &insn) {405m_decoded_thread.AppendInstruction(insn);406407if (Error err = m_anomaly_detector.DetectAnomaly()) {408m_decoded_thread.AppendCustomError(toString(std::move(err)),409/*fatal=*/true);410return false;411}412return true;413}414/// Decode all the instructions and events of the given PSB block. The415/// decoding loop might stop abruptly if an infinite decoding loop is416/// detected.417void DecodeInstructionsAndEvents(int status) {418pt_insn insn;419420while (true) {421status = ProcessPTEvents(status);422423if (IsLibiptError(status))424return;425else if (IsEndOfStream(status))426break;427428// The status returned by pt_insn_next will need to be processed429// by ProcessPTEvents in the next loop if it is not an error.430std::memset(&insn, 0, sizeof insn);431status = pt_insn_next(m_decoder_up.get(), &insn, sizeof(insn));432433if (IsLibiptError(status)) {434m_decoded_thread.AppendError(IntelPTError(status, insn.ip));435return;436} else if (IsEndOfStream(status)) {437break;438}439440if (!AppendInstructionAndDetectAnomalies(insn))441return;442}443444// We need to keep querying non-branching instructions until we hit the445// starting point of the next PSB. We won't see events at this point. This446// is based on447// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#parallel-decode448if (m_next_block_ip && insn.ip != 0) {449while (insn.ip != *m_next_block_ip) {450if (!AppendInstructionAndDetectAnomalies(insn))451return;452453status = pt_insn_next(m_decoder_up.get(), &insn, sizeof(insn));454455if (IsLibiptError(status)) {456m_decoded_thread.AppendError(IntelPTError(status, insn.ip));457return;458}459}460}461}462463/// Process the TSC of a decoded PT event. Specifically, check if this TSC464/// is below the TSC upper bound for this PSB. If the TSC exceeds the upper465/// bound, return an error to abort decoding. Otherwise add the it to the466/// underlying DecodedThread and decoding should continue as expected.467///468/// \param[in] tsc469/// The TSC of the a decoded event.470Error ProcessPTEventTSC(DecodedThread::TSC tsc) {471if (m_tsc_upper_bound && tsc >= *m_tsc_upper_bound) {472// This event and all the remaining events of this PSB have a TSC473// outside the range of the "owning" ThreadContinuousExecution. For474// now we drop all of these events/instructions, future work can475// improve upon this by determining the "owning"476// ThreadContinuousExecution of the remaining PSB data.477std::string err_msg = formatv("decoding truncated: TSC {0} exceeds "478"maximum TSC value {1}, will skip decoding"479" the remaining data of the PSB",480tsc, *m_tsc_upper_bound)481.str();482483uint64_t offset;484int status = pt_insn_get_offset(m_decoder_up.get(), &offset);485if (!IsLibiptError(status)) {486err_msg = formatv("{2} (skipping {0} of {1} bytes)", offset,487m_psb_block.size, err_msg)488.str();489}490m_decoded_thread.AppendCustomError(err_msg);491return createStringError(inconvertibleErrorCode(), err_msg);492} else {493m_decoded_thread.NotifyTsc(tsc);494return Error::success();495}496}497498/// Before querying instructions, we need to query the events associated with499/// that instruction, e.g. timing and trace disablement events.500///501/// \param[in] status502/// The status gotten from the previous instruction decoding or PSB503/// synchronization.504///505/// \return506/// The pte_status after decoding events.507int ProcessPTEvents(int status) {508while (HasEvents(status)) {509pt_event event;510std::memset(&event, 0, sizeof event);511status = pt_insn_event(m_decoder_up.get(), &event, sizeof(event));512513if (IsLibiptError(status)) {514m_decoded_thread.AppendError(IntelPTError(status));515return status;516}517518if (event.has_tsc) {519if (Error err = ProcessPTEventTSC(event.tsc)) {520consumeError(std::move(err));521return -pte_internal;522}523}524525switch (event.type) {526case ptev_disabled:527// The CPU paused tracing the program, e.g. due to ip filtering.528m_decoded_thread.AppendEvent(lldb::eTraceEventDisabledHW);529break;530case ptev_async_disabled:531// The kernel or user code paused tracing the program, e.g.532// a breakpoint or a ioctl invocation pausing the trace, or a533// context switch happened.534m_decoded_thread.AppendEvent(lldb::eTraceEventDisabledSW);535break;536case ptev_overflow:537// The CPU internal buffer had an overflow error and some instructions538// were lost. A OVF packet comes with an FUP packet (harcoded address)539// according to the documentation, so we'll continue seeing instructions540// after this event.541m_decoded_thread.AppendError(IntelPTError(-pte_overflow));542break;543default:544break;545}546}547548return status;549}550551private:552PtInsnDecoderUP m_decoder_up;553PSBBlock m_psb_block;554std::optional<lldb::addr_t> m_next_block_ip;555DecodedThread &m_decoded_thread;556PSBBlockAnomalyDetector m_anomaly_detector;557std::optional<DecodedThread::TSC> m_tsc_upper_bound;558};559560Error lldb_private::trace_intel_pt::DecodeSingleTraceForThread(561DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,562ArrayRef<uint8_t> buffer) {563Expected<std::vector<PSBBlock>> blocks =564SplitTraceIntoPSBBlock(trace_intel_pt, buffer, /*expect_tscs=*/false);565if (!blocks)566return blocks.takeError();567568for (size_t i = 0; i < blocks->size(); i++) {569PSBBlock &block = blocks->at(i);570571Expected<PSBBlockDecoder> decoder = PSBBlockDecoder::Create(572trace_intel_pt, block, buffer.slice(block.psb_offset, block.size),573*decoded_thread.GetThread()->GetProcess(),574i + 1 < blocks->size() ? blocks->at(i + 1).starting_ip : std::nullopt,575decoded_thread, std::nullopt);576if (!decoder)577return decoder.takeError();578579decoder->DecodePSBBlock();580}581582return Error::success();583}584585Error lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread(586DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,587const DenseMap<lldb::cpu_id_t, llvm::ArrayRef<uint8_t>> &buffers,588const std::vector<IntelPTThreadContinousExecution> &executions) {589bool has_seen_psbs = false;590for (size_t i = 0; i < executions.size(); i++) {591const IntelPTThreadContinousExecution &execution = executions[i];592593auto variant = execution.thread_execution.variant;594595// We emit the first valid tsc596if (execution.psb_blocks.empty()) {597decoded_thread.NotifyTsc(execution.thread_execution.GetLowestKnownTSC());598} else {599assert(execution.psb_blocks.front().tsc &&600"per cpu decoding expects TSCs");601decoded_thread.NotifyTsc(602std::min(execution.thread_execution.GetLowestKnownTSC(),603*execution.psb_blocks.front().tsc));604}605606// We then emit the CPU, which will be correctly associated with a tsc.607decoded_thread.NotifyCPU(execution.thread_execution.cpu_id);608609// If we haven't seen a PSB yet, then it's fine not to show errors610if (has_seen_psbs) {611if (execution.psb_blocks.empty()) {612decoded_thread.AppendCustomError(613formatv("Unable to find intel pt data a thread "614"execution on cpu id = {0}",615execution.thread_execution.cpu_id)616.str());617}618619// A hinted start is a non-initial execution that doesn't have a switch620// in. An only end is an initial execution that doesn't have a switch in.621// Any of those cases represent a gap because we have seen a PSB before.622if (variant == ThreadContinuousExecution::Variant::HintedStart ||623variant == ThreadContinuousExecution::Variant::OnlyEnd) {624decoded_thread.AppendCustomError(625formatv("Unable to find the context switch in for a thread "626"execution on cpu id = {0}",627execution.thread_execution.cpu_id)628.str());629}630}631632for (size_t j = 0; j < execution.psb_blocks.size(); j++) {633const PSBBlock &psb_block = execution.psb_blocks[j];634635Expected<PSBBlockDecoder> decoder = PSBBlockDecoder::Create(636trace_intel_pt, psb_block,637buffers.lookup(execution.thread_execution.cpu_id)638.slice(psb_block.psb_offset, psb_block.size),639*decoded_thread.GetThread()->GetProcess(),640j + 1 < execution.psb_blocks.size()641? execution.psb_blocks[j + 1].starting_ip642: std::nullopt,643decoded_thread, execution.thread_execution.GetEndTSC());644if (!decoder)645return decoder.takeError();646647has_seen_psbs = true;648decoder->DecodePSBBlock();649}650651// If we haven't seen a PSB yet, then it's fine not to show errors652if (has_seen_psbs) {653// A hinted end is a non-ending execution that doesn't have a switch out.654// An only start is an ending execution that doesn't have a switch out.655// Any of those cases represent a gap if we still have executions to656// process and we have seen a PSB before.657if (i + 1 != executions.size() &&658(variant == ThreadContinuousExecution::Variant::OnlyStart ||659variant == ThreadContinuousExecution::Variant::HintedEnd)) {660decoded_thread.AppendCustomError(661formatv("Unable to find the context switch out for a thread "662"execution on cpu id = {0}",663execution.thread_execution.cpu_id)664.str());665}666}667}668return Error::success();669}670671bool IntelPTThreadContinousExecution::operator<(672const IntelPTThreadContinousExecution &o) const {673// As the context switch might be incomplete, we look first for the first real674// PSB packet, which is a valid TSC. Otherwise, We query the thread execution675// itself for some tsc.676auto get_tsc = [](const IntelPTThreadContinousExecution &exec) {677return exec.psb_blocks.empty() ? exec.thread_execution.GetLowestKnownTSC()678: exec.psb_blocks.front().tsc;679};680681return get_tsc(*this) < get_tsc(o);682}683684Expected<std::vector<PSBBlock>>685lldb_private::trace_intel_pt::SplitTraceIntoPSBBlock(686TraceIntelPT &trace_intel_pt, llvm::ArrayRef<uint8_t> buffer,687bool expect_tscs) {688// This follows689// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#parallel-decode690691Expected<PtQueryDecoderUP> decoder_up =692CreateQueryDecoder(trace_intel_pt, buffer);693if (!decoder_up)694return decoder_up.takeError();695696pt_query_decoder *decoder = decoder_up.get().get();697698std::vector<PSBBlock> executions;699700while (true) {701uint64_t maybe_ip = LLDB_INVALID_ADDRESS;702int decoding_status = pt_qry_sync_forward(decoder, &maybe_ip);703if (IsLibiptError(decoding_status))704break;705706uint64_t psb_offset;707int offset_status = pt_qry_get_sync_offset(decoder, &psb_offset);708assert(offset_status >= 0 &&709"This can't fail because we were able to synchronize");710711std::optional<uint64_t> ip;712if (!(pts_ip_suppressed & decoding_status))713ip = maybe_ip;714715std::optional<uint64_t> tsc;716// Now we fetch the first TSC that comes after the PSB.717while (HasEvents(decoding_status)) {718pt_event event;719decoding_status = pt_qry_event(decoder, &event, sizeof(event));720if (IsLibiptError(decoding_status))721break;722if (event.has_tsc) {723tsc = event.tsc;724break;725}726}727if (IsLibiptError(decoding_status)) {728// We continue to the next PSB. This effectively merges this PSB with the729// previous one, and that should be fine because this PSB might be the730// direct continuation of the previous thread and it's better to show an731// error in the decoded thread than to hide it. If this is the first PSB,732// we are okay losing it. Besides that, an error at processing events733// means that we wouldn't be able to get any instruction out of it.734continue;735}736737if (expect_tscs && !tsc)738return createStringError(inconvertibleErrorCode(),739"Found a PSB without TSC.");740741executions.push_back({742psb_offset,743tsc,7440,745ip,746});747}748if (!executions.empty()) {749// We now adjust the sizes of each block750executions.back().size = buffer.size() - executions.back().psb_offset;751for (int i = (int)executions.size() - 2; i >= 0; i--) {752executions[i].size =753executions[i + 1].psb_offset - executions[i].psb_offset;754}755}756return executions;757}758759Expected<std::optional<uint64_t>>760lldb_private::trace_intel_pt::FindLowestTSCInTrace(TraceIntelPT &trace_intel_pt,761ArrayRef<uint8_t> buffer) {762Expected<PtQueryDecoderUP> decoder_up =763CreateQueryDecoder(trace_intel_pt, buffer);764if (!decoder_up)765return decoder_up.takeError();766767pt_query_decoder *decoder = decoder_up.get().get();768uint64_t ip = LLDB_INVALID_ADDRESS;769int status = pt_qry_sync_forward(decoder, &ip);770if (IsLibiptError(status))771return std::nullopt;772773while (HasEvents(status)) {774pt_event event;775status = pt_qry_event(decoder, &event, sizeof(event));776if (IsLibiptError(status))777return std::nullopt;778if (event.has_tsc)779return event.tsc;780}781return std::nullopt;782}783784785