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