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 /// \class TscRange 75 /// Class that represents the trace range associated with a given TSC. 76 /// It provides efficient iteration to the previous or next TSC range in the 77 /// decoded trace. 78 /// 79 /// TSC timestamps are emitted by the decoder infrequently, which means 80 /// that each TSC covers a range of instruction indices, which can be used to 81 /// speed up TSC lookups. 82 class TscRange { 83 public: 84 /// Check if this TSC range includes the given instruction index. 85 bool InRange(size_t insn_index) const; 86 87 /// Get the next range chronologically. 88 llvm::Optional<TscRange> Next() const; 89 90 /// Get the previous range chronologically. 91 llvm::Optional<TscRange> Prev() const; 92 93 /// Get the TSC value. 94 size_t GetTsc() const; 95 /// Get the smallest instruction index that has this TSC. 96 size_t GetStartInstructionIndex() const; 97 /// Get the largest instruction index that has this TSC. 98 size_t GetEndInstructionIndex() const; 99 100 private: 101 friend class DecodedThread; 102 103 TscRange(std::map<size_t, uint64_t>::const_iterator it, 104 const DecodedThread &decoded_thread); 105 106 /// The iterator pointing to the beginning of the range. 107 std::map<size_t, uint64_t>::const_iterator m_it; 108 /// The largest instruction index that has this TSC. 109 size_t m_end_index; 110 111 const DecodedThread *m_decoded_thread; 112 }; 113 114 // Struct holding counts for libipts errors; 115 struct LibiptErrorsStats { 116 // libipt error -> count 117 llvm::DenseMap<const char *, int> libipt_errors_counts; 118 size_t total_count = 0; 119 120 void RecordError(int libipt_error_code); 121 }; 122 123 // Struct holding counts for events; 124 struct EventsStats { 125 /// A count for each individual event kind. We use an unordered map instead 126 /// of a DenseMap because DenseMap can't understand enums. 127 std::unordered_map<lldb::TraceEvent, size_t> events_counts; 128 size_t total_count = 0; 129 130 void RecordEvent(lldb::TraceEvent event); 131 }; 132 133 DecodedThread(lldb::ThreadSP thread_sp); 134 135 /// Utility constructor that initializes the trace with a provided error. 136 DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err); 137 138 /// Get the total number of instruction, errors and events from the decoded 139 /// trace. 140 int64_t GetItemsCount() const; 141 142 /// Construct the TSC range that covers the given instruction index. 143 /// This operation is O(logn) and should be used sparingly. 144 /// If the trace was collected with TSC support, all the instructions of 145 /// the trace will have associated TSCs. This means that this method will 146 /// only return \b llvm::None if there are no TSCs whatsoever in the trace. 147 /// 148 /// \param[in] insn_index 149 /// The instruction index in question. 150 /// 151 /// \param[in] hint_range 152 /// An optional range that might include the given index or might be a 153 /// neighbor of it. It might help speed it traversals of the trace with 154 /// short jumps. 155 llvm::Optional<TscRange> CalculateTscRange( 156 size_t insn_index, 157 const llvm::Optional<DecodedThread::TscRange> &hint_range) const; 158 159 /// \return 160 /// The error associated with a given trace item. 161 const char *GetErrorByIndex(size_t item_index) const; 162 163 /// \return 164 /// The trace item kind given an item index. 165 lldb::TraceItemKind GetItemKindByIndex(size_t item_index) const; 166 167 /// \return 168 /// The underlying event type for the given trace item index. 169 lldb::TraceEvent GetEventByIndex(int item_index) const; 170 171 /// \return 172 /// The load address of the instruction at the given index. 173 lldb::addr_t GetInstructionLoadAddress(size_t item_index) const; 174 175 /// Get a new cursor for the decoded thread. 176 lldb::TraceCursorUP CreateNewCursor(); 177 178 /// Return an object with statistics of the TSC decoding errors that happened. 179 /// A TSC error is not a fatal error and doesn't create gaps in the trace. 180 /// Instead we only keep track of them as statistics. 181 /// 182 /// \return 183 /// An object with the statistics of TSC decoding errors. 184 const LibiptErrorsStats &GetTscErrorsStats() const; 185 186 /// Return an object with statistics of the trace events that happened. 187 /// 188 /// \return 189 /// The stats object of all the events. 190 const EventsStats &GetEventsStats() const; 191 192 /// Record an error decoding a TSC timestamp. 193 /// 194 /// See \a GetTscErrors() for more documentation. 195 /// 196 /// \param[in] libipt_error_code 197 /// An error returned by the libipt library. 198 void RecordTscError(int libipt_error_code); 199 200 /// The approximate size in bytes used by this instance, 201 /// including all the already decoded instructions. 202 size_t CalculateApproximateMemoryUsage() const; 203 204 lldb::ThreadSP GetThread(); 205 206 /// Notify this object that a new tsc has been seen. 207 void NotifyTsc(uint64_t tsc); 208 209 /// Append a decoding error. 210 void AppendError(const IntelPTError &error); 211 212 /// Append a custom decoding. 213 void AppendCustomError(llvm::StringRef error); 214 215 /// Append an event. 216 void AppendEvent(lldb::TraceEvent); 217 218 /// Append an instruction. 219 void AppendInstruction(const pt_insn &insn); 220 221 private: 222 /// When adding new members to this class, make sure 223 /// to update \a CalculateApproximateMemoryUsage() accordingly. 224 lldb::ThreadSP m_thread_sp; 225 226 /// We use a union to optimize the memory usage for the different kinds of 227 /// trace items. 228 union TraceItemStorage { 229 /// The load addresses of this item if it's an instruction. 230 uint64_t load_address; 231 232 /// The event kind of this item if it's an event 233 lldb::TraceEvent event; 234 235 /// The string message of this item if it's an error 236 const char *error; 237 }; 238 239 /// Create a new trace item. 240 /// 241 /// \return 242 /// The index of the new item. 243 DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind); 244 245 /// Most of the trace data is stored here. 246 std::vector<TraceItemStorage> m_item_data; 247 /// The TraceItemKind for each trace item encoded as uint8_t. We don't include 248 /// it in TraceItemStorage to avoid padding. 249 std::vector<uint8_t> m_item_kinds; 250 251 /// This map contains the TSCs of the decoded instructions. It maps 252 /// `instruction index -> TSC`, where `instruction index` is the first index 253 /// at which the mapped TSC appears. We use this representation because TSCs 254 /// are sporadic and we can think of them as ranges. If TSCs are present in 255 /// the trace, all instructions will have an associated TSC, including the 256 /// first one. Otherwise, this map will be empty. 257 std::map<uint64_t, uint64_t> m_instruction_timestamps; 258 /// This is the chronologically last TSC that has been added. 259 llvm::Optional<uint64_t> m_last_tsc = llvm::None; 260 261 /// Statistics of all tracing events. 262 EventsStats m_events_stats; 263 /// Statistics of libipt errors when decoding TSCs. 264 LibiptErrorsStats m_tsc_errors_stats; 265 /// Total amount of time spent decoding. 266 std::chrono::milliseconds m_total_decoding_time{0}; 267 }; 268 269 using DecodedThreadSP = std::shared_ptr<DecodedThread>; 270 271 } // namespace trace_intel_pt 272 } // namespace lldb_private 273 274 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 275