File: DecodedThread.h

package info (click to toggle)
llvm-toolchain-17 1%3A17.0.6-22
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,799,624 kB
  • sloc: cpp: 6,428,607; ansic: 1,383,196; asm: 793,408; python: 223,504; objc: 75,364; f90: 60,502; lisp: 33,869; pascal: 15,282; sh: 9,684; perl: 7,453; ml: 4,937; awk: 3,523; makefile: 2,889; javascript: 2,149; xml: 888; fortran: 619; cs: 573
file content (340 lines) | stat: -rw-r--r-- 11,757 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//===-- DecodedThread.h -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H

#include "intel-pt.h"
#include "lldb/Target/Trace.h"
#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include <optional>
#include <utility>
#include <vector>

namespace lldb_private {
namespace trace_intel_pt {

/// Class for representing a libipt decoding error.
class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
public:
  static char ID;

  /// \param[in] libipt_error_code
  ///     Negative number returned by libipt when decoding the trace and
  ///     signaling errors.
  ///
  /// \param[in] address
  ///     Optional instruction address. When decoding an individual instruction,
  ///     its address might be available in the \a pt_insn object, and should be
  ///     passed to this constructor. Other errors don't have an associated
  ///     address.
  IntelPTError(int libipt_error_code,
               lldb::addr_t address = LLDB_INVALID_ADDRESS);

  std::error_code convertToErrorCode() const override {
    return llvm::errc::not_supported;
  }

  int GetLibiptErrorCode() const { return m_libipt_error_code; }

  void log(llvm::raw_ostream &OS) const override;

private:
  int m_libipt_error_code;
  lldb::addr_t m_address;
};

/// \class DecodedThread
/// Class holding the instructions and function call hierarchy obtained from
/// decoding a trace, as well as a position cursor used when reverse debugging
/// the trace.
///
/// Each decoded thread contains a cursor to the current position the user is
/// stopped at. See \a Trace::GetCursorPosition for more information.
class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
public:
  using TSC = uint64_t;

  /// A structure that represents a maximal range of trace items associated to
  /// the same TSC value.
  struct TSCRange {
    TSC tsc;
    /// Number of trace items in this range.
    uint64_t items_count;
    /// Index of the first trace item in this range.
    uint64_t first_item_index;

    /// \return
    ///   \b true if and only if the given \p item_index is covered by this
    ///   range.
    bool InRange(uint64_t item_index) const;
  };

  /// A structure that represents a maximal range of trace items associated to
  /// the same non-interpolated timestamps in nanoseconds.
  struct NanosecondsRange {
    /// The nanoseconds value for this range.
    uint64_t nanos;
    /// The corresponding TSC value for this range.
    TSC tsc;
    /// A nullable pointer to the next range.
    NanosecondsRange *next_range;
    /// Number of trace items in this range.
    uint64_t items_count;
    /// Index of the first trace item in this range.
    uint64_t first_item_index;

    /// Calculate an interpolated timestamp in nanoseconds for the given item
    /// index. It's guaranteed that two different item indices will produce
    /// different interpolated values.
    ///
    /// \param[in] item_index
    ///   The index of the item whose timestamp will be estimated. It has to be
    ///   part of this range.
    ///
    /// \param[in] beginning_of_time_nanos
    ///   The timestamp at which tracing started.
    ///
    /// \param[in] tsc_conversion
    ///   The tsc -> nanos conversion utility
    ///
    /// \return
    ///   An interpolated timestamp value for the given trace item.
    double
    GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,
                        const LinuxPerfZeroTscConversion &tsc_conversion) const;

