1be691f3bSpatrick //===-- DecodedThread.h -----------------------------------------*- C++ -*-===// 2be691f3bSpatrick // 3be691f3bSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4be691f3bSpatrick // See https://llvm.org/LICENSE.txt for license information. 5be691f3bSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6be691f3bSpatrick // 7be691f3bSpatrick //===----------------------------------------------------------------------===// 8be691f3bSpatrick 9be691f3bSpatrick #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 10be691f3bSpatrick #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 11be691f3bSpatrick 12*f6aab3d8Srobert #include "intel-pt.h" 13be691f3bSpatrick #include "lldb/Target/Trace.h" 14be691f3bSpatrick #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" 15*f6aab3d8Srobert #include "llvm/Support/Errc.h" 16*f6aab3d8Srobert #include "llvm/Support/Error.h" 17*f6aab3d8Srobert #include <optional> 18*f6aab3d8Srobert #include <utility> 19*f6aab3d8Srobert #include <vector> 20be691f3bSpatrick 21be691f3bSpatrick namespace lldb_private { 22be691f3bSpatrick namespace trace_intel_pt { 23be691f3bSpatrick 24be691f3bSpatrick /// Class for representing a libipt decoding error. 25be691f3bSpatrick class IntelPTError : public llvm::ErrorInfo<IntelPTError> { 26be691f3bSpatrick public: 27be691f3bSpatrick static char ID; 28be691f3bSpatrick 29be691f3bSpatrick /// \param[in] libipt_error_code 30be691f3bSpatrick /// Negative number returned by libipt when decoding the trace and 31be691f3bSpatrick /// signaling errors. 32be691f3bSpatrick /// 33be691f3bSpatrick /// \param[in] address 34be691f3bSpatrick /// Optional instruction address. When decoding an individual instruction, 35be691f3bSpatrick /// its address might be available in the \a pt_insn object, and should be 36be691f3bSpatrick /// passed to this constructor. Other errors don't have an associated 37be691f3bSpatrick /// address. 38be691f3bSpatrick IntelPTError(int libipt_error_code, 39be691f3bSpatrick lldb::addr_t address = LLDB_INVALID_ADDRESS); 40be691f3bSpatrick convertToErrorCode()41be691f3bSpatrick std::error_code convertToErrorCode() const override { 42be691f3bSpatrick return llvm::errc::not_supported; 43be691f3bSpatrick } 44be691f3bSpatrick GetLibiptErrorCode()45*f6aab3d8Srobert int GetLibiptErrorCode() const { return m_libipt_error_code; } 46*f6aab3d8Srobert 47be691f3bSpatrick void log(llvm::raw_ostream &OS) const override; 48be691f3bSpatrick 49be691f3bSpatrick private: 50be691f3bSpatrick int m_libipt_error_code; 51be691f3bSpatrick lldb::addr_t m_address; 52be691f3bSpatrick }; 53be691f3bSpatrick 54be691f3bSpatrick /// \class DecodedThread 55be691f3bSpatrick /// Class holding the instructions and function call hierarchy obtained from 56be691f3bSpatrick /// decoding a trace, as well as a position cursor used when reverse debugging 57be691f3bSpatrick /// the trace. 58be691f3bSpatrick /// 59be691f3bSpatrick /// Each decoded thread contains a cursor to the current position the user is 60be691f3bSpatrick /// stopped at. See \a Trace::GetCursorPosition for more information. 61be691f3bSpatrick class DecodedThread : public std::enable_shared_from_this<DecodedThread> { 62be691f3bSpatrick public: 63*f6aab3d8Srobert using TSC = uint64_t; 64be691f3bSpatrick 65*f6aab3d8Srobert /// A structure that represents a maximal range of trace items associated to 66*f6aab3d8Srobert /// the same TSC value. 67*f6aab3d8Srobert struct TSCRange { 68*f6aab3d8Srobert TSC tsc; 69*f6aab3d8Srobert /// Number of trace items in this range. 70*f6aab3d8Srobert uint64_t items_count; 71*f6aab3d8Srobert /// Index of the first trace item in this range. 72*f6aab3d8Srobert uint64_t first_item_index; 73be691f3bSpatrick 74*f6aab3d8Srobert /// \return 75*f6aab3d8Srobert /// \b true if and only if the given \p item_index is covered by this 76*f6aab3d8Srobert /// range. 77*f6aab3d8Srobert bool InRange(uint64_t item_index) const; 78*f6aab3d8Srobert }; 79*f6aab3d8Srobert 80*f6aab3d8Srobert /// A structure that represents a maximal range of trace items associated to 81*f6aab3d8Srobert /// the same non-interpolated timestamps in nanoseconds. 82*f6aab3d8Srobert struct NanosecondsRange { 83*f6aab3d8Srobert /// The nanoseconds value for this range. 84*f6aab3d8Srobert uint64_t nanos; 85*f6aab3d8Srobert /// The corresponding TSC value for this range. 86*f6aab3d8Srobert TSC tsc; 87*f6aab3d8Srobert /// A nullable pointer to the next range. 88*f6aab3d8Srobert NanosecondsRange *next_range; 89*f6aab3d8Srobert /// Number of trace items in this range. 90*f6aab3d8Srobert uint64_t items_count; 91*f6aab3d8Srobert /// Index of the first trace item in this range. 92*f6aab3d8Srobert uint64_t first_item_index; 93*f6aab3d8Srobert 94*f6aab3d8Srobert /// Calculate an interpolated timestamp in nanoseconds for the given item 95*f6aab3d8Srobert /// index. It's guaranteed that two different item indices will produce 96*f6aab3d8Srobert /// different interpolated values. 97*f6aab3d8Srobert /// 98*f6aab3d8Srobert /// \param[in] item_index 99*f6aab3d8Srobert /// The index of the item whose timestamp will be estimated. It has to be 100*f6aab3d8Srobert /// part of this range. 101*f6aab3d8Srobert /// 102*f6aab3d8Srobert /// \param[in] beginning_of_time_nanos 103*f6aab3d8Srobert /// The timestamp at which tracing started. 104*f6aab3d8Srobert /// 105*f6aab3d8Srobert /// \param[in] tsc_conversion 106*f6aab3d8Srobert /// The tsc -> nanos conversion utility 107be691f3bSpatrick /// 108be691f3bSpatrick /// \return 109*f6aab3d8Srobert /// An interpolated timestamp value for the given trace item. 110*f6aab3d8Srobert double 111*f6aab3d8Srobert GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos, 112*f6aab3d8Srobert const LinuxPerfZeroTscConversion &tsc_conversion) const; 113be691f3bSpatrick 114*f6aab3d8Srobert /// \return 115*f6aab3d8Srobert /// \b true if and only if the given \p item_index is covered by this 116*f6aab3d8Srobert /// range. 117*f6aab3d8Srobert bool InRange(uint64_t item_index) const; 118*f6aab3d8Srobert }; 119be691f3bSpatrick 120*f6aab3d8Srobert // Struct holding counts for events 121*f6aab3d8Srobert struct EventsStats { 122*f6aab3d8Srobert /// A count for each individual event kind. We use an unordered map instead 123*f6aab3d8Srobert /// of a DenseMap because DenseMap can't understand enums. 124*f6aab3d8Srobert /// 125*f6aab3d8Srobert /// Note: We can't use DenseMap because lldb::TraceEvent is not 126*f6aab3d8Srobert /// automatically handled correctly by DenseMap. We'd need to implement a 127*f6aab3d8Srobert /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for 128*f6aab3d8Srobert /// such a simple structure. 129*f6aab3d8Srobert std::unordered_map<lldb::TraceEvent, uint64_t> events_counts; 130*f6aab3d8Srobert uint64_t total_count = 0; 131*f6aab3d8Srobert 132*f6aab3d8Srobert void RecordEvent(lldb::TraceEvent event); 133*f6aab3d8Srobert }; 134*f6aab3d8Srobert 135*f6aab3d8Srobert // Struct holding counts for errors 136*f6aab3d8Srobert struct ErrorStats { 137*f6aab3d8Srobert /// The following counters are mutually exclusive 138*f6aab3d8Srobert /// \{ 139*f6aab3d8Srobert uint64_t other_errors = 0; 140*f6aab3d8Srobert uint64_t fatal_errors = 0; 141*f6aab3d8Srobert // libipt error -> count 142*f6aab3d8Srobert llvm::DenseMap<const char *, uint64_t> libipt_errors; 143*f6aab3d8Srobert /// \} 144*f6aab3d8Srobert 145*f6aab3d8Srobert uint64_t GetTotalCount() const; 146*f6aab3d8Srobert 147*f6aab3d8Srobert void RecordError(int libipt_error_code); 148*f6aab3d8Srobert 149*f6aab3d8Srobert void RecordError(bool fatal); 150*f6aab3d8Srobert }; 151*f6aab3d8Srobert 152*f6aab3d8Srobert DecodedThread( 153*f6aab3d8Srobert lldb::ThreadSP thread_sp, 154*f6aab3d8Srobert const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion); 155*f6aab3d8Srobert 156*f6aab3d8Srobert /// Get the total number of instruction, errors and events from the decoded 157*f6aab3d8Srobert /// trace. 158*f6aab3d8Srobert uint64_t GetItemsCount() const; 159*f6aab3d8Srobert 160*f6aab3d8Srobert /// \return 161*f6aab3d8Srobert /// The error associated with a given trace item. 162*f6aab3d8Srobert const char *GetErrorByIndex(uint64_t item_index) const; 163*f6aab3d8Srobert 164*f6aab3d8Srobert /// \return 165*f6aab3d8Srobert /// The trace item kind given an item index. 166*f6aab3d8Srobert lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const; 167*f6aab3d8Srobert 168*f6aab3d8Srobert /// \return 169*f6aab3d8Srobert /// The underlying event type for the given trace item index. 170*f6aab3d8Srobert lldb::TraceEvent GetEventByIndex(int item_index) const; 171*f6aab3d8Srobert 172*f6aab3d8Srobert /// Get the most recent CPU id before or at the given trace item index. 173*f6aab3d8Srobert /// 174*f6aab3d8Srobert /// \param[in] item_index 175*f6aab3d8Srobert /// The trace item index to compare with. 176be691f3bSpatrick /// 177be691f3bSpatrick /// \return 178*f6aab3d8Srobert /// The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available. 179*f6aab3d8Srobert lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const; 180*f6aab3d8Srobert 181*f6aab3d8Srobert /// \return 182*f6aab3d8Srobert /// The PSB offset associated with the given item index. 183*f6aab3d8Srobert lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const; 184*f6aab3d8Srobert 185*f6aab3d8Srobert /// Get a maximal range of trace items that include the given \p item_index 186*f6aab3d8Srobert /// that have the same TSC value. 187*f6aab3d8Srobert /// 188*f6aab3d8Srobert /// \param[in] item_index 189*f6aab3d8Srobert /// The trace item index to compare with. 190*f6aab3d8Srobert /// 191*f6aab3d8Srobert /// \return 192*f6aab3d8Srobert /// The requested TSC range, or \a std::nullopt if not available. 193*f6aab3d8Srobert std::optional<DecodedThread::TSCRange> 194*f6aab3d8Srobert GetTSCRangeByIndex(uint64_t item_index) const; 195*f6aab3d8Srobert 196*f6aab3d8Srobert /// Get a maximal range of trace items that include the given \p item_index 197*f6aab3d8Srobert /// that have the same nanoseconds timestamp without interpolation. 198*f6aab3d8Srobert /// 199*f6aab3d8Srobert /// \param[in] item_index 200*f6aab3d8Srobert /// The trace item index to compare with. 201*f6aab3d8Srobert /// 202*f6aab3d8Srobert /// \return 203*f6aab3d8Srobert /// The requested nanoseconds range, or \a std::nullopt if not available. 204*f6aab3d8Srobert std::optional<DecodedThread::NanosecondsRange> 205*f6aab3d8Srobert GetNanosecondsRangeByIndex(uint64_t item_index); 206*f6aab3d8Srobert 207*f6aab3d8Srobert /// \return 208*f6aab3d8Srobert /// The load address of the instruction at the given index. 209*f6aab3d8Srobert lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const; 210*f6aab3d8Srobert 211*f6aab3d8Srobert /// \return 212*f6aab3d8Srobert /// The number of instructions in this trace (not trace items). 213*f6aab3d8Srobert uint64_t GetTotalInstructionCount() const; 214*f6aab3d8Srobert 215*f6aab3d8Srobert /// Return an object with statistics of the trace events that happened. 216*f6aab3d8Srobert /// 217*f6aab3d8Srobert /// \return 218*f6aab3d8Srobert /// The stats object of all the events. 219*f6aab3d8Srobert const EventsStats &GetEventsStats() const; 220*f6aab3d8Srobert 221*f6aab3d8Srobert /// Return an object with statistics of the trace errors that happened. 222*f6aab3d8Srobert /// 223*f6aab3d8Srobert /// \return 224*f6aab3d8Srobert /// The stats object of all the events. 225*f6aab3d8Srobert const ErrorStats &GetErrorStats() const; 226*f6aab3d8Srobert 227*f6aab3d8Srobert /// The approximate size in bytes used by this instance, 228*f6aab3d8Srobert /// including all the already decoded instructions. 229*f6aab3d8Srobert size_t CalculateApproximateMemoryUsage() const; 230*f6aab3d8Srobert 231*f6aab3d8Srobert lldb::ThreadSP GetThread(); 232*f6aab3d8Srobert 233*f6aab3d8Srobert /// Notify this object that a new tsc has been seen. 234*f6aab3d8Srobert /// If this a new TSC, an event will be created. 235*f6aab3d8Srobert void NotifyTsc(TSC tsc); 236*f6aab3d8Srobert 237*f6aab3d8Srobert /// Notify this object that a CPU has been seen. 238*f6aab3d8Srobert /// If this a new CPU, an event will be created. 239*f6aab3d8Srobert void NotifyCPU(lldb::cpu_id_t cpu_id); 240*f6aab3d8Srobert 241*f6aab3d8Srobert /// Notify this object that a new PSB has been seen. 242*f6aab3d8Srobert void NotifySyncPoint(lldb::addr_t psb_offset); 243*f6aab3d8Srobert 244*f6aab3d8Srobert /// Append a decoding error. 245*f6aab3d8Srobert void AppendError(const IntelPTError &error); 246*f6aab3d8Srobert 247*f6aab3d8Srobert /// Append a custom decoding. 248*f6aab3d8Srobert /// 249*f6aab3d8Srobert /// \param[in] error 250*f6aab3d8Srobert /// The error message. 251*f6aab3d8Srobert /// 252*f6aab3d8Srobert /// \param[in] fatal 253*f6aab3d8Srobert /// If \b true, then the whole decoded thread should be discarded because a 254*f6aab3d8Srobert /// fatal anomaly has been found. 255*f6aab3d8Srobert void AppendCustomError(llvm::StringRef error, bool fatal = false); 256*f6aab3d8Srobert 257*f6aab3d8Srobert /// Append an event. 258*f6aab3d8Srobert void AppendEvent(lldb::TraceEvent); 259*f6aab3d8Srobert 260*f6aab3d8Srobert /// Append an instruction. 261*f6aab3d8Srobert void AppendInstruction(const pt_insn &insn); 262be691f3bSpatrick 263be691f3bSpatrick private: 264*f6aab3d8Srobert /// When adding new members to this class, make sure 265*f6aab3d8Srobert /// to update \a CalculateApproximateMemoryUsage() accordingly. 266be691f3bSpatrick lldb::ThreadSP m_thread_sp; 267*f6aab3d8Srobert 268*f6aab3d8Srobert /// We use a union to optimize the memory usage for the different kinds of 269*f6aab3d8Srobert /// trace items. 270*f6aab3d8Srobert union TraceItemStorage { 271*f6aab3d8Srobert /// The load addresses of this item if it's an instruction. 272*f6aab3d8Srobert uint64_t load_address; 273*f6aab3d8Srobert 274*f6aab3d8Srobert /// The event kind of this item if it's an event 275*f6aab3d8Srobert lldb::TraceEvent event; 276*f6aab3d8Srobert 277*f6aab3d8Srobert /// The string message of this item if it's an error 278*f6aab3d8Srobert const char *error; 279*f6aab3d8Srobert }; 280*f6aab3d8Srobert 281*f6aab3d8Srobert /// Create a new trace item. 282*f6aab3d8Srobert /// 283*f6aab3d8Srobert /// \return 284*f6aab3d8Srobert /// The index of the new item. 285*f6aab3d8Srobert DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind); 286*f6aab3d8Srobert 287*f6aab3d8Srobert /// Most of the trace data is stored here. 288*f6aab3d8Srobert std::vector<TraceItemStorage> m_item_data; 289*f6aab3d8Srobert /// The TraceItemKind for each trace item encoded as uint8_t. We don't include 290*f6aab3d8Srobert /// it in TraceItemStorage to avoid padding. 291*f6aab3d8Srobert std::vector<uint8_t> m_item_kinds; 292*f6aab3d8Srobert 293*f6aab3d8Srobert /// This map contains the TSCs of the decoded trace items. It maps 294*f6aab3d8Srobert /// `item index -> TSC`, where `item index` is the first index 295*f6aab3d8Srobert /// at which the mapped TSC first appears. We use this representation because 296*f6aab3d8Srobert /// TSCs are sporadic and we can think of them as ranges. 297*f6aab3d8Srobert std::map<uint64_t, TSCRange> m_tscs; 298*f6aab3d8Srobert /// This is the chronologically last TSC that has been added. 299*f6aab3d8Srobert std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc = 300*f6aab3d8Srobert std::nullopt; 301*f6aab3d8Srobert /// This map contains the non-interpolated nanoseconds timestamps of the 302*f6aab3d8Srobert /// decoded trace items. It maps `item index -> nanoseconds`, where `item 303*f6aab3d8Srobert /// index` is the first index at which the mapped nanoseconds first appears. 304*f6aab3d8Srobert /// We use this representation because timestamps are sporadic and we think of 305*f6aab3d8Srobert /// them as ranges. 306*f6aab3d8Srobert std::map<uint64_t, NanosecondsRange> m_nanoseconds; 307*f6aab3d8Srobert std::optional<std::map<uint64_t, NanosecondsRange>::iterator> 308*f6aab3d8Srobert m_last_nanoseconds = std::nullopt; 309*f6aab3d8Srobert 310*f6aab3d8Srobert // The cpu information is stored as a map. It maps `item index -> CPU`. 311*f6aab3d8Srobert // A CPU is associated with the next instructions that follow until the next 312*f6aab3d8Srobert // cpu is seen. 313*f6aab3d8Srobert std::map<uint64_t, lldb::cpu_id_t> m_cpus; 314*f6aab3d8Srobert /// This is the chronologically last CPU ID. 315*f6aab3d8Srobert std::optional<uint64_t> m_last_cpu; 316*f6aab3d8Srobert 317*f6aab3d8Srobert // The PSB offsets are stored as a map. It maps `item index -> psb offset`. 318*f6aab3d8Srobert llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets; 319*f6aab3d8Srobert 320*f6aab3d8Srobert /// TSC -> nanos conversion utility. 321*f6aab3d8Srobert std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion; 322*f6aab3d8Srobert 323*f6aab3d8Srobert /// Statistics of all tracing errors. 324*f6aab3d8Srobert ErrorStats m_error_stats; 325*f6aab3d8Srobert 326*f6aab3d8Srobert /// Statistics of all tracing events. 327*f6aab3d8Srobert EventsStats m_events_stats; 328*f6aab3d8Srobert /// Total amount of time spent decoding. 329*f6aab3d8Srobert std::chrono::milliseconds m_total_decoding_time{0}; 330*f6aab3d8Srobert 331*f6aab3d8Srobert /// Total number of instructions in the trace. 332*f6aab3d8Srobert uint64_t m_insn_count = 0; 333be691f3bSpatrick }; 334be691f3bSpatrick 335be691f3bSpatrick using DecodedThreadSP = std::shared_ptr<DecodedThread>; 336be691f3bSpatrick 337be691f3bSpatrick } // namespace trace_intel_pt 338be691f3bSpatrick } // namespace lldb_private 339be691f3bSpatrick 340be691f3bSpatrick #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 341