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