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