1 //===-- IntelPTDecoder.cpp --------------------------------------*- 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 #include "IntelPTDecoder.h" 10 11 #include "llvm/Support/MemoryBuffer.h" 12 13 #include "lldb/Core/Module.h" 14 #include "lldb/Core/Section.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Target/ThreadTrace.h" 17 18 using namespace lldb; 19 using namespace lldb_private; 20 using namespace lldb_private::trace_intel_pt; 21 using namespace llvm; 22 23 /// Move the decoder forward to the next synchronization point (i.e. next PSB 24 /// packet). 25 /// 26 /// Once the decoder is at that sync. point, it can start decoding instructions. 27 /// 28 /// \return 29 /// A negative number with the libipt error if we couldn't synchronize. 30 /// Otherwise, a positive number with the synchronization status will be 31 /// returned. 32 static int FindNextSynchronizationPoint(pt_insn_decoder &decoder) { 33 // Try to sync the decoder. If it fails, then get 34 // the decoder_offset and try to sync again from 35 // the next synchronization point. If the 36 // new_decoder_offset is same as decoder_offset 37 // then we can't move to the next synchronization 38 // point. Otherwise, keep resyncing until either 39 // end of trace stream (eos) is reached or 40 // pt_insn_sync_forward() passes. 41 int errcode = pt_insn_sync_forward(&decoder); 42 43 if (errcode != -pte_eos && errcode < 0) { 44 uint64_t decoder_offset = 0; 45 int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset); 46 if (errcode_off >= 0) { // we could get the offset 47 while (true) { 48 errcode = pt_insn_sync_forward(&decoder); 49 if (errcode >= 0 || errcode == -pte_eos) 50 break; 51 52 uint64_t new_decoder_offset = 0; 53 errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset); 54 if (errcode_off < 0) 55 break; // We can't further synchronize. 56 else if (new_decoder_offset <= decoder_offset) { 57 // We tried resyncing the decoder and 58 // decoder didn't make any progress because 59 // the offset didn't change. We will not 60 // make any progress further. Hence, 61 // stopping in this situation. 62 break; 63 } 64 // We'll try again starting from a new offset. 65 decoder_offset = new_decoder_offset; 66 } 67 } 68 } 69 70 return errcode; 71 } 72 73 /// Before querying instructions, we need to query the events associated that 74 /// instruction e.g. timing events like ptev_tick, or paging events like 75 /// ptev_paging. 76 /// 77 /// \return 78 /// 0 if there were no errors processing the events, or a negative libipt 79 /// error code in case of errors. 80 static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) { 81 while (errcode & pts_event_pending) { 82 pt_event event; 83 errcode = pt_insn_event(&decoder, &event, sizeof(event)); 84 if (errcode < 0) 85 return errcode; 86 } 87 return 0; 88 }; 89 90 /// Decode all the instructions from a configured decoder. 91 /// The decoding flow is based on 92 /// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop 93 /// but with some relaxation to allow for gaps in the trace. 94 /// 95 /// Error codes returned by libipt while decoding are: 96 /// - negative: actual errors 97 /// - positive or zero: not an error, but a list of bits signaling the status of 98 /// the decoder 99 /// 100 /// \param[in] decoder 101 /// A configured libipt \a pt_insn_decoder. 102 /// 103 /// \return 104 /// The decoded instructions. 105 static std::vector<IntelPTInstruction> 106 DecodeInstructions(pt_insn_decoder &decoder) { 107 std::vector<IntelPTInstruction> instructions; 108 109 while (true) { 110 int errcode = FindNextSynchronizationPoint(decoder); 111 if (errcode == -pte_eos) 112 break; 113 114 if (errcode < 0) { 115 instructions.emplace_back(make_error<IntelPTError>(errcode)); 116 break; 117 } 118 119 // We have synchronized, so we can start decoding 120 // instructions and events. 121 while (true) { 122 errcode = ProcessPTEvents(decoder, errcode); 123 if (errcode < 0) { 124 instructions.emplace_back(make_error<IntelPTError>(errcode)); 125 break; 126 } 127 pt_insn insn; 128 129 errcode = pt_insn_next(&decoder, &insn, sizeof(insn)); 130 if (errcode == -pte_eos) 131 break; 132 133 if (errcode < 0) { 134 instructions.emplace_back(make_error<IntelPTError>(errcode, insn.ip)); 135 break; 136 } 137 138 instructions.emplace_back(insn); 139 } 140 } 141 142 return instructions; 143 } 144 145 /// Callback used by libipt for reading the process memory. 146 /// 147 /// More information can be found in 148 /// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md 149 static int ReadProcessMemory(uint8_t *buffer, size_t size, 150 const pt_asid * /* unused */, uint64_t pc, 151 void *context) { 152 Process *process = static_cast<Process *>(context); 153 154 Status error; 155 int bytes_read = process->ReadMemory(pc, buffer, size, error); 156 if (error.Fail()) 157 return -pte_nomap; 158 return bytes_read; 159 } 160 161 static std::vector<IntelPTInstruction> makeInstructionListFromError(Error err) { 162 std::vector<IntelPTInstruction> instructions; 163 instructions.emplace_back(std::move(err)); 164 return instructions; 165 } 166 167 static std::vector<IntelPTInstruction> 168 CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu, 169 const FileSpec &trace_file) { 170 ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = 171 MemoryBuffer::getFile(trace_file.GetPath()); 172 if (std::error_code err = trace_or_error.getError()) 173 return makeInstructionListFromError(errorCodeToError(err)); 174 175 MemoryBuffer &trace = **trace_or_error; 176 177 pt_config config; 178 pt_config_init(&config); 179 config.cpu = pt_cpu; 180 181 if (int errcode = pt_cpu_errata(&config.errata, &config.cpu)) 182 return makeInstructionListFromError(make_error<IntelPTError>(errcode)); 183 184 // The libipt library does not modify the trace buffer, hence the following 185 // cast is safe. 186 config.begin = 187 reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())); 188 config.end = 189 reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd())); 190 191 pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config); 192 if (!decoder) 193 return makeInstructionListFromError(make_error<IntelPTError>(-pte_nomem)); 194 195 pt_image *image = pt_insn_get_image(decoder); 196 197 int errcode = pt_image_set_callback(image, ReadProcessMemory, &process); 198 assert(errcode == 0); 199 (void)errcode; 200 201 std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder); 202 203 pt_insn_free_decoder(decoder); 204 return instructions; 205 } 206 207 const DecodedThread &ThreadTraceDecoder::Decode() { 208 if (!m_decoded_thread.hasValue()) { 209 m_decoded_thread = DecodedThread( 210 CreateDecoderAndDecode(*m_trace_thread->GetProcess(), m_pt_cpu, 211 m_trace_thread->GetTraceFile())); 212 } 213 214 return *m_decoded_thread; 215 } 216