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