1e8d8bef9SDimitry Andric //===-- DecodedThread.h -----------------------------------------*- C++ -*-===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric 9e8d8bef9SDimitry Andric #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 10e8d8bef9SDimitry Andric #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 11e8d8bef9SDimitry Andric 12*bdd1243dSDimitry Andric #include "intel-pt.h" 13*bdd1243dSDimitry Andric #include "lldb/Target/Trace.h" 14*bdd1243dSDimitry Andric #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" 15*bdd1243dSDimitry Andric #include "llvm/Support/Errc.h" 16*bdd1243dSDimitry Andric #include "llvm/Support/Error.h" 17*bdd1243dSDimitry Andric #include <optional> 1881ad6265SDimitry Andric #include <utility> 19e8d8bef9SDimitry Andric #include <vector> 20e8d8bef9SDimitry Andric 21e8d8bef9SDimitry Andric namespace lldb_private { 22e8d8bef9SDimitry Andric namespace trace_intel_pt { 23e8d8bef9SDimitry Andric 24e8d8bef9SDimitry Andric /// Class for representing a libipt decoding error. 25e8d8bef9SDimitry Andric class IntelPTError : public llvm::ErrorInfo<IntelPTError> { 26e8d8bef9SDimitry Andric public: 27e8d8bef9SDimitry Andric static char ID; 28e8d8bef9SDimitry Andric 29e8d8bef9SDimitry Andric /// \param[in] libipt_error_code 30e8d8bef9SDimitry Andric /// Negative number returned by libipt when decoding the trace and 31e8d8bef9SDimitry Andric /// signaling errors. 32e8d8bef9SDimitry Andric /// 33e8d8bef9SDimitry Andric /// \param[in] address 34e8d8bef9SDimitry Andric /// Optional instruction address. When decoding an individual instruction, 35e8d8bef9SDimitry Andric /// its address might be available in the \a pt_insn object, and should be 36e8d8bef9SDimitry Andric /// passed to this constructor. Other errors don't have an associated 37e8d8bef9SDimitry Andric /// address. 38e8d8bef9SDimitry Andric IntelPTError(int libipt_error_code, 39e8d8bef9SDimitry Andric lldb::addr_t address = LLDB_INVALID_ADDRESS); 40e8d8bef9SDimitry Andric 41e8d8bef9SDimitry Andric std::error_code convertToErrorCode() const override { 42e8d8bef9SDimitry Andric return llvm::errc::not_supported; 43e8d8bef9SDimitry Andric } 44e8d8bef9SDimitry Andric 4581ad6265SDimitry Andric int GetLibiptErrorCode() const { return m_libipt_error_code; } 4681ad6265SDimitry Andric 47e8d8bef9SDimitry Andric void log(llvm::raw_ostream &OS) const override; 48e8d8bef9SDimitry Andric 49e8d8bef9SDimitry Andric private: 50e8d8bef9SDimitry Andric int m_libipt_error_code; 51e8d8bef9SDimitry Andric lldb::addr_t m_address; 52e8d8bef9SDimitry Andric }; 53e8d8bef9SDimitry Andric 54e8d8bef9SDimitry Andric /// \class DecodedThread 55e8d8bef9SDimitry Andric /// Class holding the instructions and function call hierarchy obtained from 56e8d8bef9SDimitry Andric /// decoding a trace, as well as a position cursor used when reverse debugging 57e8d8bef9SDimitry Andric /// the trace. 58e8d8bef9SDimitry Andric /// 59e8d8bef9SDimitry Andric /// Each decoded thread contains a cursor to the current position the user is 60e8d8bef9SDimitry Andric /// stopped at. See \a Trace::GetCursorPosition for more information. 61fe6060f1SDimitry Andric class DecodedThread : public std::enable_shared_from_this<DecodedThread> { 62e8d8bef9SDimitry Andric public: 63972a253aSDimitry Andric using TSC = uint64_t; 6481ad6265SDimitry Andric 65972a253aSDimitry Andric /// A structure that represents a maximal range of trace items associated to 66972a253aSDimitry Andric /// the same TSC value. 67972a253aSDimitry Andric struct TSCRange { 68972a253aSDimitry Andric TSC tsc; 69972a253aSDimitry Andric /// Number of trace items in this range. 70972a253aSDimitry Andric uint64_t items_count; 71972a253aSDimitry Andric /// Index of the first trace item in this range. 72972a253aSDimitry Andric uint64_t first_item_index; 73972a253aSDimitry Andric 74972a253aSDimitry Andric /// \return 75972a253aSDimitry Andric /// \b true if and only if the given \p item_index is covered by this 76972a253aSDimitry Andric /// range. 77972a253aSDimitry Andric bool InRange(uint64_t item_index) const; 78972a253aSDimitry Andric }; 79972a253aSDimitry Andric 80972a253aSDimitry Andric /// A structure that represents a maximal range of trace items associated to 81972a253aSDimitry Andric /// the same non-interpolated timestamps in nanoseconds. 82972a253aSDimitry Andric struct NanosecondsRange { 83972a253aSDimitry Andric /// The nanoseconds value for this range. 84972a253aSDimitry Andric uint64_t nanos; 85972a253aSDimitry Andric /// The corresponding TSC value for this range. 86972a253aSDimitry Andric TSC tsc; 87972a253aSDimitry Andric /// A nullable pointer to the next range. 88972a253aSDimitry Andric NanosecondsRange *next_range; 89972a253aSDimitry Andric /// Number of trace items in this range. 90972a253aSDimitry Andric uint64_t items_count; 91972a253aSDimitry Andric /// Index of the first trace item in this range. 92972a253aSDimitry Andric uint64_t first_item_index; 93972a253aSDimitry Andric 94972a253aSDimitry Andric /// Calculate an interpolated timestamp in nanoseconds for the given item 95972a253aSDimitry Andric /// index. It's guaranteed that two different item indices will produce 96972a253aSDimitry Andric /// different interpolated values. 97972a253aSDimitry Andric /// 98972a253aSDimitry Andric /// \param[in] item_index 99972a253aSDimitry Andric /// The index of the item whose timestamp will be estimated. It has to be 100972a253aSDimitry Andric /// part of this range. 101972a253aSDimitry Andric /// 102972a253aSDimitry Andric /// \param[in] beginning_of_time_nanos 103972a253aSDimitry Andric /// The timestamp at which tracing started. 104972a253aSDimitry Andric /// 105972a253aSDimitry Andric /// \param[in] tsc_conversion 106972a253aSDimitry Andric /// The tsc -> nanos conversion utility 107972a253aSDimitry Andric /// 108972a253aSDimitry Andric /// \return 109972a253aSDimitry Andric /// An interpolated timestamp value for the given trace item. 110972a253aSDimitry Andric double 111972a253aSDimitry Andric GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos, 112972a253aSDimitry Andric const LinuxPerfZeroTscConversion &tsc_conversion) const; 113972a253aSDimitry Andric 114972a253aSDimitry Andric /// \return 115972a253aSDimitry Andric /// \b true if and only if the given \p item_index is covered by this 116972a253aSDimitry Andric /// range. 117972a253aSDimitry Andric bool InRange(uint64_t item_index) const; 118972a253aSDimitry Andric }; 119972a253aSDimitry Andric 120*bdd1243dSDimitry Andric // Struct holding counts for events 12181ad6265SDimitry Andric struct EventsStats { 12281ad6265SDimitry Andric /// A count for each individual event kind. We use an unordered map instead 12381ad6265SDimitry Andric /// of a DenseMap because DenseMap can't understand enums. 124*bdd1243dSDimitry Andric /// 125*bdd1243dSDimitry Andric /// Note: We can't use DenseMap because lldb::TraceEvent is not 126*bdd1243dSDimitry Andric /// automatically handled correctly by DenseMap. We'd need to implement a 127*bdd1243dSDimitry Andric /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for 128*bdd1243dSDimitry Andric /// such a simple structure. 129*bdd1243dSDimitry Andric std::unordered_map<lldb::TraceEvent, uint64_t> events_counts; 130*bdd1243dSDimitry Andric uint64_t total_count = 0; 13181ad6265SDimitry Andric 13281ad6265SDimitry Andric void RecordEvent(lldb::TraceEvent event); 13381ad6265SDimitry Andric }; 13481ad6265SDimitry Andric 135*bdd1243dSDimitry Andric // Struct holding counts for errors 136*bdd1243dSDimitry Andric struct ErrorStats { 137*bdd1243dSDimitry Andric /// The following counters are mutually exclusive 138*bdd1243dSDimitry Andric /// \{ 139*bdd1243dSDimitry Andric uint64_t other_errors = 0; 140*bdd1243dSDimitry Andric uint64_t fatal_errors = 0; 141*bdd1243dSDimitry Andric // libipt error -> count 142*bdd1243dSDimitry Andric llvm::DenseMap<const char *, uint64_t> libipt_errors; 143*bdd1243dSDimitry Andric /// \} 144*bdd1243dSDimitry Andric 145*bdd1243dSDimitry Andric uint64_t GetTotalCount() const; 146*bdd1243dSDimitry Andric 147*bdd1243dSDimitry Andric void RecordError(int libipt_error_code); 148*bdd1243dSDimitry Andric 149*bdd1243dSDimitry Andric void RecordError(bool fatal); 150*bdd1243dSDimitry Andric }; 151*bdd1243dSDimitry Andric 152972a253aSDimitry Andric DecodedThread( 153972a253aSDimitry Andric lldb::ThreadSP thread_sp, 154*bdd1243dSDimitry Andric const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion); 15581ad6265SDimitry Andric 15681ad6265SDimitry Andric /// Get the total number of instruction, errors and events from the decoded 15781ad6265SDimitry Andric /// trace. 158972a253aSDimitry Andric uint64_t GetItemsCount() const; 15981ad6265SDimitry Andric 16081ad6265SDimitry Andric /// \return 16181ad6265SDimitry Andric /// The error associated with a given trace item. 162972a253aSDimitry Andric const char *GetErrorByIndex(uint64_t item_index) const; 16381ad6265SDimitry Andric 16481ad6265SDimitry Andric /// \return 16581ad6265SDimitry Andric /// The trace item kind given an item index. 166972a253aSDimitry Andric lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const; 16781ad6265SDimitry Andric 16881ad6265SDimitry Andric /// \return 16981ad6265SDimitry Andric /// The underlying event type for the given trace item index. 17081ad6265SDimitry Andric lldb::TraceEvent GetEventByIndex(int item_index) const; 17181ad6265SDimitry Andric 172753f127fSDimitry Andric /// Get the most recent CPU id before or at the given trace item index. 173753f127fSDimitry Andric /// 174753f127fSDimitry Andric /// \param[in] item_index 175753f127fSDimitry Andric /// The trace item index to compare with. 176753f127fSDimitry Andric /// 177753f127fSDimitry Andric /// \return 178*bdd1243dSDimitry Andric /// The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available. 179*bdd1243dSDimitry Andric lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const; 180*bdd1243dSDimitry Andric 181*bdd1243dSDimitry Andric /// \return 182*bdd1243dSDimitry Andric /// The PSB offset associated with the given item index. 183*bdd1243dSDimitry Andric lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const; 184753f127fSDimitry Andric 185972a253aSDimitry Andric /// Get a maximal range of trace items that include the given \p item_index 186972a253aSDimitry Andric /// that have the same TSC value. 187972a253aSDimitry Andric /// 188972a253aSDimitry Andric /// \param[in] item_index 189972a253aSDimitry Andric /// The trace item index to compare with. 190972a253aSDimitry Andric /// 191972a253aSDimitry Andric /// \return 192*bdd1243dSDimitry Andric /// The requested TSC range, or \a std::nullopt if not available. 193*bdd1243dSDimitry Andric std::optional<DecodedThread::TSCRange> 194972a253aSDimitry Andric GetTSCRangeByIndex(uint64_t item_index) const; 195972a253aSDimitry Andric 196972a253aSDimitry Andric /// Get a maximal range of trace items that include the given \p item_index 197972a253aSDimitry Andric /// that have the same nanoseconds timestamp without interpolation. 198972a253aSDimitry Andric /// 199972a253aSDimitry Andric /// \param[in] item_index 200972a253aSDimitry Andric /// The trace item index to compare with. 201972a253aSDimitry Andric /// 202972a253aSDimitry Andric /// \return 203*bdd1243dSDimitry Andric /// The requested nanoseconds range, or \a std::nullopt if not available. 204*bdd1243dSDimitry Andric std::optional<DecodedThread::NanosecondsRange> 205972a253aSDimitry Andric GetNanosecondsRangeByIndex(uint64_t item_index); 206972a253aSDimitry Andric 20781ad6265SDimitry Andric /// \return 20881ad6265SDimitry Andric /// The load address of the instruction at the given index. 209972a253aSDimitry Andric lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const; 21081ad6265SDimitry Andric 21181ad6265SDimitry Andric /// \return 212*bdd1243dSDimitry Andric /// The number of instructions in this trace (not trace items). 213*bdd1243dSDimitry Andric uint64_t GetTotalInstructionCount() const; 21481ad6265SDimitry Andric 21581ad6265SDimitry Andric /// Return an object with statistics of the trace events that happened. 21681ad6265SDimitry Andric /// 21781ad6265SDimitry Andric /// \return 21881ad6265SDimitry Andric /// The stats object of all the events. 21981ad6265SDimitry Andric const EventsStats &GetEventsStats() const; 22081ad6265SDimitry Andric 221*bdd1243dSDimitry Andric /// Return an object with statistics of the trace errors that happened. 22281ad6265SDimitry Andric /// 223*bdd1243dSDimitry Andric /// \return 224*bdd1243dSDimitry Andric /// The stats object of all the events. 225*bdd1243dSDimitry Andric const ErrorStats &GetErrorStats() const; 22681ad6265SDimitry Andric 22781ad6265SDimitry Andric /// The approximate size in bytes used by this instance, 22881ad6265SDimitry Andric /// including all the already decoded instructions. 22981ad6265SDimitry Andric size_t CalculateApproximateMemoryUsage() const; 23081ad6265SDimitry Andric 23181ad6265SDimitry Andric lldb::ThreadSP GetThread(); 23281ad6265SDimitry Andric 23381ad6265SDimitry Andric /// Notify this object that a new tsc has been seen. 234753f127fSDimitry Andric /// If this a new TSC, an event will be created. 235972a253aSDimitry Andric void NotifyTsc(TSC tsc); 23681ad6265SDimitry Andric 237753f127fSDimitry Andric /// Notify this object that a CPU has been seen. 238753f127fSDimitry Andric /// If this a new CPU, an event will be created. 239753f127fSDimitry Andric void NotifyCPU(lldb::cpu_id_t cpu_id); 240753f127fSDimitry Andric 241*bdd1243dSDimitry Andric /// Notify this object that a new PSB has been seen. 242*bdd1243dSDimitry Andric void NotifySyncPoint(lldb::addr_t psb_offset); 243*bdd1243dSDimitry Andric 24481ad6265SDimitry Andric /// Append a decoding error. 24581ad6265SDimitry Andric void AppendError(const IntelPTError &error); 24681ad6265SDimitry Andric 24781ad6265SDimitry Andric /// Append a custom decoding. 248*bdd1243dSDimitry Andric /// 249*bdd1243dSDimitry Andric /// \param[in] error 250*bdd1243dSDimitry Andric /// The error message. 251*bdd1243dSDimitry Andric /// 252*bdd1243dSDimitry Andric /// \param[in] fatal 253*bdd1243dSDimitry Andric /// If \b true, then the whole decoded thread should be discarded because a 254*bdd1243dSDimitry Andric /// fatal anomaly has been found. 255*bdd1243dSDimitry Andric void AppendCustomError(llvm::StringRef error, bool fatal = false); 25681ad6265SDimitry Andric 25781ad6265SDimitry Andric /// Append an event. 25881ad6265SDimitry Andric void AppendEvent(lldb::TraceEvent); 25981ad6265SDimitry Andric 26081ad6265SDimitry Andric /// Append an instruction. 26181ad6265SDimitry Andric void AppendInstruction(const pt_insn &insn); 26281ad6265SDimitry Andric 26381ad6265SDimitry Andric private: 26481ad6265SDimitry Andric /// When adding new members to this class, make sure 26581ad6265SDimitry Andric /// to update \a CalculateApproximateMemoryUsage() accordingly. 266fe6060f1SDimitry Andric lldb::ThreadSP m_thread_sp; 26781ad6265SDimitry Andric 26881ad6265SDimitry Andric /// We use a union to optimize the memory usage for the different kinds of 26981ad6265SDimitry Andric /// trace items. 27081ad6265SDimitry Andric union TraceItemStorage { 27181ad6265SDimitry Andric /// The load addresses of this item if it's an instruction. 27281ad6265SDimitry Andric uint64_t load_address; 27381ad6265SDimitry Andric 27481ad6265SDimitry Andric /// The event kind of this item if it's an event 27581ad6265SDimitry Andric lldb::TraceEvent event; 27681ad6265SDimitry Andric 27781ad6265SDimitry Andric /// The string message of this item if it's an error 27881ad6265SDimitry Andric const char *error; 27981ad6265SDimitry Andric }; 28081ad6265SDimitry Andric 28181ad6265SDimitry Andric /// Create a new trace item. 28281ad6265SDimitry Andric /// 28381ad6265SDimitry Andric /// \return 28481ad6265SDimitry Andric /// The index of the new item. 28581ad6265SDimitry Andric DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind); 28681ad6265SDimitry Andric 28781ad6265SDimitry Andric /// Most of the trace data is stored here. 28881ad6265SDimitry Andric std::vector<TraceItemStorage> m_item_data; 28981ad6265SDimitry Andric /// The TraceItemKind for each trace item encoded as uint8_t. We don't include 29081ad6265SDimitry Andric /// it in TraceItemStorage to avoid padding. 29181ad6265SDimitry Andric std::vector<uint8_t> m_item_kinds; 29281ad6265SDimitry Andric 293972a253aSDimitry Andric /// This map contains the TSCs of the decoded trace items. It maps 294972a253aSDimitry Andric /// `item index -> TSC`, where `item index` is the first index 295972a253aSDimitry Andric /// at which the mapped TSC first appears. We use this representation because 296972a253aSDimitry Andric /// TSCs are sporadic and we can think of them as ranges. 297972a253aSDimitry Andric std::map<uint64_t, TSCRange> m_tscs; 29881ad6265SDimitry Andric /// This is the chronologically last TSC that has been added. 299*bdd1243dSDimitry Andric std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc = 300*bdd1243dSDimitry Andric std::nullopt; 301972a253aSDimitry Andric /// This map contains the non-interpolated nanoseconds timestamps of the 302972a253aSDimitry Andric /// decoded trace items. It maps `item index -> nanoseconds`, where `item 303972a253aSDimitry Andric /// index` is the first index at which the mapped nanoseconds first appears. 304972a253aSDimitry Andric /// We use this representation because timestamps are sporadic and we think of 305972a253aSDimitry Andric /// them as ranges. 306972a253aSDimitry Andric std::map<uint64_t, NanosecondsRange> m_nanoseconds; 307*bdd1243dSDimitry Andric std::optional<std::map<uint64_t, NanosecondsRange>::iterator> 308*bdd1243dSDimitry Andric m_last_nanoseconds = std::nullopt; 30981ad6265SDimitry Andric 310*bdd1243dSDimitry Andric // The cpu information is stored as a map. It maps `item index -> CPU`. 311753f127fSDimitry Andric // A CPU is associated with the next instructions that follow until the next 312753f127fSDimitry Andric // cpu is seen. 313753f127fSDimitry Andric std::map<uint64_t, lldb::cpu_id_t> m_cpus; 314753f127fSDimitry Andric /// This is the chronologically last CPU ID. 315*bdd1243dSDimitry Andric std::optional<uint64_t> m_last_cpu; 316*bdd1243dSDimitry Andric 317*bdd1243dSDimitry Andric // The PSB offsets are stored as a map. It maps `item index -> psb offset`. 318*bdd1243dSDimitry Andric llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets; 319753f127fSDimitry Andric 320972a253aSDimitry Andric /// TSC -> nanos conversion utility. 321*bdd1243dSDimitry Andric std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion; 322*bdd1243dSDimitry Andric 323*bdd1243dSDimitry Andric /// Statistics of all tracing errors. 324*bdd1243dSDimitry Andric ErrorStats m_error_stats; 325972a253aSDimitry Andric 32681ad6265SDimitry Andric /// Statistics of all tracing events. 32781ad6265SDimitry Andric EventsStats m_events_stats; 32881ad6265SDimitry Andric /// Total amount of time spent decoding. 32981ad6265SDimitry Andric std::chrono::milliseconds m_total_decoding_time{0}; 330*bdd1243dSDimitry Andric 331*bdd1243dSDimitry Andric /// Total number of instructions in the trace. 332*bdd1243dSDimitry Andric uint64_t m_insn_count = 0; 333e8d8bef9SDimitry Andric }; 334e8d8bef9SDimitry Andric 335fe6060f1SDimitry Andric using DecodedThreadSP = std::shared_ptr<DecodedThread>; 336fe6060f1SDimitry Andric 337e8d8bef9SDimitry Andric } // namespace trace_intel_pt 338e8d8bef9SDimitry Andric } // namespace lldb_private 339e8d8bef9SDimitry Andric 340e8d8bef9SDimitry Andric #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 341