1 //===-- IntelPTDecoder.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 "IntelPTDecoder.h" 9 10 #include "llvm/Support/MemoryBuffer.h" 11 12 #include "../common/ThreadPostMortemTrace.h" 13 #include "DecodedThread.h" 14 #include "TraceIntelPT.h" 15 #include "lldb/Core/Module.h" 16 #include "lldb/Core/Section.h" 17 #include "lldb/Target/Target.h" 18 #include "lldb/Utility/StringExtractor.h" 19 20 using namespace lldb; 21 using namespace lldb_private; 22 using namespace lldb_private::trace_intel_pt; 23 using namespace llvm; 24 25 /// Move the decoder forward to the next synchronization point (i.e. next PSB 26 /// packet). 27 /// 28 /// Once the decoder is at that sync. point, it can start decoding instructions. 29 /// 30 /// \return 31 /// A negative number with the libipt error if we couldn't synchronize. 32 /// Otherwise, a positive number with the synchronization status will be 33 /// returned. 34 static int FindNextSynchronizationPoint(pt_insn_decoder &decoder) { 35 // Try to sync the decoder. If it fails, then get 36 // the decoder_offset and try to sync again from 37 // the next synchronization point. If the 38 // new_decoder_offset is same as decoder_offset 39 // then we can't move to the next synchronization 40 // point. Otherwise, keep resyncing until either 41 // end of trace stream (eos) is reached or 42 // pt_insn_sync_forward() passes. 43 int errcode = pt_insn_sync_forward(&decoder); 44 45 if (errcode != -pte_eos && errcode < 0) { 46 uint64_t decoder_offset = 0; 47 int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset); 48 if (errcode_off >= 0) { // we could get the offset 49 while (true) { 50 errcode = pt_insn_sync_forward(&decoder); 51 if (errcode >= 0 || errcode == -pte_eos) 52 break; 53 54 uint64_t new_decoder_offset = 0; 55 errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset); 56 if (errcode_off < 0) 57 break; // We can't further synchronize. 58 else if (new_decoder_offset <= decoder_offset) { 59 // We tried resyncing the decoder and 60 // decoder didn't make any progress because 61 // the offset didn't change. We will not 62 // make any progress further. Hence, 63 // stopping in this situation. 64 break; 65 } 66 // We'll try again starting from a new offset. 67 decoder_offset = new_decoder_offset; 68 } 69 } 70 } 71 72 return errcode; 73 } 74 75 /// Before querying instructions, we need to query the events associated that 76 /// instruction e.g. timing events like ptev_tick, or paging events like 77 /// ptev_paging. 78 /// 79 /// \return 80 /// 0 if there were no errors processing the events, or a negative libipt 81 /// error code in case of errors. 82 static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) { 83 while (errcode & pts_event_pending) { 84 pt_event event; 85 errcode = pt_insn_event(&decoder, &event, sizeof(event)); 86 if (errcode < 0) 87 return errcode; 88 } 89 return 0; 90 } 91 92 /// Decode all the instructions from a configured decoder. 93 /// The decoding flow is based on 94 /// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop 95 /// but with some relaxation to allow for gaps in the trace. 96 /// 97 /// Error codes returned by libipt while decoding are: 98 /// - negative: actual errors 99 /// - positive or zero: not an error, but a list of bits signaling the status of 100 /// the decoder 101 /// 102 /// \param[in] decoder 103 /// A configured libipt \a pt_insn_decoder. 104 /// 105 /// \return 106 /// The decoded instructions. 107 static std::vector<IntelPTInstruction> 108 DecodeInstructions(pt_insn_decoder &decoder) { 109 std::vector<IntelPTInstruction> instructions; 110 111 while (true) { 112 int errcode = FindNextSynchronizationPoint(decoder); 113 if (errcode == -pte_eos) 114 break; 115 116 if (errcode < 0) { 117 instructions.emplace_back(make_error<IntelPTError>(errcode)); 118 break; 119 } 120 121 // We have synchronized, so we can start decoding 122 // instructions and events. 123 while (true) { 124 errcode = ProcessPTEvents(decoder, errcode); 125 if (errcode < 0) { 126 instructions.emplace_back(make_error<IntelPTError>(errcode)); 127 break; 128 } 129 pt_insn insn; 130 131 errcode = pt_insn_next(&decoder, &insn, sizeof(insn)); 132 if (errcode == -pte_eos) 133 break; 134 135 if (errcode < 0) { 136 instructions.emplace_back(make_error<IntelPTError>(errcode, insn.ip)); 137 break; 138 } 139 140 uint64_t time; 141 int time_error = pt_insn_time(&decoder, &time, nullptr, nullptr); 142 if (time_error == -pte_invalid) { 143 // This happens if we invoke the pt_insn_time method incorrectly, 144 // but the instruction is good though. 145 instructions.emplace_back( 146 make_error<IntelPTError>(time_error, insn.ip)); 147 instructions.emplace_back(insn); 148 break; 149 } 150 if (time_error == -pte_no_time) { 151 // We simply don't have time information, i.e. None of TSC, MTC or CYC 152 // was enabled. 153 instructions.emplace_back(insn); 154 } else { 155 instructions.emplace_back(insn, time); 156 } 157 } 158 } 159 160 return instructions; 161 } 162 163 /// Callback used by libipt for reading the process memory. 164 /// 165 /// More information can be found in 166 /// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md 167 static int ReadProcessMemory(uint8_t *buffer, size_t size, 168 const pt_asid * /* unused */, uint64_t pc, 169 void *context) { 170 Process *process = static_cast<Process *>(context); 171 172 Status error; 173 int bytes_read = process->ReadMemory(pc, buffer, size, error); 174 if (error.Fail()) 175 return -pte_nomap; 176 return bytes_read; 177 } 178 179 static Expected<std::vector<IntelPTInstruction>> 180 DecodeInMemoryTrace(Process &process, TraceIntelPT &trace_intel_pt, 181 MutableArrayRef<uint8_t> buffer) { 182 Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo(); 183 if (!cpu_info) 184 return cpu_info.takeError(); 185 186 pt_config config; 187 pt_config_init(&config); 188 config.cpu = *cpu_info; 189 190 if (int errcode = pt_cpu_errata(&config.errata, &config.cpu)) 191 return make_error<IntelPTError>(errcode); 192 193 config.begin = buffer.data(); 194 config.end = buffer.data() + buffer.size(); 195 196 pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config); 197 if (!decoder) 198 return make_error<IntelPTError>(-pte_nomem); 199 200 pt_image *image = pt_insn_get_image(decoder); 201 202 int errcode = pt_image_set_callback(image, ReadProcessMemory, &process); 203 assert(errcode == 0); 204 (void)errcode; 205 206 std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder); 207 208 pt_insn_free_decoder(decoder); 209 return instructions; 210 } 211 212 static Expected<std::vector<IntelPTInstruction>> 213 DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt, 214 const FileSpec &trace_file, size_t &raw_trace_size) { 215 ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = 216 MemoryBuffer::getFile(trace_file.GetPath()); 217 if (std::error_code err = trace_or_error.getError()) 218 return errorCodeToError(err); 219 220 MemoryBuffer &trace = **trace_or_error; 221 MutableArrayRef<uint8_t> trace_data( 222 // The libipt library does not modify the trace buffer, hence the 223 // following cast is safe. 224 reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())), 225 trace.getBufferSize()); 226 raw_trace_size = trace_data.size(); 227 return DecodeInMemoryTrace(process, trace_intel_pt, trace_data); 228 } 229 230 static Expected<std::vector<IntelPTInstruction>> 231 DecodeLiveThread(Thread &thread, TraceIntelPT &trace, size_t &raw_trace_size) { 232 Expected<std::vector<uint8_t>> buffer = 233 trace.GetLiveThreadBuffer(thread.GetID()); 234 if (!buffer) 235 return buffer.takeError(); 236 raw_trace_size = buffer->size(); 237 if (Expected<pt_cpu> cpu_info = trace.GetCPUInfo()) 238 return DecodeInMemoryTrace(*thread.GetProcess(), trace, 239 MutableArrayRef<uint8_t>(*buffer)); 240 else 241 return cpu_info.takeError(); 242 } 243 244 DecodedThreadSP ThreadDecoder::Decode() { 245 if (!m_decoded_thread.hasValue()) 246 m_decoded_thread = DoDecode(); 247 return *m_decoded_thread; 248 } 249 250 PostMortemThreadDecoder::PostMortemThreadDecoder( 251 const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace) 252 : m_trace_thread(trace_thread), m_trace(trace) {} 253 254 DecodedThreadSP PostMortemThreadDecoder::DoDecode() { 255 size_t raw_trace_size = 0; 256 if (Expected<std::vector<IntelPTInstruction>> instructions = 257 DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace, 258 m_trace_thread->GetTraceFile(), raw_trace_size)) 259 return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(), 260 std::move(*instructions), 261 raw_trace_size); 262 else 263 return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(), 264 instructions.takeError()); 265 } 266 267 LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace) 268 : m_thread_sp(thread.shared_from_this()), m_trace(trace) {} 269 270 DecodedThreadSP LiveThreadDecoder::DoDecode() { 271 size_t raw_trace_size = 0; 272 if (Expected<std::vector<IntelPTInstruction>> instructions = 273 DecodeLiveThread(*m_thread_sp, m_trace, raw_trace_size)) 274 return std::make_shared<DecodedThread>( 275 m_thread_sp, std::move(*instructions), raw_trace_size); 276 else 277 return std::make_shared<DecodedThread>(m_thread_sp, 278 instructions.takeError()); 279 } 280