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