diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h new file mode 100644 index 000000000000..a48c55cc76df --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h @@ -0,0 +1,330 @@ +//===-- DecodedThread.h -----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H +#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H + +#include "intel-pt.h" +#include "lldb/Target/Trace.h" +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include <deque> +#include <optional> +#include <utility> +#include <variant> + +namespace lldb_private { +namespace trace_intel_pt { + +/// Class for representing a libipt decoding error. +class IntelPTError : public llvm::ErrorInfo<IntelPTError> { +public: + static char ID; + + /// \param[in] libipt_error_code + /// Negative number returned by libipt when decoding the trace and + /// signaling errors. + /// + /// \param[in] address + /// Optional instruction address. When decoding an individual instruction, + /// its address might be available in the \a pt_insn object, and should be + /// passed to this constructor. Other errors don't have an associated + /// address. + IntelPTError(int libipt_error_code, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + std::error_code convertToErrorCode() const override { + return llvm::errc::not_supported; + } + + int GetLibiptErrorCode() const { return m_libipt_error_code; } + + void log(llvm::raw_ostream &OS) const override; + +private: + int m_libipt_error_code; + lldb::addr_t m_address; +}; + +/// \class DecodedThread +/// Class holding the instructions and function call hierarchy obtained from +/// decoding a trace, as well as a position cursor used when reverse debugging +/// the trace. +/// +/// Each decoded thread contains a cursor to the current position the user is +/// stopped at. See \a Trace::GetCursorPosition for more information. +class DecodedThread : public std::enable_shared_from_this<DecodedThread> { +public: + using TSC = uint64_t; + + /// A structure that represents a maximal range of trace items associated to + /// the same TSC value. + struct TSCRange { + TSC tsc; + /// Number of trace items in this range. + uint64_t items_count; + /// Index of the first trace item in this range. + uint64_t first_item_index; + + /// \return + /// \b true if and only if the given \p item_index is covered by this + /// range. + bool InRange(uint64_t item_index) const; + }; + + /// A structure that represents a maximal range of trace items associated to + /// the same non-interpolated timestamps in nanoseconds. + struct NanosecondsRange { + /// The nanoseconds value for this range. + uint64_t nanos; + /// The corresponding TSC value for this range. + TSC tsc; + /// A nullable pointer to the next range. + NanosecondsRange *next_range; + /// Number of trace items in this range. + uint64_t items_count; + /// Index of the first trace item in this range. + uint64_t first_item_index; + + /// Calculate an interpolated timestamp in nanoseconds for the given item + /// index. It's guaranteed that two different item indices will produce + /// different interpolated values. + /// + /// \param[in] item_index + /// The index of the item whose timestamp will be estimated. It has to be + /// part of this range. + /// + /// \param[in] beginning_of_time_nanos + /// The timestamp at which tracing started. + /// + /// \param[in] tsc_conversion + /// The tsc -> nanos conversion utility + /// + /// \return + /// An interpolated timestamp value for the given trace item. + double + GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos, + const LinuxPerfZeroTscConversion &tsc_conversion) const; + + /// \return + /// \b true if and only if the given \p item_index is covered by this + /// range. + bool InRange(uint64_t item_index) const; + }; + + // Struct holding counts for events + struct EventsStats { + /// A count for each individual event kind. We use an unordered map instead + /// of a DenseMap because DenseMap can't understand enums. + /// + /// Note: We can't use DenseMap because lldb::TraceEvent is not + /// automatically handled correctly by DenseMap. We'd need to implement a + /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for + /// such a simple structure. + std::unordered_map<lldb::TraceEvent, uint64_t> events_counts; + uint64_t total_count = 0; + + void RecordEvent(lldb::TraceEvent event); + }; + + // Struct holding counts for errors + struct ErrorStats { + /// The following counters are mutually exclusive + /// \{ + uint64_t other_errors = 0; + uint64_t fatal_errors = 0; + // libipt error -> count + llvm::DenseMap<const char *, uint64_t> libipt_errors; + /// \} + + uint64_t GetTotalCount() const; + + void RecordError(int libipt_error_code); + + void RecordError(bool fatal); + }; + + DecodedThread( + lldb::ThreadSP thread_sp, + const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion); + + /// Get the total number of instruction, errors and events from the decoded + /// trace. + uint64_t GetItemsCount() const; + + /// \return + /// The error associated with a given trace item. + llvm::StringRef GetErrorByIndex(uint64_t item_index) const; + + /// \return + /// The trace item kind given an item index. + lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const; + + /// \return + /// The underlying event type for the given trace item index. + lldb::TraceEvent GetEventByIndex(int item_index) const; + + /// Get the most recent CPU id before or at the given trace item index. + /// + /// \param[in] item_index + /// The trace item index to compare with. + /// + /// \return + /// The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available. + lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const; + + /// \return + /// The PSB offset associated with the given item index. + lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const; + + /// Get a maximal range of trace items that include the given \p item_index + /// that have the same TSC value. + /// + /// \param[in] item_index + /// The trace item index to compare with. + /// + /// \return + /// The requested TSC range, or \a std::nullopt if not available. + std::optional<DecodedThread::TSCRange> + GetTSCRangeByIndex(uint64_t item_index) const; + + /// Get a maximal range of trace items that include the given \p item_index + /// that have the same nanoseconds timestamp without interpolation. + /// + /// \param[in] item_index + /// The trace item index to compare with. + /// + /// \return + /// The requested nanoseconds range, or \a std::nullopt if not available. + std::optional<DecodedThread::NanosecondsRange> + GetNanosecondsRangeByIndex(uint64_t item_index); + + /// \return + /// The load address of the instruction at the given index. + lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const; + + /// \return + /// The number of instructions in this trace (not trace items). + uint64_t GetTotalInstructionCount() const; + + /// Return an object with statistics of the trace events that happened. + /// + /// \return + /// The stats object of all the events. + const EventsStats &GetEventsStats() const; + + /// Return an object with statistics of the trace errors that happened. + /// + /// \return + /// The stats object of all the events. + const ErrorStats &GetErrorStats() const; + + /// The approximate size in bytes used by this instance, + /// including all the already decoded instructions. + size_t CalculateApproximateMemoryUsage() const; + + lldb::ThreadSP GetThread(); + + /// Notify this object that a new tsc has been seen. + /// If this a new TSC, an event will be created. + void NotifyTsc(TSC tsc); + + /// Notify this object that a CPU has been seen. + /// If this a new CPU, an event will be created. + void NotifyCPU(lldb::cpu_id_t cpu_id); + + /// Notify this object that a new PSB has been seen. + void NotifySyncPoint(lldb::addr_t psb_offset); + + /// Append a decoding error. + void AppendError(const IntelPTError &error); + + /// Append a custom decoding. + /// + /// \param[in] error + /// The error message. + /// + /// \param[in] fatal + /// If \b true, then the whole decoded thread should be discarded because a + /// fatal anomaly has been found. + void AppendCustomError(llvm::StringRef error, bool fatal = false); + + /// Append an event. + void AppendEvent(lldb::TraceEvent); + + /// Append an instruction. + void AppendInstruction(const pt_insn &insn); + +private: + /// When adding new members to this class, make sure + /// to update \a CalculateApproximateMemoryUsage() accordingly. + lldb::ThreadSP m_thread_sp; + + using TraceItemStorage = + std::variant<std::string, lldb::TraceEvent, lldb::addr_t>; + + /// Create a new trace item. + /// + /// \return + /// The index of the new item. + template <typename Data> + DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind, + Data &&data); + + /// Most of the trace data is stored here. + std::deque<TraceItemStorage> m_item_data; + + /// This map contains the TSCs of the decoded trace items. It maps + /// `item index -> TSC`, where `item index` is the first index + /// at which the mapped TSC first appears. We use this representation because + /// TSCs are sporadic and we can think of them as ranges. + std::map<uint64_t, TSCRange> m_tscs; + /// This is the chronologically last TSC that has been added. + std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc = + std::nullopt; + /// This map contains the non-interpolated nanoseconds timestamps of the + /// decoded trace items. It maps `item index -> nanoseconds`, where `item + /// index` is the first index at which the mapped nanoseconds first appears. + /// We use this representation because timestamps are sporadic and we think of + /// them as ranges. + std::map<uint64_t, NanosecondsRange> m_nanoseconds; + std::optional<std::map<uint64_t, NanosecondsRange>::iterator> + m_last_nanoseconds = std::nullopt; + + // The cpu information is stored as a map. It maps `item index -> CPU`. + // A CPU is associated with the next instructions that follow until the next + // cpu is seen. + std::map<uint64_t, lldb::cpu_id_t> m_cpus; + /// This is the chronologically last CPU ID. + std::optional<uint64_t> m_last_cpu; + + // The PSB offsets are stored as a map. It maps `item index -> psb offset`. + llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets; + + /// TSC -> nanos conversion utility. + std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion; + + /// Statistics of all tracing errors. + ErrorStats m_error_stats; + + /// Statistics of all tracing events. + EventsStats m_events_stats; + /// Total amount of time spent decoding. + std::chrono::milliseconds m_total_decoding_time{0}; + + /// Total number of instructions in the trace. + uint64_t m_insn_count = 0; +}; + +using DecodedThreadSP = std::shared_ptr<DecodedThread>; + +} // namespace trace_intel_pt +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H |