Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
39645 views
//===-- DecodedThread.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "DecodedThread.h"9#include "TraceCursorIntelPT.h"10#include <intel-pt.h>11#include <memory>12#include <optional>1314using namespace lldb;15using namespace lldb_private;16using namespace lldb_private::trace_intel_pt;17using namespace llvm;1819char IntelPTError::ID;2021IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)22: m_libipt_error_code(libipt_error_code), m_address(address) {23assert(libipt_error_code < 0);24}2526void IntelPTError::log(llvm::raw_ostream &OS) const {27OS << pt_errstr(pt_errcode(m_libipt_error_code));28if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)29OS << formatv(": {0:x+16}", m_address);30}3132bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {33return item_index >= first_item_index &&34item_index < first_item_index + items_count;35}3637bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {38return item_index >= first_item_index &&39item_index < first_item_index + items_count;40}4142double DecodedThread::NanosecondsRange::GetInterpolatedTime(43uint64_t item_index, uint64_t begin_of_time_nanos,44const LinuxPerfZeroTscConversion &tsc_conversion) const {45uint64_t items_since_last_tsc = item_index - first_item_index;4647auto interpolate = [&](uint64_t next_range_start_ns) {48if (next_range_start_ns == nanos) {49// If the resolution of the conversion formula is bad enough to consider50// these two timestamps as equal, then we just increase the next one by 151// for correction52next_range_start_ns++;53}54long double item_duration =55static_cast<long double>(items_count) / (next_range_start_ns - nanos);56return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;57};5859if (!next_range) {60// If this is the last TSC range, so we have to extrapolate. In this case,61// we assume that each instruction took one TSC, which is what an62// instruction would take if no parallelism is achieved and the frequency63// multiplier is 1.64return interpolate(tsc_conversion.ToNanos(tsc + items_count));65}66if (items_count < (next_range->tsc - tsc)) {67// If the numbers of items in this range is less than the total TSC duration68// of this range, i.e. each instruction taking longer than 1 TSC, then we69// can assume that something else happened between these TSCs (e.g. a70// context switch, change to kernel, decoding errors, etc). In this case, we71// also assume that each instruction took 1 TSC. A proper way to improve72// this would be to analize the next events in the trace looking for context73// switches or trace disablement events, but for now, as we only want an74// approximation, we keep it simple. We are also guaranteed that the time in75// nanos of the next range is different to the current one, just because of76// the definition of a NanosecondsRange.77return interpolate(78std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));79}8081// In this case, each item took less than 1 TSC, so some parallelism was82// achieved, which is an indication that we didn't suffered of any kind of83// interruption.84return interpolate(next_range->nanos);85}8687uint64_t DecodedThread::GetItemsCount() const { return m_item_data.size(); }8889lldb::addr_t90DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {91return std::get<lldb::addr_t>(m_item_data[item_index]);92}9394lldb::addr_t95DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {96return m_psb_offsets.find(item_index)->second;97}9899ThreadSP DecodedThread::GetThread() { return m_thread_sp; }100101template <typename Data>102DecodedThread::TraceItemStorage &103DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind, Data &&data) {104m_item_data.emplace_back(data);105106if (m_last_tsc)107(*m_last_tsc)->second.items_count++;108if (m_last_nanoseconds)109(*m_last_nanoseconds)->second.items_count++;110111return m_item_data.back();112}113114void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) {115m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);116AppendEvent(lldb::eTraceEventSyncPoint);117}118119void DecodedThread::NotifyTsc(TSC tsc) {120if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)121return;122if (m_last_tsc)123assert(tsc >= (*m_last_tsc)->second.tsc &&124"We can't have decreasing times");125126m_last_tsc =127m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;128129if (m_tsc_conversion) {130uint64_t nanos = m_tsc_conversion->ToNanos(tsc);131if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {132m_last_nanoseconds =133m_nanoseconds134.emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,135GetItemsCount()})136.first;137if (*m_last_nanoseconds != m_nanoseconds.begin()) {138auto prev_range = prev(*m_last_nanoseconds);139prev_range->second.next_range = &(*m_last_nanoseconds)->second;140}141}142}143AppendEvent(lldb::eTraceEventHWClockTick);144}145146void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {147if (!m_last_cpu || *m_last_cpu != cpu_id) {148m_cpus.emplace(GetItemsCount(), cpu_id);149m_last_cpu = cpu_id;150AppendEvent(lldb::eTraceEventCPUChanged);151}152}153154lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {155auto it = m_cpus.upper_bound(item_index);156return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;157}158159std::optional<DecodedThread::TSCRange>160DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {161auto next_it = m_tscs.upper_bound(item_index);162if (next_it == m_tscs.begin())163return std::nullopt;164return prev(next_it)->second;165}166167std::optional<DecodedThread::NanosecondsRange>168DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) {169auto next_it = m_nanoseconds.upper_bound(item_index);170if (next_it == m_nanoseconds.begin())171return std::nullopt;172return prev(next_it)->second;173}174175uint64_t DecodedThread::GetTotalInstructionCount() const {176return m_insn_count;177}178179void DecodedThread::AppendEvent(lldb::TraceEvent event) {180CreateNewTraceItem(lldb::eTraceItemKindEvent, event);181m_events_stats.RecordEvent(event);182}183184void DecodedThread::AppendInstruction(const pt_insn &insn) {185CreateNewTraceItem(lldb::eTraceItemKindInstruction, insn.ip);186m_insn_count++;187}188189void DecodedThread::AppendError(const IntelPTError &error) {190CreateNewTraceItem(lldb::eTraceItemKindError, error.message());191m_error_stats.RecordError(/*fatal=*/false);192}193194void DecodedThread::AppendCustomError(StringRef err, bool fatal) {195CreateNewTraceItem(lldb::eTraceItemKindError, err.str());196m_error_stats.RecordError(fatal);197}198199lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {200return std::get<lldb::TraceEvent>(m_item_data[item_index]);201}202203const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {204return m_events_stats;205}206207void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {208events_counts[event]++;209total_count++;210}211212uint64_t DecodedThread::ErrorStats::GetTotalCount() const {213uint64_t total = 0;214for (const auto &[kind, count] : libipt_errors)215total += count;216217return total + other_errors + fatal_errors;218}219220void DecodedThread::ErrorStats::RecordError(bool fatal) {221if (fatal)222fatal_errors++;223else224other_errors++;225}226227void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {228libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;229}230231const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const {232return m_error_stats;233}234235lldb::TraceItemKind236DecodedThread::GetItemKindByIndex(uint64_t item_index) const {237return std::visit(238llvm::makeVisitor(239[](const std::string &) { return lldb::eTraceItemKindError; },240[](lldb::TraceEvent) { return lldb::eTraceItemKindEvent; },241[](lldb::addr_t) { return lldb::eTraceItemKindInstruction; }),242m_item_data[item_index]);243}244245llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const {246if (item_index >= m_item_data.size())247return llvm::StringRef();248return std::get<std::string>(m_item_data[item_index]);249}250251DecodedThread::DecodedThread(252ThreadSP thread_sp,253const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)254: m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}255256size_t DecodedThread::CalculateApproximateMemoryUsage() const {257return sizeof(TraceItemStorage) * m_item_data.size() +258(sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +259(sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +260(sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();261}262263264