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); 75 76 /// Get the next range chronologically. 77 llvm::Optional<TscRange> Next(); 78 79 /// Get the previous range chronologically. 80 llvm::Optional<TscRange> Prev(); 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 llvm::Optional<TscRange> CalculateTscRange(size_t insn_index) const; 154 155 /// Check if an instruction given by its index is an error. 156 bool IsInstructionAnError(size_t insn_idx) const; 157 158 /// Get the error associated with a given instruction index. 159 /// 160 /// \return 161 /// The error message of \b nullptr if the given index 162 /// points to a valid instruction. 163 const char *GetErrorByInstructionIndex(size_t ins_idx); 164 165 /// Get a new cursor for the decoded thread. 166 lldb::TraceCursorUP GetCursor(); 167 168 /// Set the size in bytes of the corresponding Intel PT raw trace. 169 void SetRawTraceSize(size_t size); 170 171 /// Get the size in bytes of the corresponding Intel PT raw trace. 172 /// 173 /// \return 174 /// The size of the trace, or \b llvm::None if not available. 175 llvm::Optional<size_t> GetRawTraceSize() const; 176 177 /// Return the number of TSC decoding errors that happened. A TSC error 178 /// is not a fatal error and doesn't create gaps in the trace. Instead 179 /// we only keep track of them as a statistic. 180 /// 181 /// \return 182 /// The number of TSC decoding errors. 183 const LibiptErrors &GetTscErrors() const; 184 185 /// Record an error decoding a TSC timestamp. 186 /// 187 /// See \a GetTscErrors() for more documentation. 188 /// 189 /// \param[in] libipt_error_code 190 /// An error returned by the libipt library. 191 void RecordTscError(int libipt_error_code); 192 193 /// The approximate size in bytes used by this instance, 194 /// including all the already decoded instructions. 195 size_t CalculateApproximateMemoryUsage() const; 196 197 lldb::ThreadSP GetThread(); 198 199 private: 200 /// Notify this class that the last added instruction or error has 201 /// an associated TSC. 202 void RecordTscForLastInstruction(uint64_t tsc); 203 204 /// When adding new members to this class, make sure 205 /// to update \a CalculateApproximateMemoryUsage() accordingly. 206 lldb::ThreadSP m_thread_sp; 207 /// The low level storage of all instruction addresses. Each instruction has 208 /// an index in this vector and it will be used in other parts of the code. 209 std::vector<lldb::addr_t> m_instruction_ips; 210 /// The size in bytes of each instruction. 211 std::vector<uint8_t> m_instruction_sizes; 212 /// The libipt instruction class for each instruction. 213 std::vector<pt_insn_class> m_instruction_classes; 214 215 /// This map contains the TSCs of the decoded instructions. It maps 216 /// `instruction index -> TSC`, where `instruction index` is the first index 217 /// at which the mapped TSC appears. We use this representation because TSCs 218 /// are sporadic and we can think of them as ranges. If TSCs are present in 219 /// the trace, all instructions will have an associated TSC, including the 220 /// first one. Otherwise, this map will be empty. 221 std::map<size_t, uint64_t> m_instruction_timestamps; 222 /// This is the chronologically last TSC that has been added. 223 llvm::Optional<uint64_t> m_last_tsc = llvm::None; 224 // This variables stores the messages of all the error instructions in the 225 // trace. It maps `instruction index -> error message`. 226 llvm::DenseMap<uint64_t, std::string> m_errors; 227 /// The size in bytes of the raw buffer before decoding. It might be None if 228 /// the decoding failed. 229 llvm::Optional<size_t> m_raw_trace_size; 230 /// All occurrences of libipt errors when decoding TSCs. 231 LibiptErrors m_tsc_errors; 232 }; 233 234 using DecodedThreadSP = std::shared_ptr<DecodedThread>; 235 236 } // namespace trace_intel_pt 237 } // namespace lldb_private 238 239 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 240