1e8d8bef9SDimitry Andric //===-- DecodedThread.h -----------------------------------------*- C++ -*-===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric 
9e8d8bef9SDimitry Andric #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
10e8d8bef9SDimitry Andric #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
11e8d8bef9SDimitry Andric 
12bdd1243dSDimitry Andric #include "intel-pt.h"
13bdd1243dSDimitry Andric #include "lldb/Target/Trace.h"
14bdd1243dSDimitry Andric #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
15bdd1243dSDimitry Andric #include "llvm/Support/Errc.h"
16bdd1243dSDimitry Andric #include "llvm/Support/Error.h"
17*297eecfbSDimitry Andric #include <deque>
18bdd1243dSDimitry Andric #include <optional>
1981ad6265SDimitry Andric #include <utility>
20*297eecfbSDimitry Andric #include <variant>
21e8d8bef9SDimitry Andric 
22e8d8bef9SDimitry Andric namespace lldb_private {
23e8d8bef9SDimitry Andric namespace trace_intel_pt {
24e8d8bef9SDimitry Andric 
25e8d8bef9SDimitry Andric /// Class for representing a libipt decoding error.
26e8d8bef9SDimitry Andric class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
27e8d8bef9SDimitry Andric public:
28e8d8bef9SDimitry Andric   static char ID;
29e8d8bef9SDimitry Andric 
30e8d8bef9SDimitry Andric   /// \param[in] libipt_error_code
31e8d8bef9SDimitry Andric   ///     Negative number returned by libipt when decoding the trace and
32e8d8bef9SDimitry Andric   ///     signaling errors.
33e8d8bef9SDimitry Andric   ///
34e8d8bef9SDimitry Andric   /// \param[in] address
35e8d8bef9SDimitry Andric   ///     Optional instruction address. When decoding an individual instruction,
36e8d8bef9SDimitry Andric   ///     its address might be available in the \a pt_insn object, and should be
37e8d8bef9SDimitry Andric   ///     passed to this constructor. Other errors don't have an associated
38e8d8bef9SDimitry Andric   ///     address.
39e8d8bef9SDimitry Andric   IntelPTError(int libipt_error_code,
40e8d8bef9SDimitry Andric                lldb::addr_t address = LLDB_INVALID_ADDRESS);
41e8d8bef9SDimitry Andric 
convertToErrorCode()42e8d8bef9SDimitry Andric   std::error_code convertToErrorCode() const override {
43e8d8bef9SDimitry Andric     return llvm::errc::not_supported;
44e8d8bef9SDimitry Andric   }
45e8d8bef9SDimitry Andric 
GetLibiptErrorCode()4681ad6265SDimitry Andric   int GetLibiptErrorCode() const { return m_libipt_error_code; }
4781ad6265SDimitry Andric 
48e8d8bef9SDimitry Andric   void log(llvm::raw_ostream &OS) const override;
49e8d8bef9SDimitry Andric 
50e8d8bef9SDimitry Andric private:
51e8d8bef9SDimitry Andric   int m_libipt_error_code;
52e8d8bef9SDimitry Andric   lldb::addr_t m_address;
53e8d8bef9SDimitry Andric };
54e8d8bef9SDimitry Andric 
55e8d8bef9SDimitry Andric /// \class DecodedThread
56e8d8bef9SDimitry Andric /// Class holding the instructions and function call hierarchy obtained from
57e8d8bef9SDimitry Andric /// decoding a trace, as well as a position cursor used when reverse debugging
58e8d8bef9SDimitry Andric /// the trace.
59e8d8bef9SDimitry Andric ///
60e8d8bef9SDimitry Andric /// Each decoded thread contains a cursor to the current position the user is
61e8d8bef9SDimitry Andric /// stopped at. See \a Trace::GetCursorPosition for more information.
62fe6060f1SDimitry Andric class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
63e8d8bef9SDimitry Andric public:
64972a253aSDimitry Andric   using TSC = uint64_t;
6581ad6265SDimitry Andric 
66972a253aSDimitry Andric   /// A structure that represents a maximal range of trace items associated to
67972a253aSDimitry Andric   /// the same TSC value.
68972a253aSDimitry Andric   struct TSCRange {
69972a253aSDimitry Andric     TSC tsc;
70972a253aSDimitry Andric     /// Number of trace items in this range.
71972a253aSDimitry Andric     uint64_t items_count;
72972a253aSDimitry Andric     /// Index of the first trace item in this range.
73972a253aSDimitry Andric     uint64_t first_item_index;
74972a253aSDimitry Andric 
75972a253aSDimitry Andric     /// \return
76972a253aSDimitry Andric     ///   \b true if and only if the given \p item_index is covered by this
77972a253aSDimitry Andric     ///   range.
78972a253aSDimitry Andric     bool InRange(uint64_t item_index) const;
79972a253aSDimitry Andric   };
80972a253aSDimitry Andric 
81972a253aSDimitry Andric   /// A structure that represents a maximal range of trace items associated to
82972a253aSDimitry Andric   /// the same non-interpolated timestamps in nanoseconds.
83972a253aSDimitry Andric   struct NanosecondsRange {
84972a253aSDimitry Andric     /// The nanoseconds value for this range.
85972a253aSDimitry Andric     uint64_t nanos;
86972a253aSDimitry Andric     /// The corresponding TSC value for this range.
87972a253aSDimitry Andric     TSC tsc;
88972a253aSDimitry Andric     /// A nullable pointer to the next range.
89972a253aSDimitry Andric     NanosecondsRange *next_range;
90972a253aSDimitry Andric     /// Number of trace items in this range.
91972a253aSDimitry Andric     uint64_t items_count;
92972a253aSDimitry Andric     /// Index of the first trace item in this range.
93972a253aSDimitry Andric     uint64_t first_item_index;
94972a253aSDimitry Andric 
95972a253aSDimitry Andric     /// Calculate an interpolated timestamp in nanoseconds for the given item
96972a253aSDimitry Andric     /// index. It's guaranteed that two different item indices will produce
97972a253aSDimitry Andric     /// different interpolated values.
98972a253aSDimitry Andric     ///
99972a253aSDimitry Andric     /// \param[in] item_index
100972a253aSDimitry Andric     ///   The index of the item whose timestamp will be estimated. It has to be
101972a253aSDimitry Andric     ///   part of this range.
102972a253aSDimitry Andric     ///
103972a253aSDimitry Andric     /// \param[in] beginning_of_time_nanos
104972a253aSDimitry Andric     ///   The timestamp at which tracing started.
105972a253aSDimitry Andric     ///
106972a253aSDimitry Andric     /// \param[in] tsc_conversion
107972a253aSDimitry Andric     ///   The tsc -> nanos conversion utility
108972a253aSDimitry Andric     ///
109972a253aSDimitry Andric     /// \return
110972a253aSDimitry Andric     ///   An interpolated timestamp value for the given trace item.
111972a253aSDimitry Andric     double
112972a253aSDimitry Andric     GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,
113972a253aSDimitry Andric                         const LinuxPerfZeroTscConversion &tsc_conversion) const;
114972a253aSDimitry Andric 
115972a253aSDimitry Andric     /// \return
116972a253aSDimitry Andric     ///   \b true if and only if the given \p item_index is covered by this
117972a253aSDimitry Andric     ///   range.
118972a253aSDimitry Andric     bool InRange(uint64_t item_index) const;
119972a253aSDimitry Andric   };
120972a253aSDimitry Andric 
121bdd1243dSDimitry Andric   // Struct holding counts for events
12281ad6265SDimitry Andric   struct EventsStats {
12381ad6265SDimitry Andric     /// A count for each individual event kind. We use an unordered map instead
12481ad6265SDimitry Andric     /// of a DenseMap because DenseMap can't understand enums.
125bdd1243dSDimitry Andric     ///
126bdd1243dSDimitry Andric     /// Note: We can't use DenseMap because lldb::TraceEvent is not
127bdd1243dSDimitry Andric     /// automatically handled correctly by DenseMap. We'd need to implement a
128bdd1243dSDimitry Andric     /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for
129bdd1243dSDimitry Andric     /// such a simple structure.
130bdd1243dSDimitry Andric     std::unordered_map<lldb::TraceEvent, uint64_t> events_counts;
131bdd1243dSDimitry Andric     uint64_t total_count = 0;
13281ad6265SDimitry Andric 
13381ad6265SDimitry Andric     void RecordEvent(lldb::TraceEvent event);
13481ad6265SDimitry Andric   };
13581ad6265SDimitry Andric 
136bdd1243dSDimitry Andric   // Struct holding counts for errors
137bdd1243dSDimitry Andric   struct ErrorStats {
138bdd1243dSDimitry Andric     /// The following counters are mutually exclusive
139bdd1243dSDimitry Andric     /// \{
140bdd1243dSDimitry Andric     uint64_t other_errors = 0;
141bdd1243dSDimitry Andric     uint64_t fatal_errors = 0;
142bdd1243dSDimitry Andric     // libipt error -> count
143bdd1243dSDimitry Andric     llvm::DenseMap<const char *, uint64_t> libipt_errors;
144bdd1243dSDimitry Andric     /// \}
145bdd1243dSDimitry Andric 
146bdd1243dSDimitry Andric     uint64_t GetTotalCount() const;
147bdd1243dSDimitry Andric 
148bdd1243dSDimitry Andric     void RecordError(int libipt_error_code);
149bdd1243dSDimitry Andric 
150bdd1243dSDimitry Andric     void RecordError(bool fatal);
151bdd1243dSDimitry Andric   };
152bdd1243dSDimitry Andric 
153972a253aSDimitry Andric   DecodedThread(
154972a253aSDimitry Andric       lldb::ThreadSP thread_sp,
155bdd1243dSDimitry Andric       const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);
15681ad6265SDimitry Andric 
15781ad6265SDimitry Andric   /// Get the total number of instruction, errors and events from the decoded
15881ad6265SDimitry Andric   /// trace.
159972a253aSDimitry Andric   uint64_t GetItemsCount() const;
16081ad6265SDimitry Andric 
16181ad6265SDimitry Andric   /// \return
16281ad6265SDimitry Andric   ///   The error associated with a given trace item.
16306c3fb27SDimitry Andric   llvm::StringRef GetErrorByIndex(uint64_t item_index) const;
16481ad6265SDimitry Andric 
16581ad6265SDimitry Andric   /// \return
16681ad6265SDimitry Andric   ///   The trace item kind given an item index.
167972a253aSDimitry Andric   lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;
16881ad6265SDimitry Andric 
16981ad6265SDimitry Andric   /// \return
17081ad6265SDimitry Andric   ///   The underlying event type for the given trace item index.
17181ad6265SDimitry Andric   lldb::TraceEvent GetEventByIndex(int item_index) const;
17281ad6265SDimitry Andric 
173753f127fSDimitry Andric   /// Get the most recent CPU id before or at the given trace item index.
174753f127fSDimitry Andric   ///
175753f127fSDimitry Andric   /// \param[in] item_index
176753f127fSDimitry Andric   ///   The trace item index to compare with.
177753f127fSDimitry Andric   ///
178753f127fSDimitry Andric   /// \return
179bdd1243dSDimitry Andric   ///   The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.
180bdd1243dSDimitry Andric   lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;
181bdd1243dSDimitry Andric 
182bdd1243dSDimitry Andric   /// \return
183bdd1243dSDimitry Andric   ///   The PSB offset associated with the given item index.
184bdd1243dSDimitry Andric   lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;
185753f127fSDimitry Andric 
186972a253aSDimitry Andric   /// Get a maximal range of trace items that include the given \p item_index
187972a253aSDimitry Andric   /// that have the same TSC value.
188972a253aSDimitry Andric   ///
189972a253aSDimitry Andric   /// \param[in] item_index
190972a253aSDimitry Andric   ///   The trace item index to compare with.
191972a253aSDimitry Andric   ///
192972a253aSDimitry Andric   /// \return
193bdd1243dSDimitry Andric   ///   The requested TSC range, or \a std::nullopt if not available.
194bdd1243dSDimitry Andric   std::optional<DecodedThread::TSCRange>
195972a253aSDimitry Andric   GetTSCRangeByIndex(uint64_t item_index) const;
196972a253aSDimitry Andric 
197972a253aSDimitry Andric   /// Get a maximal range of trace items that include the given \p item_index
198972a253aSDimitry Andric   /// that have the same nanoseconds timestamp without interpolation.
199972a253aSDimitry Andric   ///
200972a253aSDimitry Andric   /// \param[in] item_index
201972a253aSDimitry Andric   ///   The trace item index to compare with.
202972a253aSDimitry Andric   ///
203972a253aSDimitry Andric   /// \return
204bdd1243dSDimitry Andric   ///   The requested nanoseconds range, or \a std::nullopt if not available.
205bdd1243dSDimitry Andric   std::optional<DecodedThread::NanosecondsRange>
206972a253aSDimitry Andric   GetNanosecondsRangeByIndex(uint64_t item_index);
207972a253aSDimitry Andric 
20881ad6265SDimitry Andric   /// \return
20981ad6265SDimitry Andric   ///     The load address of the instruction at the given index.
210972a253aSDimitry Andric   lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;
21181ad6265SDimitry Andric 
21281ad6265SDimitry Andric   /// \return
213bdd1243dSDimitry Andric   ///     The number of instructions in this trace (not trace items).
214bdd1243dSDimitry Andric   uint64_t GetTotalInstructionCount() const;
21581ad6265SDimitry Andric 
21681ad6265SDimitry Andric   /// Return an object with statistics of the trace events that happened.
21781ad6265SDimitry Andric   ///
21881ad6265SDimitry Andric   /// \return
21981ad6265SDimitry Andric   ///   The stats object of all the events.
22081ad6265SDimitry Andric   const EventsStats &GetEventsStats() const;
22181ad6265SDimitry Andric 
222bdd1243dSDimitry Andric   /// Return an object with statistics of the trace errors that happened.
22381ad6265SDimitry Andric   ///
224bdd1243dSDimitry Andric   /// \return
225bdd1243dSDimitry Andric   ///   The stats object of all the events.
226bdd1243dSDimitry Andric   const ErrorStats &GetErrorStats() const;
22781ad6265SDimitry Andric 
22881ad6265SDimitry Andric   /// The approximate size in bytes used by this instance,
22981ad6265SDimitry Andric   /// including all the already decoded instructions.
23081ad6265SDimitry Andric   size_t CalculateApproximateMemoryUsage() const;
23181ad6265SDimitry Andric 
23281ad6265SDimitry Andric   lldb::ThreadSP GetThread();
23381ad6265SDimitry Andric 
23481ad6265SDimitry Andric   /// Notify this object that a new tsc has been seen.
235753f127fSDimitry Andric   /// If this a new TSC, an event will be created.
236972a253aSDimitry Andric   void NotifyTsc(TSC tsc);
23781ad6265SDimitry Andric 
238753f127fSDimitry Andric   /// Notify this object that a CPU has been seen.
239753f127fSDimitry Andric   /// If this a new CPU, an event will be created.
240753f127fSDimitry Andric   void NotifyCPU(lldb::cpu_id_t cpu_id);
241753f127fSDimitry Andric 
242bdd1243dSDimitry Andric   /// Notify this object that a new PSB has been seen.
243bdd1243dSDimitry Andric   void NotifySyncPoint(lldb::addr_t psb_offset);
244bdd1243dSDimitry Andric 
24581ad6265SDimitry Andric   /// Append a decoding error.
24681ad6265SDimitry Andric   void AppendError(const IntelPTError &error);
24781ad6265SDimitry Andric 
24881ad6265SDimitry Andric   /// Append a custom decoding.
249bdd1243dSDimitry Andric   ///
250bdd1243dSDimitry Andric   /// \param[in] error
251bdd1243dSDimitry Andric   ///   The error message.
252bdd1243dSDimitry Andric   ///
253bdd1243dSDimitry Andric   /// \param[in] fatal
254bdd1243dSDimitry Andric   ///   If \b true, then the whole decoded thread should be discarded because a
255bdd1243dSDimitry Andric   ///   fatal anomaly has been found.
256bdd1243dSDimitry Andric   void AppendCustomError(llvm::StringRef error, bool fatal = false);
25781ad6265SDimitry Andric 
25881ad6265SDimitry Andric   /// Append an event.
25981ad6265SDimitry Andric   void AppendEvent(lldb::TraceEvent);
26081ad6265SDimitry Andric 
26181ad6265SDimitry Andric   /// Append an instruction.
26281ad6265SDimitry Andric   void AppendInstruction(const pt_insn &insn);
26381ad6265SDimitry Andric 
26481ad6265SDimitry Andric private:
26581ad6265SDimitry Andric   /// When adding new members to this class, make sure
26681ad6265SDimitry Andric   /// to update \a CalculateApproximateMemoryUsage() accordingly.
267fe6060f1SDimitry Andric   lldb::ThreadSP m_thread_sp;
26881ad6265SDimitry Andric 
269*297eecfbSDimitry Andric   using TraceItemStorage =
270*297eecfbSDimitry Andric       std::variant<std::string, lldb::TraceEvent, lldb::addr_t>;
27181ad6265SDimitry Andric 
27281ad6265SDimitry Andric   /// Create a new trace item.
27381ad6265SDimitry Andric   ///
27481ad6265SDimitry Andric   /// \return
27581ad6265SDimitry Andric   ///   The index of the new item.
276*297eecfbSDimitry Andric   template <typename Data>
277*297eecfbSDimitry Andric   DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind,
278*297eecfbSDimitry Andric                                                       Data &&data);
27981ad6265SDimitry Andric 
28081ad6265SDimitry Andric   /// Most of the trace data is stored here.
281*297eecfbSDimitry Andric   std::deque<TraceItemStorage> m_item_data;
28281ad6265SDimitry Andric 
283972a253aSDimitry Andric   /// This map contains the TSCs of the decoded trace items. It maps
284972a253aSDimitry Andric   /// `item index -> TSC`, where `item index` is the first index
285972a253aSDimitry Andric   /// at which the mapped TSC first appears. We use this representation because
286972a253aSDimitry Andric   /// TSCs are sporadic and we can think of them as ranges.
287972a253aSDimitry Andric   std::map<uint64_t, TSCRange> m_tscs;
28881ad6265SDimitry Andric   /// This is the chronologically last TSC that has been added.
289bdd1243dSDimitry Andric   std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =
290bdd1243dSDimitry Andric       std::nullopt;
291972a253aSDimitry Andric   /// This map contains the non-interpolated nanoseconds timestamps of the
292972a253aSDimitry Andric   /// decoded trace items. It maps `item index -> nanoseconds`, where `item
293972a253aSDimitry Andric   /// index` is the first index at which the mapped nanoseconds first appears.
294972a253aSDimitry Andric   /// We use this representation because timestamps are sporadic and we think of
295972a253aSDimitry Andric   /// them as ranges.
296972a253aSDimitry Andric   std::map<uint64_t, NanosecondsRange> m_nanoseconds;
297bdd1243dSDimitry Andric   std::optional<std::map<uint64_t, NanosecondsRange>::iterator>
298bdd1243dSDimitry Andric       m_last_nanoseconds = std::nullopt;
29981ad6265SDimitry Andric 
300bdd1243dSDimitry Andric   // The cpu information is stored as a map. It maps `item index -> CPU`.
301753f127fSDimitry Andric   // A CPU is associated with the next instructions that follow until the next
302753f127fSDimitry Andric   // cpu is seen.
303753f127fSDimitry Andric   std::map<uint64_t, lldb::cpu_id_t> m_cpus;
304753f127fSDimitry Andric   /// This is the chronologically last CPU ID.
305bdd1243dSDimitry Andric   std::optional<uint64_t> m_last_cpu;
306bdd1243dSDimitry Andric 
307bdd1243dSDimitry Andric   // The PSB offsets are stored as a map. It maps `item index -> psb offset`.
308bdd1243dSDimitry Andric   llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets;
309753f127fSDimitry Andric 
310972a253aSDimitry Andric   /// TSC -> nanos conversion utility.
311bdd1243dSDimitry Andric   std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
312bdd1243dSDimitry Andric 
313bdd1243dSDimitry Andric   /// Statistics of all tracing errors.
314bdd1243dSDimitry Andric   ErrorStats m_error_stats;
315972a253aSDimitry Andric 
31681ad6265SDimitry Andric   /// Statistics of all tracing events.
31781ad6265SDimitry Andric   EventsStats m_events_stats;
31881ad6265SDimitry Andric   /// Total amount of time spent decoding.
31981ad6265SDimitry Andric   std::chrono::milliseconds m_total_decoding_time{0};
320bdd1243dSDimitry Andric 
321bdd1243dSDimitry Andric   /// Total number of instructions in the trace.
322bdd1243dSDimitry Andric   uint64_t m_insn_count = 0;
323e8d8bef9SDimitry Andric };
324e8d8bef9SDimitry Andric 
325fe6060f1SDimitry Andric using DecodedThreadSP = std::shared_ptr<DecodedThread>;
326fe6060f1SDimitry Andric 
327e8d8bef9SDimitry Andric } // namespace trace_intel_pt
328e8d8bef9SDimitry Andric } // namespace lldb_private
329e8d8bef9SDimitry Andric 
330e8d8bef9SDimitry Andric #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
331