Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
39645 views
//===-- DecodedThread.h -----------------------------------------*- C++ -*-===//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#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H9#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H1011#include "intel-pt.h"12#include "lldb/Target/Trace.h"13#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"14#include "llvm/Support/Errc.h"15#include "llvm/Support/Error.h"16#include <deque>17#include <optional>18#include <utility>19#include <variant>2021namespace lldb_private {22namespace trace_intel_pt {2324/// Class for representing a libipt decoding error.25class IntelPTError : public llvm::ErrorInfo<IntelPTError> {26public:27static char ID;2829/// \param[in] libipt_error_code30/// Negative number returned by libipt when decoding the trace and31/// signaling errors.32///33/// \param[in] address34/// Optional instruction address. When decoding an individual instruction,35/// its address might be available in the \a pt_insn object, and should be36/// passed to this constructor. Other errors don't have an associated37/// address.38IntelPTError(int libipt_error_code,39lldb::addr_t address = LLDB_INVALID_ADDRESS);4041std::error_code convertToErrorCode() const override {42return llvm::errc::not_supported;43}4445int GetLibiptErrorCode() const { return m_libipt_error_code; }4647void log(llvm::raw_ostream &OS) const override;4849private:50int m_libipt_error_code;51lldb::addr_t m_address;52};5354/// \class DecodedThread55/// Class holding the instructions and function call hierarchy obtained from56/// decoding a trace, as well as a position cursor used when reverse debugging57/// the trace.58///59/// Each decoded thread contains a cursor to the current position the user is60/// stopped at. See \a Trace::GetCursorPosition for more information.61class DecodedThread : public std::enable_shared_from_this<DecodedThread> {62public:63using TSC = uint64_t;6465/// A structure that represents a maximal range of trace items associated to66/// the same TSC value.67struct TSCRange {68TSC tsc;69/// Number of trace items in this range.70uint64_t items_count;71/// Index of the first trace item in this range.72uint64_t first_item_index;7374/// \return75/// \b true if and only if the given \p item_index is covered by this76/// range.77bool InRange(uint64_t item_index) const;78};7980/// A structure that represents a maximal range of trace items associated to81/// the same non-interpolated timestamps in nanoseconds.82struct NanosecondsRange {83/// The nanoseconds value for this range.84uint64_t nanos;85/// The corresponding TSC value for this range.86TSC tsc;87/// A nullable pointer to the next range.88NanosecondsRange *next_range;89/// Number of trace items in this range.90uint64_t items_count;91/// Index of the first trace item in this range.92uint64_t first_item_index;9394/// Calculate an interpolated timestamp in nanoseconds for the given item95/// index. It's guaranteed that two different item indices will produce96/// different interpolated values.97///98/// \param[in] item_index99/// The index of the item whose timestamp will be estimated. It has to be100/// part of this range.101///102/// \param[in] beginning_of_time_nanos103/// The timestamp at which tracing started.104///105/// \param[in] tsc_conversion106/// The tsc -> nanos conversion utility107///108/// \return109/// An interpolated timestamp value for the given trace item.110double111GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,112const LinuxPerfZeroTscConversion &tsc_conversion) const;113114/// \return115/// \b true if and only if the given \p item_index is covered by this116/// range.117bool InRange(uint64_t item_index) const;118};119120// Struct holding counts for events121struct EventsStats {122/// A count for each individual event kind. We use an unordered map instead123/// of a DenseMap because DenseMap can't understand enums.124///125/// Note: We can't use DenseMap because lldb::TraceEvent is not126/// automatically handled correctly by DenseMap. We'd need to implement a127/// custom DenseMapInfo struct for TraceEvent and that's a bit too much for128/// such a simple structure.129std::unordered_map<lldb::TraceEvent, uint64_t> events_counts;130uint64_t total_count = 0;131132void RecordEvent(lldb::TraceEvent event);133};134135// Struct holding counts for errors136struct ErrorStats {137/// The following counters are mutually exclusive138/// \{139uint64_t other_errors = 0;140uint64_t fatal_errors = 0;141// libipt error -> count142llvm::DenseMap<const char *, uint64_t> libipt_errors;143/// \}144145uint64_t GetTotalCount() const;146147void RecordError(int libipt_error_code);148149void RecordError(bool fatal);150};151152DecodedThread(153lldb::ThreadSP thread_sp,154const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);155156/// Get the total number of instruction, errors and events from the decoded157/// trace.158uint64_t GetItemsCount() const;159160/// \return161/// The error associated with a given trace item.162llvm::StringRef GetErrorByIndex(uint64_t item_index) const;163164/// \return165/// The trace item kind given an item index.166lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;167168/// \return169/// The underlying event type for the given trace item index.170lldb::TraceEvent GetEventByIndex(int item_index) const;171172/// Get the most recent CPU id before or at the given trace item index.173///174/// \param[in] item_index175/// The trace item index to compare with.176///177/// \return178/// The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.179lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;180181/// \return182/// The PSB offset associated with the given item index.183lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;184185/// Get a maximal range of trace items that include the given \p item_index186/// that have the same TSC value.187///188/// \param[in] item_index189/// The trace item index to compare with.190///191/// \return192/// The requested TSC range, or \a std::nullopt if not available.193std::optional<DecodedThread::TSCRange>194GetTSCRangeByIndex(uint64_t item_index) const;195196/// Get a maximal range of trace items that include the given \p item_index197/// that have the same nanoseconds timestamp without interpolation.198///199/// \param[in] item_index200/// The trace item index to compare with.201///202/// \return203/// The requested nanoseconds range, or \a std::nullopt if not available.204std::optional<DecodedThread::NanosecondsRange>205GetNanosecondsRangeByIndex(uint64_t item_index);206207/// \return208/// The load address of the instruction at the given index.209lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;210211/// \return212/// The number of instructions in this trace (not trace items).213uint64_t GetTotalInstructionCount() const;214215/// Return an object with statistics of the trace events that happened.216///217/// \return218/// The stats object of all the events.219const EventsStats &GetEventsStats() const;220221/// Return an object with statistics of the trace errors that happened.222///223/// \return224/// The stats object of all the events.225const ErrorStats &GetErrorStats() const;226227/// The approximate size in bytes used by this instance,228/// including all the already decoded instructions.229size_t CalculateApproximateMemoryUsage() const;230231lldb::ThreadSP GetThread();232233/// Notify this object that a new tsc has been seen.234/// If this a new TSC, an event will be created.235void NotifyTsc(TSC tsc);236237/// Notify this object that a CPU has been seen.238/// If this a new CPU, an event will be created.239void NotifyCPU(lldb::cpu_id_t cpu_id);240241/// Notify this object that a new PSB has been seen.242void NotifySyncPoint(lldb::addr_t psb_offset);243244/// Append a decoding error.245void AppendError(const IntelPTError &error);246247/// Append a custom decoding.248///249/// \param[in] error250/// The error message.251///252/// \param[in] fatal253/// If \b true, then the whole decoded thread should be discarded because a254/// fatal anomaly has been found.255void AppendCustomError(llvm::StringRef error, bool fatal = false);256257/// Append an event.258void AppendEvent(lldb::TraceEvent);259260/// Append an instruction.261void AppendInstruction(const pt_insn &insn);262263private:264/// When adding new members to this class, make sure265/// to update \a CalculateApproximateMemoryUsage() accordingly.266lldb::ThreadSP m_thread_sp;267268using TraceItemStorage =269std::variant<std::string, lldb::TraceEvent, lldb::addr_t>;270271/// Create a new trace item.272///273/// \return274/// The index of the new item.275template <typename Data>276DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind,277Data &&data);278279/// Most of the trace data is stored here.280std::deque<TraceItemStorage> m_item_data;281282/// This map contains the TSCs of the decoded trace items. It maps283/// `item index -> TSC`, where `item index` is the first index284/// at which the mapped TSC first appears. We use this representation because285/// TSCs are sporadic and we can think of them as ranges.286std::map<uint64_t, TSCRange> m_tscs;287/// This is the chronologically last TSC that has been added.288std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =289std::nullopt;290/// This map contains the non-interpolated nanoseconds timestamps of the291/// decoded trace items. It maps `item index -> nanoseconds`, where `item292/// index` is the first index at which the mapped nanoseconds first appears.293/// We use this representation because timestamps are sporadic and we think of294/// them as ranges.295std::map<uint64_t, NanosecondsRange> m_nanoseconds;296std::optional<std::map<uint64_t, NanosecondsRange>::iterator>297m_last_nanoseconds = std::nullopt;298299// The cpu information is stored as a map. It maps `item index -> CPU`.300// A CPU is associated with the next instructions that follow until the next301// cpu is seen.302std::map<uint64_t, lldb::cpu_id_t> m_cpus;303/// This is the chronologically last CPU ID.304std::optional<uint64_t> m_last_cpu;305306// The PSB offsets are stored as a map. It maps `item index -> psb offset`.307llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets;308309/// TSC -> nanos conversion utility.310std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;311312/// Statistics of all tracing errors.313ErrorStats m_error_stats;314315/// Statistics of all tracing events.316EventsStats m_events_stats;317/// Total amount of time spent decoding.318std::chrono::milliseconds m_total_decoding_time{0};319320/// Total number of instructions in the trace.321uint64_t m_insn_count = 0;322};323324using DecodedThreadSP = std::shared_ptr<DecodedThread>;325326} // namespace trace_intel_pt327} // namespace lldb_private328329#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H330331332