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