239 lines · cpp
1//===-- TraceIntelPTMultiCpuDecoder.cpp -----------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "TraceIntelPTMultiCpuDecoder.h"10#include "TraceIntelPT.h"11#include "llvm/Support/Error.h"12#include <optional>13 14using namespace lldb;15using namespace lldb_private;16using namespace lldb_private::trace_intel_pt;17using namespace llvm;18 19TraceIntelPTMultiCpuDecoder::TraceIntelPTMultiCpuDecoder(20 TraceIntelPTSP trace_sp)21 : m_trace_wp(trace_sp) {22 for (Process *proc : trace_sp->GetAllProcesses()) {23 for (ThreadSP thread_sp : proc->GetThreadList().Threads()) {24 m_tids.insert(thread_sp->GetID());25 }26 }27}28 29TraceIntelPTSP TraceIntelPTMultiCpuDecoder::GetTrace() {30 return m_trace_wp.lock();31}32 33bool TraceIntelPTMultiCpuDecoder::TracesThread(lldb::tid_t tid) const {34 return m_tids.count(tid);35}36 37Expected<std::optional<uint64_t>> TraceIntelPTMultiCpuDecoder::FindLowestTSC() {38 std::optional<uint64_t> lowest_tsc;39 TraceIntelPTSP trace_sp = GetTrace();40 41 Error err = GetTrace()->OnAllCpusBinaryDataRead(42 IntelPTDataKinds::kIptTrace,43 [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {44 for (auto &cpu_id_to_buffer : buffers) {45 Expected<std::optional<uint64_t>> tsc =46 FindLowestTSCInTrace(*trace_sp, cpu_id_to_buffer.second);47 if (!tsc)48 return tsc.takeError();49 if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))50 lowest_tsc = **tsc;51 }52 return Error::success();53 });54 if (err)55 return std::move(err);56 return lowest_tsc;57}58 59Expected<DecodedThreadSP> TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) {60 if (Error err = CorrelateContextSwitchesAndIntelPtTraces())61 return std::move(err);62 63 TraceIntelPTSP trace_sp = GetTrace();64 65 return trace_sp->GetThreadTimer(thread.GetID())66 .TimeTask("Decoding instructions", [&]() -> Expected<DecodedThreadSP> {67 auto it = m_decoded_threads.find(thread.GetID());68 if (it != m_decoded_threads.end())69 return it->second;70 71 DecodedThreadSP decoded_thread_sp = std::make_shared<DecodedThread>(72 thread.shared_from_this(), trace_sp->GetPerfZeroTscConversion());73 74 Error err = trace_sp->OnAllCpusBinaryDataRead(75 IntelPTDataKinds::kIptTrace,76 [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {77 auto it =78 m_continuous_executions_per_thread->find(thread.GetID());79 if (it != m_continuous_executions_per_thread->end())80 return DecodeSystemWideTraceForThread(81 *decoded_thread_sp, *trace_sp, buffers, it->second);82 83 return Error::success();84 });85 if (err)86 return std::move(err);87 88 m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp);89 return decoded_thread_sp;90 });91}92 93static Expected<std::vector<PSBBlock>> GetPSBBlocksForCPU(TraceIntelPT &trace,94 cpu_id_t cpu_id) {95 std::vector<PSBBlock> psb_blocks;96 Error err = trace.OnCpuBinaryDataRead(97 cpu_id, IntelPTDataKinds::kIptTrace,98 [&](ArrayRef<uint8_t> data) -> Error {99 Expected<std::vector<PSBBlock>> split_trace =100 SplitTraceIntoPSBBlock(trace, data, /*expect_tscs=*/true);101 if (!split_trace)102 return split_trace.takeError();103 104 psb_blocks = std::move(*split_trace);105 return Error::success();106 });107 if (err)108 return std::move(err);109 return psb_blocks;110}111 112Expected<DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>113TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() {114 DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>115 continuous_executions_per_thread;116 TraceIntelPTSP trace_sp = GetTrace();117 118 std::optional<LinuxPerfZeroTscConversion> conv_opt =119 trace_sp->GetPerfZeroTscConversion();120 if (!conv_opt)121 return createStringError(122 inconvertibleErrorCode(),123 "TSC to nanoseconds conversion values were not found");124 125 LinuxPerfZeroTscConversion tsc_conversion = *conv_opt;126 127 for (cpu_id_t cpu_id : trace_sp->GetTracedCpus()) {128 Expected<std::vector<PSBBlock>> psb_blocks =129 GetPSBBlocksForCPU(*trace_sp, cpu_id);130 if (!psb_blocks)131 return psb_blocks.takeError();132 133 m_total_psb_blocks += psb_blocks->size();134 // We'll be iterating through the thread continuous executions and the intel135 // pt subtraces sorted by time.136 auto it = psb_blocks->begin();137 auto on_new_thread_execution =138 [&](const ThreadContinuousExecution &thread_execution) {139 IntelPTThreadContinousExecution execution(thread_execution);140 141 for (; it != psb_blocks->end() &&142 *it->tsc < thread_execution.GetEndTSC();143 it++) {144 if (*it->tsc > thread_execution.GetStartTSC()) {145 execution.psb_blocks.push_back(*it);146 } else {147 m_unattributed_psb_blocks++;148 }149 }150 continuous_executions_per_thread[thread_execution.tid].push_back(151 execution);152 };153 Error err = trace_sp->OnCpuBinaryDataRead(154 cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,155 [&](ArrayRef<uint8_t> data) -> Error {156 Expected<std::vector<ThreadContinuousExecution>> executions =157 DecodePerfContextSwitchTrace(data, cpu_id, tsc_conversion);158 if (!executions)159 return executions.takeError();160 for (const ThreadContinuousExecution &exec : *executions)161 on_new_thread_execution(exec);162 return Error::success();163 });164 if (err)165 return std::move(err);166 167 m_unattributed_psb_blocks += psb_blocks->end() - it;168 }169 // We now sort the executions of each thread to have them ready for170 // instruction decoding171 for (auto &tid_executions : continuous_executions_per_thread)172 std::sort(tid_executions.second.begin(), tid_executions.second.end());173 174 return continuous_executions_per_thread;175}176 177Error TraceIntelPTMultiCpuDecoder::CorrelateContextSwitchesAndIntelPtTraces() {178 if (m_setup_error)179 return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());180 181 if (m_continuous_executions_per_thread)182 return Error::success();183 184 Error err = GetTrace()->GetGlobalTimer().TimeTask(185 "Context switch and Intel PT traces correlation", [&]() -> Error {186 if (auto correlation = DoCorrelateContextSwitchesAndIntelPtTraces()) {187 m_continuous_executions_per_thread.emplace(std::move(*correlation));188 return Error::success();189 } else {190 return correlation.takeError();191 }192 });193 if (err) {194 m_setup_error = toString(std::move(err));195 return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());196 }197 return Error::success();198}199 200size_t TraceIntelPTMultiCpuDecoder::GetNumContinuousExecutionsForThread(201 lldb::tid_t tid) const {202 if (!m_continuous_executions_per_thread)203 return 0;204 auto it = m_continuous_executions_per_thread->find(tid);205 if (it == m_continuous_executions_per_thread->end())206 return 0;207 return it->second.size();208}209 210size_t TraceIntelPTMultiCpuDecoder::GetTotalContinuousExecutionsCount() const {211 if (!m_continuous_executions_per_thread)212 return 0;213 size_t count = 0;214 for (const auto &kv : *m_continuous_executions_per_thread)215 count += kv.second.size();216 return count;217}218 219size_t220TraceIntelPTMultiCpuDecoder::GePSBBlocksCountForThread(lldb::tid_t tid) const {221 if (!m_continuous_executions_per_thread)222 return 0;223 size_t count = 0;224 auto it = m_continuous_executions_per_thread->find(tid);225 if (it == m_continuous_executions_per_thread->end())226 return 0;227 for (const IntelPTThreadContinousExecution &execution : it->second)228 count += execution.psb_blocks.size();229 return count;230}231 232size_t TraceIntelPTMultiCpuDecoder::GetUnattributedPSBBlocksCount() const {233 return m_unattributed_psb_blocks;234}235 236size_t TraceIntelPTMultiCpuDecoder::GetTotalPSBBlocksCount() const {237 return m_total_psb_blocks;238}239