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
21namespace lldb_private {
22namespace trace_intel_pt {
23
24/// Class for representing a libipt decoding error.
25class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
26public:
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
41  std::error_code convertToErrorCode() const override {
42    return llvm::errc::not_supported;
43  }
44
45  int GetLibiptErrorCode() const { return m_libipt_error_code; }
46
47  void log(llvm::raw_ostream &OS) const override;
48
49private:
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.
61class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
62public:
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
263private:
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
335using 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