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