1 //===-- DecodedThread.h -----------------------------------------*- 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 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
10 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
11 
12 #include "intel-pt.h"
13 #include "lldb/Target/Trace.h"
14 #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
15 #include "llvm/Support/Errc.h"
16 #include "llvm/Support/Error.h"
17 #include <optional>
18 #include <utility>
19 #include <vector>
20 
21 namespace lldb_private {
22 namespace trace_intel_pt {
23 
24 /// Class for representing a libipt decoding error.
25 class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
26 public:
27   static char ID;
28 
29   /// \param[in] libipt_error_code
30   ///     Negative number returned by libipt when decoding the trace and
31   ///     signaling errors.
32   ///
33   /// \param[in] address
34   ///     Optional instruction address. When decoding an individual instruction,
35   ///     its address might be available in the \a pt_insn object, and should be
36   ///     passed to this constructor. Other errors don't have an associated
37   ///     address.
38   IntelPTError(int libipt_error_code,
39                lldb::addr_t address = LLDB_INVALID_ADDRESS);
40 
convertToErrorCode()41   std::error_code convertToErrorCode() const override {
42     return llvm::errc::not_supported;
43   }
44 
GetLibiptErrorCode()45   int GetLibiptErrorCode() const { return m_libipt_error_code; }
46 
47   void log(llvm::raw_ostream &OS) const override;
48 
49 private:
50   int m_libipt_error_code;
51   lldb::addr_t m_address;
52 };
53 
54 /// \class DecodedThread
55 /// Class holding the instructions and function call hierarchy obtained from
56 /// decoding a trace, as well as a position cursor used when reverse debugging
57 /// the trace.
58 ///
59 /// Each decoded thread contains a cursor to the current position the user is
60 /// stopped at. See \a Trace::GetCursorPosition for more information.
61 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
62 public:
63   using TSC = uint64_t;
64 
65   /// A structure that represents a maximal range of trace items associated to
66   /// the same TSC value.
67   struct TSCRange {
68     TSC tsc;
69     /// Number of trace items in this range.
70     uint64_t items_count;
71     /// Index of the first trace item in this range.
72     uint64_t first_item_index;
73 
74     /// \return
75     ///   \b true if and only if the given \p item_index is covered by this
76     ///   range.
77     bool InRange(uint64_t item_index) const;
78   };
79 
80   /// A structure that represents a maximal range of trace items associated to
81   /// the same non-interpolated timestamps in nanoseconds.
82   struct NanosecondsRange {
83     /// The nanoseconds value for this range.
84     uint64_t nanos;
85     /// The corresponding TSC value for this range.
86     TSC tsc;
87     /// A nullable pointer to the next range.
88     NanosecondsRange *next_range;
89     /// Number of trace items in this range.
90     uint64_t items_count;
91     /// Index of the first trace item in this range.
92     uint64_t first_item_index;
93 
94     /// Calculate an interpolated timestamp in nanoseconds for the given item
95     /// index. It's guaranteed that two different item indices will produce
96     /// different interpolated values.
97     ///
98     /// \param[in] item_index
99     ///   The index of the item whose timestamp will be estimated. It has to be
100     ///   part of this range.
101     ///
102     /// \param[in] beginning_of_time_nanos
103     ///   The timestamp at which tracing started.
104     ///
105     /// \param[in] tsc_conversion
106     ///   The tsc -> nanos conversion utility
107     ///
108     /// \return
109     ///   An interpolated timestamp value for the given trace item.
110     double
111     GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,
112                         const LinuxPerfZeroTscConversion &tsc_conversion) const;
113 
114     /// \return
115     ///   \b true if and only if the given \p item_index is covered by this
116     ///   range.
117     bool InRange(uint64_t item_index) const;
118   };
119 
120   // Struct holding counts for events
121   struct EventsStats {
122     /// A count for each individual event kind. We use an unordered map instead
123     /// of a DenseMap because DenseMap can't understand enums.
124     ///
125     /// Note: We can't use DenseMap because lldb::TraceEvent is not
126     /// automatically handled correctly by DenseMap. We'd need to implement a
127     /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for
128     /// such a simple structure.
129     std::unordered_map<lldb::TraceEvent, uint64_t> events_counts;
130     uint64_t total_count = 0;
131 
132     void RecordEvent(lldb::TraceEvent event);
133   };
134 
135   // Struct holding counts for errors
136   struct ErrorStats {
137     /// The following counters are mutually exclusive
138     /// \{
139     uint64_t other_errors = 0;
140     uint64_t fatal_errors = 0;
141     // libipt error -> count
142     llvm::DenseMap<const char *, uint64_t> libipt_errors;
143     /// \}
144 
145     uint64_t GetTotalCount() const;
146 
147     void RecordError(int libipt_error_code);
148 
149     void RecordError(bool fatal);
150   };
151 
152   DecodedThread(
153       lldb::ThreadSP thread_sp,
154       const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);
155 
156   /// Get the total number of instruction, errors and events from the decoded
157   /// trace.
158   uint64_t GetItemsCount() const;
159 
160   /// \return
161   ///   The error associated with a given trace item.
162   const char *GetErrorByIndex(uint64_t item_index) const;
163 
164   /// \return
165   ///   The trace item kind given an item index.
166   lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;
167 
168   /// \return
169   ///   The underlying event type for the given trace item index.
170   lldb::TraceEvent GetEventByIndex(int item_index) const;
171 
172   /// Get the most recent CPU id before or at the given trace item index.
173   ///
174   /// \param[in] item_index
175   ///   The trace item index to compare with.
176   ///
177   /// \return
178   ///   The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.
179   lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;
180 
181   /// \return
182   ///   The PSB offset associated with the given item index.
183   lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;
184 
185   /// Get a maximal range of trace items that include the given \p item_index
186   /// that have the same TSC value.
187   ///
188   /// \param[in] item_index
189   ///   The trace item index to compare with.
190   ///
191   /// \return
192   ///   The requested TSC range, or \a std::nullopt if not available.
193   std::optional<DecodedThread::TSCRange>
194   GetTSCRangeByIndex(uint64_t item_index) const;
195 
196   /// Get a maximal range of trace items that include the given \p item_index
197   /// that have the same nanoseconds timestamp without interpolation.
198   ///
199   /// \param[in] item_index
200   ///   The trace item index to compare with.
201   ///
202   /// \return
203   ///   The requested nanoseconds range, or \a std::nullopt if not available.
204   std::optional<DecodedThread::NanosecondsRange>
205   GetNanosecondsRangeByIndex(uint64_t item_index);
206 
207   /// \return
208   ///     The load address of the instruction at the given index.
209   lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;
210 
211   /// \return
212   ///     The number of instructions in this trace (not trace items).
213   uint64_t GetTotalInstructionCount() const;
214 
215   /// Return an object with statistics of the trace events that happened.
216   ///
217   /// \return
218   ///   The stats object of all the events.
219   const EventsStats &GetEventsStats() const;
220 
221   /// Return an object with statistics of the trace errors that happened.
222   ///
223   /// \return
224   ///   The stats object of all the events.
225   const ErrorStats &GetErrorStats() const;
226 
227   /// The approximate size in bytes used by this instance,
228   /// including all the already decoded instructions.
229   size_t CalculateApproximateMemoryUsage() const;
230 
231   lldb::ThreadSP GetThread();
232 
233   /// Notify this object that a new tsc has been seen.
234   /// If this a new TSC, an event will be created.
235   void NotifyTsc(TSC tsc);
236 
237   /// Notify this object that a CPU has been seen.
238   /// If this a new CPU, an event will be created.
239   void NotifyCPU(lldb::cpu_id_t cpu_id);
240 
241   /// Notify this object that a new PSB has been seen.
242   void NotifySyncPoint(lldb::addr_t psb_offset);
243 
244   /// Append a decoding error.
245   void AppendError(const IntelPTError &error);
246 
247   /// Append a custom decoding.
248   ///
249   /// \param[in] error
250   ///   The error message.
251   ///
252   /// \param[in] fatal
253   ///   If \b true, then the whole decoded thread should be discarded because a
254   ///   fatal anomaly has been found.
255   void AppendCustomError(llvm::StringRef error, bool fatal = false);
256 
257   /// Append an event.
258   void AppendEvent(lldb::TraceEvent);
259 
260   /// Append an instruction.
261   void AppendInstruction(const pt_insn &insn);
262 
263 private:
264   /// When adding new members to this class, make sure
265   /// to update \a CalculateApproximateMemoryUsage() accordingly.
266   lldb::ThreadSP m_thread_sp;
267 
268   /// We use a union to optimize the memory usage for the different kinds of
269   /// trace items.
270   union TraceItemStorage {
271     /// The load addresses of this item if it's an instruction.
272     uint64_t load_address;
273 
274     /// The event kind of this item if it's an event
275     lldb::TraceEvent event;
276 
277     /// The string message of this item if it's an error
278     const char *error;
279   };
280 
281   /// Create a new trace item.
282   ///
283   /// \return
284   ///   The index of the new item.
285   DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind);
286 
287   /// Most of the trace data is stored here.
288   std::vector<TraceItemStorage> m_item_data;
289   /// The TraceItemKind for each trace item encoded as uint8_t. We don't include
290   /// it in TraceItemStorage to avoid padding.
291   std::vector<uint8_t> m_item_kinds;
292 
293   /// This map contains the TSCs of the decoded trace items. It maps
294   /// `item index -> TSC`, where `item index` is the first index
295   /// at which the mapped TSC first appears. We use this representation because
296   /// TSCs are sporadic and we can think of them as ranges.
297   std::map<uint64_t, TSCRange> m_tscs;
298   /// This is the chronologically last TSC that has been added.
299   std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =
300       std::nullopt;
301   /// This map contains the non-interpolated nanoseconds timestamps of the
302   /// decoded trace items. It maps `item index -> nanoseconds`, where `item
303   /// index` is the first index at which the mapped nanoseconds first appears.
304   /// We use this representation because timestamps are sporadic and we think of
305   /// them as ranges.
306   std::map<uint64_t, NanosecondsRange> m_nanoseconds;
307   std::optional<std::map<uint64_t, NanosecondsRange>::iterator>
308       m_last_nanoseconds = std::nullopt;
309 
310   // The cpu information is stored as a map. It maps `item index -> CPU`.
311   // A CPU is associated with the next instructions that follow until the next
312   // cpu is seen.
313   std::map<uint64_t, lldb::cpu_id_t> m_cpus;
314   /// This is the chronologically last CPU ID.
315   std::optional<uint64_t> m_last_cpu;
316 
317   // The PSB offsets are stored as a map. It maps `item index -> psb offset`.
318   llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets;
319 
320   /// TSC -> nanos conversion utility.
321   std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
322 
323   /// Statistics of all tracing errors.
324   ErrorStats m_error_stats;
325 
326   /// Statistics of all tracing events.
327   EventsStats m_events_stats;
328   /// Total amount of time spent decoding.
329   std::chrono::milliseconds m_total_decoding_time{0};
330 
331   /// Total number of instructions in the trace.
332   uint64_t m_insn_count = 0;
333 };
334 
335 using DecodedThreadSP = std::shared_ptr<DecodedThread>;
336 
337 } // namespace trace_intel_pt
338 } // namespace lldb_private
339 
340 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
341