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