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