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.
FindNextSynchronizationPoint(pt_insn_decoder & decoder)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.
ProcessPTEvents(pt_insn_decoder & decoder,int errcode)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>
DecodeInstructions(pt_insn_decoder & decoder)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
ReadProcessMemory(uint8_t * buffer,size_t size,const pt_asid *,uint64_t pc,void * context)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>>
DecodeInMemoryTrace(Process & process,TraceIntelPT & trace_intel_pt,MutableArrayRef<uint8_t> buffer)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>>
DecodeTraceFile(Process & process,TraceIntelPT & trace_intel_pt,const FileSpec & trace_file,size_t & raw_trace_size)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>>
DecodeLiveThread(Thread & thread,TraceIntelPT & trace,size_t & raw_trace_size)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
Decode()244 DecodedThreadSP ThreadDecoder::Decode() {
245 if (!m_decoded_thread.hasValue())
246 m_decoded_thread = DoDecode();
247 return *m_decoded_thread;
248 }
249
PostMortemThreadDecoder(const lldb::ThreadPostMortemTraceSP & trace_thread,TraceIntelPT & trace)250 PostMortemThreadDecoder::PostMortemThreadDecoder(
251 const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
252 : m_trace_thread(trace_thread), m_trace(trace) {}
253
DoDecode()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
LiveThreadDecoder(Thread & thread,TraceIntelPT & trace)267 LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
268 : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
269
DoDecode()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