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
22namespace lldb_private {
23namespace trace_intel_pt {
24
25/// Class for representing a libipt decoding error.
26class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
27public:
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
50private:
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.
62class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
63public:
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
264private:
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
325using 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