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