Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / source / Plugins / Trace / intel-pt / DecodedThread.h
blob5745cdb67ab68fd211bcf939ca179b771b3724cb
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 //===----------------------------------------------------------------------===//
9 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
10 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
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>
21 namespace lldb_private {
22 namespace trace_intel_pt {
24 /// Class for representing a libipt decoding error.
25 class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
26 public:
27 static char ID;
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);
41 std::error_code convertToErrorCode() const override {
42 return llvm::errc::not_supported;
45 int GetLibiptErrorCode() const { return m_libipt_error_code; }
47 void log(llvm::raw_ostream &OS) const override;
49 private:
50 int m_libipt_error_code;
51 lldb::addr_t m_address;
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.
61 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
62 public:
63 using TSC = uint64_t;
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;
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;
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;
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.
102 /// \param[in] beginning_of_time_nanos
103 /// The timestamp at which tracing started.
105 /// \param[in] tsc_conversion
106 /// The tsc -> nanos conversion utility
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;
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;
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.
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;
132 void RecordEvent(lldb::TraceEvent event);
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 /// \}
145 uint64_t GetTotalCount() const;
147 void RecordError(int libipt_error_code);
149 void RecordError(bool fatal);
152 DecodedThread(
153 lldb::ThreadSP thread_sp,
154 const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);
156 /// Get the total number of instruction, errors and events from the decoded
157 /// trace.
158 uint64_t GetItemsCount() const;
160 /// \return
161 /// The error associated with a given trace item.
162 llvm::StringRef GetErrorByIndex(uint64_t item_index) const;
164 /// \return
165 /// The trace item kind given an item index.
166 lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;
168 /// \return
169 /// The underlying event type for the given trace item index.
170 lldb::TraceEvent GetEventByIndex(int item_index) const;
172 /// Get the most recent CPU id before or at the given trace item index.
174 /// \param[in] item_index
175 /// The trace item index to compare with.
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;
181 /// \return
182 /// The PSB offset associated with the given item index.
183 lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;
185 /// Get a maximal range of trace items that include the given \p item_index
186 /// that have the same TSC value.
188 /// \param[in] item_index
189 /// The trace item index to compare with.
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;
196 /// Get a maximal range of trace items that include the given \p item_index
197 /// that have the same nanoseconds timestamp without interpolation.
199 /// \param[in] item_index
200 /// The trace item index to compare with.
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);
207 /// \return
208 /// The load address of the instruction at the given index.
209 lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;
211 /// \return
212 /// The number of instructions in this trace (not trace items).
213 uint64_t GetTotalInstructionCount() const;
215 /// Return an object with statistics of the trace events that happened.
217 /// \return
218 /// The stats object of all the events.
219 const EventsStats &GetEventsStats() const;
221 /// Return an object with statistics of the trace errors that happened.
223 /// \return
224 /// The stats object of all the events.
225 const ErrorStats &GetErrorStats() const;
227 /// The approximate size in bytes used by this instance,
228 /// including all the already decoded instructions.
229 size_t CalculateApproximateMemoryUsage() const;
231 lldb::ThreadSP GetThread();
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);
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);
241 /// Notify this object that a new PSB has been seen.
242 void NotifySyncPoint(lldb::addr_t psb_offset);
244 /// Append a decoding error.
245 void AppendError(const IntelPTError &error);
247 /// Append a custom decoding.
249 /// \param[in] error
250 /// The error message.
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);
257 /// Append an event.
258 void AppendEvent(lldb::TraceEvent);
260 /// Append an instruction.
261 void AppendInstruction(const pt_insn &insn);
263 private:
264 /// When adding new members to this class, make sure
265 /// to update \a CalculateApproximateMemoryUsage() accordingly.
266 lldb::ThreadSP m_thread_sp;
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;
274 /// The event kind of this item if it's an event
275 lldb::TraceEvent event;
277 /// The string message of this item if it's an error
278 std::string error;
281 /// Create a new trace item.
283 /// \return
284 /// The index of the new item.
285 DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind);
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;
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;
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;
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;
320 /// TSC -> nanos conversion utility.
321 std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
323 /// Statistics of all tracing errors.
324 ErrorStats m_error_stats;
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};
331 /// Total number of instructions in the trace.
332 uint64_t m_insn_count = 0;
335 using DecodedThreadSP = std::shared_ptr<DecodedThread>;
337 } // namespace trace_intel_pt
338 } // namespace lldb_private
340 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H