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 /// Set the size in bytes of the corresponding Intel PT raw trace. 179 void SetRawTraceSize(size_t size); 180 181 /// Get the size in bytes of the corresponding Intel PT raw trace. 182 /// 183 /// \return 184 /// The size of the trace, or \b llvm::None if not available. 185 llvm::Optional<size_t> GetRawTraceSize() const; 186 187 /// Return the number of TSC decoding errors that happened. A TSC error 188 /// is not a fatal error and doesn't create gaps in the trace. Instead 189 /// we only keep track of them as a statistic. 190 /// 191 /// \return 192 /// The number of TSC decoding errors. 193 const LibiptErrors &GetTscErrors() const; 194 195 /// Record an error decoding a TSC timestamp. 196 /// 197 /// See \a GetTscErrors() for more documentation. 198 /// 199 /// \param[in] libipt_error_code 200 /// An error returned by the libipt library. 201 void RecordTscError(int libipt_error_code); 202 203 /// The approximate size in bytes used by this instance, 204 /// including all the already decoded instructions. 205 size_t CalculateApproximateMemoryUsage() const; 206 207 lldb::ThreadSP GetThread(); 208 209 private: 210 /// Notify this class that the last added instruction or error has 211 /// an associated TSC. 212 void RecordTscForLastInstruction(uint64_t tsc); 213 214 /// When adding new members to this class, make sure 215 /// to update \a CalculateApproximateMemoryUsage() accordingly. 216 lldb::ThreadSP m_thread_sp; 217 /// The low level storage of all instruction addresses. Each instruction has 218 /// an index in this vector and it will be used in other parts of the code. 219 std::vector<lldb::addr_t> m_instruction_ips; 220 /// The size in bytes of each instruction. 221 std::vector<uint8_t> m_instruction_sizes; 222 /// The libipt instruction class for each instruction. 223 std::vector<pt_insn_class> m_instruction_classes; 224 225 /// This map contains the TSCs of the decoded instructions. It maps 226 /// `instruction index -> TSC`, where `instruction index` is the first index 227 /// at which the mapped TSC appears. We use this representation because TSCs 228 /// are sporadic and we can think of them as ranges. If TSCs are present in 229 /// the trace, all instructions will have an associated TSC, including the 230 /// first one. Otherwise, this map will be empty. 231 std::map<size_t, uint64_t> m_instruction_timestamps; 232 /// This is the chronologically last TSC that has been added. 233 llvm::Optional<uint64_t> m_last_tsc = llvm::None; 234 // This variables stores the messages of all the error instructions in the 235 // trace. It maps `instruction index -> error message`. 236 llvm::DenseMap<uint64_t, std::string> m_errors; 237 /// The size in bytes of the raw buffer before decoding. It might be None if 238 /// the decoding failed. 239 llvm::Optional<size_t> m_raw_trace_size; 240 /// All occurrences of libipt errors when decoding TSCs. 241 LibiptErrors m_tsc_errors; 242 }; 243 244 using DecodedThreadSP = std::shared_ptr<DecodedThread>; 245 246 } // namespace trace_intel_pt 247 } // namespace lldb_private 248 249 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H 250