1 //===-- LibiptDecoder.cpp --======-----------------------------------------===// 2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 3 // See https://llvm.org/LICENSE.txt for license information. 4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 5 // 6 //===----------------------------------------------------------------------===// 7 8 #include "LibiptDecoder.h" 9 10 #include "TraceIntelPT.h" 11 12 #include "lldb/Target/Process.h" 13 14 using namespace lldb; 15 using namespace lldb_private; 16 using namespace lldb_private::trace_intel_pt; 17 using namespace llvm; 18 19 // Simple struct used by the decoder to keep the state of the most 20 // recent TSC and a flag indicating whether TSCs are enabled, not enabled 21 // or we just don't yet. 22 struct TscInfo { 23 uint64_t tsc = 0; 24 LazyBool has_tsc = eLazyBoolCalculate; 25 26 explicit operator bool() const { return has_tsc == eLazyBoolYes; } 27 }; 28 29 static inline bool IsLibiptError(int libipt_status) { 30 return libipt_status < 0; 31 } 32 33 static inline bool IsEndOfStream(int libipt_status) { 34 return libipt_status == -pte_eos; 35 } 36 37 static inline bool IsTscUnavailable(int libipt_status) { 38 return libipt_status == -pte_no_time; 39 } 40 41 /// Class that decodes a raw buffer for a single thread using the low level 42 /// libipt library. 43 /// 44 /// Throughout this code, the status of the decoder will be used to identify 45 /// events needed to be processed or errors in the decoder. The values can be 46 /// - negative: actual errors 47 /// - positive or zero: not an error, but a list of bits signaling the status 48 /// of the decoder, e.g. whether there are events that need to be decoded or 49 /// not. 50 class LibiptDecoder { 51 public: 52 /// \param[in] decoder 53 /// A well configured decoder. Using the current state of that decoder, 54 /// decoding will start at its next valid PSB. It's not assumed that the 55 /// decoder is already pointing at a valid PSB. 56 /// 57 /// \param[in] decoded_thread 58 /// A \a DecodedThread object where the decoded instructions will be 59 /// appended to. It might have already some instructions. 60 LibiptDecoder(pt_insn_decoder &decoder, DecodedThread &decoded_thread) 61 : m_decoder(decoder), m_decoded_thread(decoded_thread) {} 62 63 /// Decode all the instructions until the end of the trace. 64 /// The decoding flow is based on 65 /// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop 66 /// but with some relaxation to allow for gaps in the trace. 67 void DecodeUntilEndOfTrace() { 68 int status = pte_ok; 69 while (!IsLibiptError(status = FindNextSynchronizationPoint())) { 70 // We have synchronized, so we can start decoding instructions and 71 // events. 72 // Multiple loops indicate gaps in the trace. 73 DecodeInstructionsAndEvents(status); 74 } 75 } 76 77 private: 78 /// Decode all the instructions and events until an error is found or the end 79 /// of the trace is reached. 80 /// 81 /// \param[in] status 82 /// The status that was result of synchronizing to the most recent PSB. 83 void DecodeInstructionsAndEvents(int status) { 84 pt_insn insn; 85 while (ProcessPTEvents(status)) { 86 status = pt_insn_next(&m_decoder, &insn, sizeof(insn)); 87 // The status returned by pt_insn_next will need to be processed by 88 // ProcessPTEvents in the next loop. 89 if (FoundErrors(status, insn.ip)) 90 break; 91 AppendInstruction(insn); 92 } 93 } 94 95 /// Move the decoder forward to the next synchronization point (i.e. next PSB 96 /// packet). 97 /// 98 /// Once the decoder is at that synchronization point, it can start decoding 99 /// instructions. 100 /// 101 /// \return 102 /// The libipt decoder status after moving to the next PSB. Negative if 103 /// no PSB was found. 104 int FindNextSynchronizationPoint() { 105 // Try to sync the decoder. If it fails, then get the decoder_offset and 106 // try to sync again from the next synchronization point. If the 107 // new_decoder_offset is same as decoder_offset then we can't move to the 108 // next synchronization point. Otherwise, keep resyncing until either end 109 // of trace stream (eos) is reached or pt_insn_sync_forward() passes. 110 int status = pt_insn_sync_forward(&m_decoder); 111 112 if (!IsEndOfStream(status) && IsLibiptError(status)) { 113 uint64_t decoder_offset = 0; 114 int errcode_off = pt_insn_get_offset(&m_decoder, &decoder_offset); 115 if (!IsLibiptError(errcode_off)) { // we could get the offset 116 while (true) { 117 status = pt_insn_sync_forward(&m_decoder); 118 if (!IsLibiptError(status) || IsEndOfStream(status)) 119 break; 120 121 uint64_t new_decoder_offset = 0; 122 errcode_off = pt_insn_get_offset(&m_decoder, &new_decoder_offset); 123 if (IsLibiptError(errcode_off)) 124 break; // We can't further synchronize. 125 else if (new_decoder_offset <= decoder_offset) { 126 // We tried resyncing the decoder and it didn't make any progress 127 // because the offset didn't change. We will not make any further 128 // progress. Hence, we stop in this situation. 129 break; 130 } 131 // We'll try again starting from a new offset. 132 decoder_offset = new_decoder_offset; 133 } 134 } 135 } 136 137 // We make this call to record any synchronization errors. 138 FoundErrors(status); 139 return status; 140 } 141 142 /// Before querying instructions, we need to query the events associated that 143 /// instruction e.g. timing events like ptev_tick, or paging events like 144 /// ptev_paging. 145 /// 146 /// \return 147 /// \b true if we could process the events, \b false if errors were found. 148 bool ProcessPTEvents(int status) { 149 while (status & pts_event_pending) { 150 pt_event event; 151 status = pt_insn_event(&m_decoder, &event, sizeof(event)); 152 if (IsLibiptError(status)) 153 break; 154 } 155 156 // We refresh the TSC that might have changed after processing the events. 157 // See 158 // https://github.com/intel/libipt/blob/master/doc/man/pt_evt_next.3.md 159 RefreshTscInfo(); 160 return !FoundErrors(status); 161 } 162 163 /// Query the decoder for the most recent TSC timestamp and update 164 /// the inner tsc information accordingly. 165 void RefreshTscInfo() { 166 if (m_tsc_info.has_tsc == eLazyBoolNo) 167 return; 168 169 uint64_t new_tsc; 170 int tsc_status; 171 if (IsLibiptError(tsc_status = pt_insn_time(&m_decoder, &new_tsc, nullptr, 172 nullptr))) { 173 if (IsTscUnavailable(tsc_status)) { 174 // We now know that the trace doesn't support TSC, so we won't try 175 // again. 176 // See 177 // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_time.3.md 178 m_tsc_info.has_tsc = eLazyBoolNo; 179 } else { 180 // We don't add TSC decoding errors in the decoded trace itself to 181 // prevent creating unnecessary gaps, but we can count how many of 182 // these errors happened. In this case we reuse the previous correct 183 // TSC we saw, as it's better than no TSC at all. 184 m_decoded_thread.RecordTscError(tsc_status); 185 } 186 } else { 187 m_tsc_info.tsc = new_tsc; 188 m_tsc_info.has_tsc = eLazyBoolYes; 189 } 190 } 191 192 /// Check if the given libipt status signals any errors. If errors were found, 193 /// they will be recorded in the decoded trace. 194 /// 195 /// \param[in] ip 196 /// An optional ip address can be passed if the error is associated with 197 /// the decoding of a specific instruction. 198 /// 199 /// \return 200 /// \b true if errors were found, \b false otherwise. 201 bool FoundErrors(int status, lldb::addr_t ip = LLDB_INVALID_ADDRESS) { 202 if (!IsLibiptError(status)) 203 return false; 204 205 // We signal a gap only if it's not "end of stream", as that's not a proper 206 // error. 207 if (!IsEndOfStream(status)) { 208 if (m_tsc_info) { 209 m_decoded_thread.AppendError(make_error<IntelPTError>(status, ip), 210 m_tsc_info.tsc); 211 } else { 212 m_decoded_thread.AppendError(make_error<IntelPTError>(status, ip)); 213 } 214 } 215 return true; 216 } 217 218 void AppendInstruction(const pt_insn &insn) { 219 if (m_tsc_info) 220 m_decoded_thread.AppendInstruction(insn, m_tsc_info.tsc); 221 else 222 m_decoded_thread.AppendInstruction(insn); 223 } 224 225 private: 226 pt_insn_decoder &m_decoder; 227 DecodedThread &m_decoded_thread; 228 TscInfo m_tsc_info; 229 }; 230 231 /// Callback used by libipt for reading the process memory. 232 /// 233 /// More information can be found in 234 /// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md 235 static int ReadProcessMemory(uint8_t *buffer, size_t size, 236 const pt_asid * /* unused */, uint64_t pc, 237 void *context) { 238 Process *process = static_cast<Process *>(context); 239 240 Status error; 241 int bytes_read = process->ReadMemory(pc, buffer, size, error); 242 if (error.Fail()) 243 return -pte_nomap; 244 return bytes_read; 245 } 246 247 // RAII deleter for libipt's decoder 248 auto DecoderDeleter = [](pt_insn_decoder *decoder) { 249 pt_insn_free_decoder(decoder); 250 }; 251 252 using PtInsnDecoderUP = 253 std::unique_ptr<pt_insn_decoder, decltype(DecoderDeleter)>; 254 255 static Expected<PtInsnDecoderUP> 256 CreateInstructionDecoder(DecodedThread &decoded_thread, 257 TraceIntelPT &trace_intel_pt, 258 ArrayRef<uint8_t> buffer) { 259 Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo(); 260 if (!cpu_info) 261 return cpu_info.takeError(); 262 263 pt_config config; 264 pt_config_init(&config); 265 config.cpu = *cpu_info; 266 int status = pte_ok; 267 268 if (IsLibiptError(status = pt_cpu_errata(&config.errata, &config.cpu))) 269 return make_error<IntelPTError>(status); 270 271 // The libipt library does not modify the trace buffer, hence the 272 // following casts are safe. 273 config.begin = const_cast<uint8_t *>(buffer.data()); 274 config.end = const_cast<uint8_t *>(buffer.data() + buffer.size()); 275 276 pt_insn_decoder *decoder_ptr = pt_insn_alloc_decoder(&config); 277 if (!decoder_ptr) 278 return make_error<IntelPTError>(-pte_nomem); 279 PtInsnDecoderUP decoder_up(decoder_ptr, DecoderDeleter); 280 281 pt_image *image = pt_insn_get_image(decoder_ptr); 282 Process *process = decoded_thread.GetThread()->GetProcess().get(); 283 284 if (IsLibiptError( 285 status = pt_image_set_callback(image, ReadProcessMemory, process))) 286 return make_error<IntelPTError>(status); 287 return decoder_up; 288 } 289 290 void lldb_private::trace_intel_pt::DecodeTrace(DecodedThread &decoded_thread, 291 TraceIntelPT &trace_intel_pt, 292 ArrayRef<uint8_t> buffer) { 293 decoded_thread.SetRawTraceSize(buffer.size()); 294 295 Expected<PtInsnDecoderUP> decoder_up = 296 CreateInstructionDecoder(decoded_thread, trace_intel_pt, buffer); 297 if (!decoder_up) 298 return decoded_thread.AppendError(decoder_up.takeError()); 299 300 LibiptDecoder libipt_decoder(*decoder_up.get(), decoded_thread); 301 libipt_decoder.DecodeUntilEndOfTrace(); 302 } 303