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 /// Class for representing a libipt decoding error. 27 class IntelPTError : public llvm::ErrorInfo<IntelPTError> { 28 public: 29 static char ID; 30 31 /// \param[in] libipt_error_code 32 /// Negative number returned by libipt when decoding the trace and 33 /// signaling errors. 34 /// 35 /// \param[in] address 36 /// Optional instruction address. When decoding an individual instruction, 37 /// its address might be available in the \a pt_insn object, and should be 38 /// passed to this constructor. Other errors don't have an associated 39 /// address. 40 IntelPTError(int libipt_error_code, 41 lldb::addr_t address = LLDB_INVALID_ADDRESS); 42 43 std::error_code convertToErrorCode() const override { 44 return llvm::errc::not_supported; 45 } 46 47 void log(llvm::raw_ostream &OS) const override; 48 49 private: 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. 61 class DecodedThread : public std::enable_shared_from_this<DecodedThread> { 62 public: 63 /// \class TscRange 64 /// Class that represents the trace range associated with a given TSC. 65 /// It provides efficient iteration to the previous or next TSC range in the 66 /// decoded trace. 67 /// 68 /// TSC timestamps are emitted by the decoder infrequently, which means 69 /// that each TSC covers a range of instruction indices, which can be used to 70 /// speed up TSC lookups. 71 class TscRange { 72 public: 73 /// Check if this TSC range includes the given instruction index. 74 bool InRange(size_t insn_index) const; 75 76 /// Get the next range chronologically. 77 llvm::Optional<TscRange> Next() const; 78 79 /// Get the previous range chronologically. 80 llvm::Optional<TscRange> Prev() const; 81 82 /// Get the TSC value. 83 size_t GetTsc() const; 84 /// Get the smallest instruction index that has this TSC. 85 size_t GetStartInstructionIndex() const; 86 /// Get the largest instruction index that has this TSC. 87 size_t GetEndInstructionIndex() const; 88 89 private: 90 friend class DecodedThread; 91 92 TscRange(std::map<size_t, uint64_t>::const_iterator it, 93 const DecodedThread &decoded_thread); 94 95 /// The iterator pointing to the beginning of the range. 96 std::map<size_t, uint64_t>::const_iterator m_it; 97 /// The largest instruction index that has this TSC. 98 size_t m_end_index; 99 100 const DecodedThread *m_decoded_thread; 101 }; 102 103 // Struct holding counts for libipts errors; 104 struct LibiptErrors { 105 // libipt error -> count 106 llvm::DenseMap<const char *, int> libipt_errors; 107 int total_count = 0; 108 109 void RecordError(int libipt_error_code); 110 }; 111 112 DecodedThread(lldb::ThreadSP thread_sp); 113 114 /// Utility constructor that initializes the trace with a provided error. 115 DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err); 116 117 /// Append a successfully decoded instruction. 118 void AppendInstruction(const pt_insn &instruction); 119 120 /// Append a sucessfully decoded instruction with an associated TSC timestamp. 121 void AppendInstruction(const pt_insn &instruction, uint64_t tsc); 122 123 /// Append a decoding error (i.e. an instruction that failed to be decoded). 124 void AppendError(llvm::Error &&error); 125 126 /// Append a decoding error with a corresponding TSC. 127 void AppendError(llvm::Error &&error, uint64_t tsc); 128 129 /// Get the total number of instruction pointers from the decoded trace. 130 /// This will include instructions that indicate errors (or gaps) in the 131 /// trace. For an instruction error, you can access its underlying error 132 /// message with the \a GetErrorByInstructionIndex() method. 133 size_t GetInstructionsCount() const; 134 135 /// \return 136 /// The load address of the instruction at the given index, or \a 137 /// LLDB_INVALID_ADDRESS if it is an error. 138 lldb::addr_t GetInstructionLoadAddress(size_t insn_index) const; 139 140 /// Get the \a lldb::TraceInstructionControlFlowType categories of the 141 /// instruction. 142 /// 143 /// \return 144 /// The control flow categories, or \b 0 if the instruction is an error. 145 lldb::TraceInstructionControlFlowType 146 GetInstructionControlFlowType(size_t insn_index) const; 147 148 /// Construct the TSC range that covers the given instruction index. 149 /// This operation is O(logn) and should be used sparingly. 150 /// If the trace was collected with TSC support, all the instructions of 151 /// the trace will have associated TSCs. This means that this method will 152 /// only return \b llvm::None if there are no TSCs whatsoever in the trace. 153 /// 154 /// \param[in] insn_index 155 /// The instruction index in question. 156 /// 157 /// \param[in] hint_range 158 /// An optional range that might include the given index or might be a 159 /// neighbor of it. It might help speed it traversals of the trace with 160 /// short jumps. 161 llvm::Optional<TscRange> CalculateTscRange( 162 size_t insn_index, 163 const llvm::Optional<DecodedThread::TscRange> &hint_range) const; 164 165 /// Check if an instruction given by its index is an error. 166 bool IsInstructionAnError(size_t insn_idx) const; 167 168 /// Get the error associated with a given instruction index. 169 /// 170 /// \return 171 /// The error message of \b nullptr if the given index 172 /// points to a valid instruction. 173 const char *GetErrorByInstructionIndex(size_t ins_idx); 174 175 /// Get a new cursor for the decoded thread. 176 lldb::TraceCursorUP GetCursor(); 177 178 /// Return the number of TSC decoding errors that happened. A TSC error 179 /// is not a fatal error and doesn't create gaps in the trace. Instead 180 /// we only keep track of them as a statistic. 181 /// 182 /// \return 183 /// The number of TSC decoding errors. 184 const LibiptErrors &GetTscErrors() const; 185 186 /// Record an error decoding a TSC timestamp. 187 /// 188 /// See \a GetTscErrors() for more documentation. 189 /// 190 /// \param[in] libipt_error_code 191 /// An error returned by the libipt library. 192 void RecordTscError(int libipt_error_code); 193 194 /// The approximate size in bytes used by this instance, 195 /// including all the already decoded instructions. 196 size_t CalculateApproximateMemoryUsage() const; 197 198 lldb::ThreadSP GetThread(); 199 200 private: 201 /// Notify this class that the last added instruction or error has 202 /// an associated TSC. 203 void RecordTscForLastInstruction(uint64_t tsc); 204 205 /// When adding new members to this class, make sure 206 /// to update \a CalculateApproximateMemoryUsage() accordingly. 207 lldb::ThreadSP m_thread_sp; 208 /// The low level storage of all instruction addresses. Each instruction has 209 /// an index in this vector and it will be used in other parts of the code. 210 std::vector<lldb::addr_t> m_instruction_ips; 211 /// The size in bytes of each instruction. 212 std::vector<uint8_t> m_instruction_sizes; 213 /// The libipt instruction class for each instruction. 214 std::vector<pt_insn_class> m_instruction_classes; 215 216 /// This map contains the TSCs of the decoded instructions. It maps 217 /// `instruction index -> TSC`, where `instruction index` is the first index 218 /// at which the mapped TSC appears. We use this representation because TSCs 219 /// are sporadic and we can think of them as ranges. If TSCs are present in 220 /// the trace, all instructions will have an associated TSC, including the 221 /// first one. Otherwise, this map will be empty. 222 std::map<size_t, uint64_t> m_instruction_timestamps; 223 /// This is the chronologically last TSC that has been added. 224 llvm::Optional<uint64_t> m_last_tsc = llvm::None; 225 // This variables stores the messages of all the error instructions in the 226 // trace. It maps `instruction index -> error message`. 227 llvm::DenseMap<uint64_t, std::string> m_errors; 228 /// The size in bytes of the raw buffer before decoding. It might be None if 229 /// the decoding failed. 230 llvm::Optional<size_t> m_raw_trace_size; 231 /// All occurrences of libipt errors when decoding TSCs. 232 LibiptErrors m_tsc_errors; 233 /// Total amount of time spent decoding. 234 std::chrono::milliseconds m_total_decoding_time{0}; 235 }; 236 237 using DecodedThreadSP = std::shared_ptr<DecodedThread>; 238 239 } // namespace trace_intel_pt 240 } // namespace lldb_private 241 242 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 243