    /// \return
    ///   \b true if and only if the given \p item_index is covered by this
    ///   range.
    bool InRange(uint64_t item_index) const;
  };

  // Struct holding counts for events
  struct EventsStats {
    /// A count for each individual event kind. We use an unordered map instead
    /// of a DenseMap because DenseMap can't understand enums.
    ///
    /// Note: We can't use DenseMap because lldb::TraceEvent is not
    /// automatically handled correctly by DenseMap. We'd need to implement a
    /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for
    /// such a simple structure.
    std::unordered_map<lldb::TraceEvent, uint64_t> events_counts;
    uint64_t total_count = 0;

    void RecordEvent(lldb::TraceEvent event);
  };

  // Struct holding counts for errors
  struct ErrorStats {
    /// The following counters are mutually exclusive
    /// \{
    uint64_t other_errors = 0;
    uint64_t fatal_errors = 0;
    // libipt error -> count
    llvm::DenseMap<const char *, uint64_t> libipt_errors;
    /// \}

    uint64_t GetTotalCount() const;

    void RecordError(int libipt_error_code);

    void RecordError(bool fatal);
  };

  DecodedThread(
      lldb::ThreadSP thread_sp,
      const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);

  /// Get the total number of instruction, errors and events from the decoded
  /// trace.
  uint64_t GetItemsCount() const;

  /// \return
  ///   The error associated with a given trace item.
  llvm::StringRef GetErrorByIndex(uint64_t item_index) const;

  /// \return
  ///   The trace item kind given an item index.
  lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;

  /// \return
  ///   The underlying event type for the given trace item index.
  lldb::TraceEvent GetEventByIndex(int item_index) const;

  /// Get the most recent CPU id before or at the given trace item index.
  ///
  /// \param[in] item_index
  ///   The trace item index to compare with.
  ///
  /// \return
  ///   The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.
  lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;

  /// \return
  ///   The PSB offset associated with the given item index.
  lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;

  /// Get a maximal range of trace items that include the given \p item_index
  /// that have the same TSC value.
  ///
  /// \param[in] item_index
  ///   The trace item index to compare with.
  ///
  /// \return
  ///   The requested TSC range, or \a std::nullopt if not available.
  std::optional<DecodedThread::TSCRange>
  GetTSCRangeByIndex(uint64_t item_index) const;

  /// Get a maximal range of trace items that include the given \p item_index
  /// that have the same nanoseconds timestamp without interpolation.
  ///
  /// \param[in] item_index
  ///   The trace item index to compare with.
  ///
  /// \return
  ///   The requested nanoseconds range, or \a std::nullopt if not available.
  std::optional<DecodedThread::NanosecondsRange>
  GetNanosecondsRangeByIndex(uint64_t item_index);

  /// \return
  ///     The load address of the instruction at the given index.
  lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;

  /// \return
  ///     The number of instructions in this trace (not trace items).
  uint64_t GetTotalInstructionCount() const;

  /// Return an object with statistics of the trace events that happened.
  ///
  /// \return
  ///   The stats object of all the events.
  const EventsStats &GetEventsStats() const;

  /// Return an object with statistics of the trace errors that happened.
  ///
  /// \return
  ///   The stats object of all the events.
  const ErrorStats &GetErrorStats() const;

  /// The approximate size in bytes used by this instance,
  /// including all the already decoded instructions.
  size_t CalculateApproximateMemoryUsage() const;

  lldb::ThreadSP GetThread();

  /// Notify this object that a new tsc has been seen.
  /// If this a new TSC, an event will be created.
  void NotifyTsc(TSC tsc);

  /// Notify this object that a CPU has been seen.
  /// If this a new CPU, an event will be created.
  void NotifyCPU(lldb::cpu_id_t cpu_id);

  /// Notify this object that a new PSB has been seen.
  void NotifySyncPoint(lldb::addr_t psb_offset);

  /// Append a decoding error.
  void AppendError(const IntelPTError &error);

  /// Append a custom decoding.
  ///
  /// \param[in] error
  ///   The error message.
  ///
  /// \param[in] fatal
  ///   If \b true, then the whole decoded thread should be discarded because a
  ///   fatal anomaly has been found.
  void AppendCustomError(llvm::StringRef error, bool fatal = false);

  /// Append an event.
  void AppendEvent(lldb::TraceEvent);

  /// Append an instruction.
  void AppendInstruction(const pt_insn &insn);

private:
  /// When adding new members to this class, make sure
  /// to update \a CalculateApproximateMemoryUsage() accordingly.
  lldb::ThreadSP m_thread_sp;

  /// We use a union to optimize the memory usage for the different kinds of
  /// trace items.
  union TraceItemStorage {
    /// The load addresses of this item if it's an instruction.
    uint64_t load_address;

    /// The event kind of this item if it's an event
    lldb::TraceEvent event;

    /// The string message of this item if it's an error
    std::string error;
  };

  /// Create a new trace item.
  ///
  /// \return
  ///   The index of the new item.
  DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind);

  /// Most of the trace data is stored here.
  std::vector<TraceItemStorage> m_item_data;
  /// The TraceItemKind for each trace item encoded as uint8_t. We don't include
  /// it in TraceItemStorage to avoid padding.
  std::vector<uint8_t> m_item_kinds;

  /// This map contains the TSCs of the decoded trace items. It maps
  /// `item index -> TSC`, where `item index` is the first index
  /// at which the mapped TSC first appears. We use this representation because
  /// TSCs are sporadic and we can think of them as ranges.
  std::map<uint64_t, TSCRange> m_tscs;
  /// This is the chronologically last TSC that has been added.
  std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =
      std::nullopt;
  /// This map contains the non-interpolated nanoseconds timestamps of the
  /// decoded trace items. It maps `item index -> nanoseconds`, where `item
  /// index` is the first index at which the mapped nanoseconds first appears.
  /// We use this representation because timestamps are sporadic and we think of
  /// them as ranges.
  std::map<uint64_t, NanosecondsRange> m_nanoseconds;
  std::optional<std::map<uint64_t, NanosecondsRange>::iterator>
      m_last_nanoseconds = std::nullopt;

  // The cpu information is stored as a map. It maps `item index -> CPU`.
  // A CPU is associated with the next instructions that follow until the next
  // cpu is seen.
  std::map<uint64_t, lldb::cpu_id_t> m_cpus;
  /// This is the chronologically last CPU ID.
  std::optional<uint64_t> m_last_cpu;

  // The PSB offsets are stored as a map. It maps `item index -> psb offset`.
  llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets;

  /// TSC -> nanos conversion utility.
  std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;

  /// Statistics of all tracing errors.
  ErrorStats m_error_stats;

  /// Statistics of all tracing events.
  EventsStats m_events_stats;
  /// Total amount of time spent decoding.
  std::chrono::milliseconds m_total_decoding_time{0};

  /// Total number of instructions in the trace.
  uint64_t m_insn_count = 0;
};

using DecodedThreadSP = std::shared_ptr<DecodedThread>;

} // namespace trace_intel_pt
} // namespace lldb_private

#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H