xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h (revision 972a253a57b6f144b0e4a3e2080a2a0076ec55a0)
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 
1281ad6265SDimitry Andric #include <utility>
13e8d8bef9SDimitry Andric #include <vector>
14e8d8bef9SDimitry Andric 
15e8d8bef9SDimitry Andric #include "llvm/Support/Errc.h"
16e8d8bef9SDimitry Andric #include "llvm/Support/Error.h"
17e8d8bef9SDimitry Andric 
18e8d8bef9SDimitry Andric #include "lldb/Target/Trace.h"
19fe6060f1SDimitry Andric #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
20e8d8bef9SDimitry Andric 
21e8d8bef9SDimitry Andric #include "intel-pt.h"
22e8d8bef9SDimitry Andric 
23e8d8bef9SDimitry Andric namespace lldb_private {
24e8d8bef9SDimitry Andric namespace trace_intel_pt {
25e8d8bef9SDimitry Andric 
2681ad6265SDimitry Andric /// libipt status utils
2781ad6265SDimitry Andric /// \{
2881ad6265SDimitry Andric bool IsLibiptError(int libipt_status);
2981ad6265SDimitry Andric 
3081ad6265SDimitry Andric bool IsEndOfStream(int libipt_status);
3181ad6265SDimitry Andric 
3281ad6265SDimitry Andric bool IsTscUnavailable(int libipt_status);
3381ad6265SDimitry Andric /// \}
3481ad6265SDimitry Andric 
35e8d8bef9SDimitry Andric /// Class for representing a libipt decoding error.
36e8d8bef9SDimitry Andric class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
37e8d8bef9SDimitry Andric public:
38e8d8bef9SDimitry Andric   static char ID;
39e8d8bef9SDimitry Andric 
40e8d8bef9SDimitry Andric   /// \param[in] libipt_error_code
41e8d8bef9SDimitry Andric   ///     Negative number returned by libipt when decoding the trace and
42e8d8bef9SDimitry Andric   ///     signaling errors.
43e8d8bef9SDimitry Andric   ///
44e8d8bef9SDimitry Andric   /// \param[in] address
45e8d8bef9SDimitry Andric   ///     Optional instruction address. When decoding an individual instruction,
46e8d8bef9SDimitry Andric   ///     its address might be available in the \a pt_insn object, and should be
47e8d8bef9SDimitry Andric   ///     passed to this constructor. Other errors don't have an associated
48e8d8bef9SDimitry Andric   ///     address.
49e8d8bef9SDimitry Andric   IntelPTError(int libipt_error_code,
50e8d8bef9SDimitry Andric                lldb::addr_t address = LLDB_INVALID_ADDRESS);
51e8d8bef9SDimitry Andric 
52e8d8bef9SDimitry Andric   std::error_code convertToErrorCode() const override {
53e8d8bef9SDimitry Andric     return llvm::errc::not_supported;
54e8d8bef9SDimitry Andric   }
55e8d8bef9SDimitry Andric 
5681ad6265SDimitry Andric   int GetLibiptErrorCode() const { return m_libipt_error_code; }
5781ad6265SDimitry Andric 
58e8d8bef9SDimitry Andric   void log(llvm::raw_ostream &OS) const override;
59e8d8bef9SDimitry Andric 
60e8d8bef9SDimitry Andric private:
61e8d8bef9SDimitry Andric   int m_libipt_error_code;
62e8d8bef9SDimitry Andric   lldb::addr_t m_address;
63e8d8bef9SDimitry Andric };
64e8d8bef9SDimitry Andric 
65e8d8bef9SDimitry Andric /// \class DecodedThread
66e8d8bef9SDimitry Andric /// Class holding the instructions and function call hierarchy obtained from
67e8d8bef9SDimitry Andric /// decoding a trace, as well as a position cursor used when reverse debugging
68e8d8bef9SDimitry Andric /// the trace.
69e8d8bef9SDimitry Andric ///
70e8d8bef9SDimitry Andric /// Each decoded thread contains a cursor to the current position the user is
71e8d8bef9SDimitry Andric /// stopped at. See \a Trace::GetCursorPosition for more information.
72fe6060f1SDimitry Andric class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
73e8d8bef9SDimitry Andric public:
74*972a253aSDimitry Andric   using TSC = uint64_t;
7581ad6265SDimitry Andric 
7681ad6265SDimitry Andric   // Struct holding counts for libipts errors;
7781ad6265SDimitry Andric   struct LibiptErrorsStats {
7881ad6265SDimitry Andric     // libipt error -> count
7981ad6265SDimitry Andric     llvm::DenseMap<const char *, int> libipt_errors_counts;
8081ad6265SDimitry Andric     size_t total_count = 0;
8181ad6265SDimitry Andric 
8281ad6265SDimitry Andric     void RecordError(int libipt_error_code);
8381ad6265SDimitry Andric   };
8481ad6265SDimitry Andric 
85*972a253aSDimitry Andric   /// A structure that represents a maximal range of trace items associated to
86*972a253aSDimitry Andric   /// the same TSC value.
87*972a253aSDimitry Andric   struct TSCRange {
88*972a253aSDimitry Andric     TSC tsc;
89*972a253aSDimitry Andric     /// Number of trace items in this range.
90*972a253aSDimitry Andric     uint64_t items_count;
91*972a253aSDimitry Andric     /// Index of the first trace item in this range.
92*972a253aSDimitry Andric     uint64_t first_item_index;
93*972a253aSDimitry Andric 
94*972a253aSDimitry Andric     /// \return
95*972a253aSDimitry Andric     ///   \b true if and only if the given \p item_index is covered by this
96*972a253aSDimitry Andric     ///   range.
97*972a253aSDimitry Andric     bool InRange(uint64_t item_index) const;
98*972a253aSDimitry Andric   };
99*972a253aSDimitry Andric 
100*972a253aSDimitry Andric   /// A structure that represents a maximal range of trace items associated to
101*972a253aSDimitry Andric   /// the same non-interpolated timestamps in nanoseconds.
102*972a253aSDimitry Andric   struct NanosecondsRange {
103*972a253aSDimitry Andric     /// The nanoseconds value for this range.
104*972a253aSDimitry Andric     uint64_t nanos;
105*972a253aSDimitry Andric     /// The corresponding TSC value for this range.
106*972a253aSDimitry Andric     TSC tsc;
107*972a253aSDimitry Andric     /// A nullable pointer to the next range.
108*972a253aSDimitry Andric     NanosecondsRange *next_range;
109*972a253aSDimitry Andric     /// Number of trace items in this range.
110*972a253aSDimitry Andric     uint64_t items_count;
111*972a253aSDimitry Andric     /// Index of the first trace item in this range.
112*972a253aSDimitry Andric     uint64_t first_item_index;
113*972a253aSDimitry Andric 
114*972a253aSDimitry Andric     /// Calculate an interpolated timestamp in nanoseconds for the given item
115*972a253aSDimitry Andric     /// index. It's guaranteed that two different item indices will produce
116*972a253aSDimitry Andric     /// different interpolated values.
117*972a253aSDimitry Andric     ///
118*972a253aSDimitry Andric     /// \param[in] item_index
119*972a253aSDimitry Andric     ///   The index of the item whose timestamp will be estimated. It has to be
120*972a253aSDimitry Andric     ///   part of this range.
121*972a253aSDimitry Andric     ///
122*972a253aSDimitry Andric     /// \param[in] beginning_of_time_nanos
123*972a253aSDimitry Andric     ///   The timestamp at which tracing started.
124*972a253aSDimitry Andric     ///
125*972a253aSDimitry Andric     /// \param[in] tsc_conversion
126*972a253aSDimitry Andric     ///   The tsc -> nanos conversion utility
127*972a253aSDimitry Andric     ///
128*972a253aSDimitry Andric     /// \return
129*972a253aSDimitry Andric     ///   An interpolated timestamp value for the given trace item.
130*972a253aSDimitry Andric     double
131*972a253aSDimitry Andric     GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,
132*972a253aSDimitry Andric                         const LinuxPerfZeroTscConversion &tsc_conversion) const;
133*972a253aSDimitry Andric 
134*972a253aSDimitry Andric     /// \return
135*972a253aSDimitry Andric     ///   \b true if and only if the given \p item_index is covered by this
136*972a253aSDimitry Andric     ///   range.
137*972a253aSDimitry Andric     bool InRange(uint64_t item_index) const;
138*972a253aSDimitry Andric   };
139*972a253aSDimitry Andric 
14081ad6265SDimitry Andric   // Struct holding counts for events;
14181ad6265SDimitry Andric   struct EventsStats {
14281ad6265SDimitry Andric     /// A count for each individual event kind. We use an unordered map instead
14381ad6265SDimitry Andric     /// of a DenseMap because DenseMap can't understand enums.
14481ad6265SDimitry Andric     std::unordered_map<lldb::TraceEvent, size_t> events_counts;
14581ad6265SDimitry Andric     size_t total_count = 0;
14681ad6265SDimitry Andric 
14781ad6265SDimitry Andric     void RecordEvent(lldb::TraceEvent event);
14881ad6265SDimitry Andric   };
14981ad6265SDimitry Andric 
150*972a253aSDimitry Andric   DecodedThread(
151*972a253aSDimitry Andric       lldb::ThreadSP thread_sp,
152*972a253aSDimitry Andric       const llvm::Optional<LinuxPerfZeroTscConversion> &tsc_conversion);
15381ad6265SDimitry Andric 
15481ad6265SDimitry Andric   /// Get the total number of instruction, errors and events from the decoded
15581ad6265SDimitry Andric   /// trace.
156*972a253aSDimitry Andric   uint64_t GetItemsCount() const;
15781ad6265SDimitry Andric 
15881ad6265SDimitry Andric   /// \return
15981ad6265SDimitry Andric   ///   The error associated with a given trace item.
160*972a253aSDimitry Andric   const char *GetErrorByIndex(uint64_t item_index) const;
16181ad6265SDimitry Andric 
16281ad6265SDimitry Andric   /// \return
16381ad6265SDimitry Andric   ///   The trace item kind given an item index.
164*972a253aSDimitry Andric   lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;
16581ad6265SDimitry Andric 
16681ad6265SDimitry Andric   /// \return
16781ad6265SDimitry Andric   ///   The underlying event type for the given trace item index.
16881ad6265SDimitry Andric   lldb::TraceEvent GetEventByIndex(int item_index) const;
16981ad6265SDimitry Andric 
170753f127fSDimitry Andric   /// Get the most recent CPU id before or at the given trace item index.
171753f127fSDimitry Andric   ///
172753f127fSDimitry Andric   /// \param[in] item_index
173753f127fSDimitry Andric   ///   The trace item index to compare with.
174753f127fSDimitry Andric   ///
175753f127fSDimitry Andric   /// \return
176753f127fSDimitry Andric   ///   The requested cpu id, or \a llvm::None if not available.
177753f127fSDimitry Andric   llvm::Optional<lldb::cpu_id_t> GetCPUByIndex(uint64_t item_index) const;
178753f127fSDimitry Andric 
179*972a253aSDimitry Andric   /// Get a maximal range of trace items that include the given \p item_index
180*972a253aSDimitry Andric   /// that have the same TSC value.
181*972a253aSDimitry Andric   ///
182*972a253aSDimitry Andric   /// \param[in] item_index
183*972a253aSDimitry Andric   ///   The trace item index to compare with.
184*972a253aSDimitry Andric   ///
185*972a253aSDimitry Andric   /// \return
186*972a253aSDimitry Andric   ///   The requested TSC range, or \a llvm::None if not available.
187*972a253aSDimitry Andric   llvm::Optional<DecodedThread::TSCRange>
188*972a253aSDimitry Andric   GetTSCRangeByIndex(uint64_t item_index) const;
189*972a253aSDimitry Andric 
190*972a253aSDimitry Andric   /// Get a maximal range of trace items that include the given \p item_index
191*972a253aSDimitry Andric   /// that have the same nanoseconds timestamp without interpolation.
192*972a253aSDimitry Andric   ///
193*972a253aSDimitry Andric   /// \param[in] item_index
194*972a253aSDimitry Andric   ///   The trace item index to compare with.
195*972a253aSDimitry Andric   ///
196*972a253aSDimitry Andric   /// \return
197*972a253aSDimitry Andric   ///   The requested nanoseconds range, or \a llvm::None if not available.
198*972a253aSDimitry Andric   llvm::Optional<DecodedThread::NanosecondsRange>
199*972a253aSDimitry Andric   GetNanosecondsRangeByIndex(uint64_t item_index);
200*972a253aSDimitry Andric 
20181ad6265SDimitry Andric   /// \return
20281ad6265SDimitry Andric   ///     The load address of the instruction at the given index.
203*972a253aSDimitry Andric   lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;
20481ad6265SDimitry Andric 
20581ad6265SDimitry Andric   /// Return an object with statistics of the TSC decoding errors that happened.
20681ad6265SDimitry Andric   /// A TSC error is not a fatal error and doesn't create gaps in the trace.
20781ad6265SDimitry Andric   /// Instead we only keep track of them as statistics.
20881ad6265SDimitry Andric   ///
20981ad6265SDimitry Andric   /// \return
21081ad6265SDimitry Andric   ///   An object with the statistics of TSC decoding errors.
21181ad6265SDimitry Andric   const LibiptErrorsStats &GetTscErrorsStats() const;
21281ad6265SDimitry Andric 
21381ad6265SDimitry Andric   /// Return an object with statistics of the trace events that happened.
21481ad6265SDimitry Andric   ///
21581ad6265SDimitry Andric   /// \return
21681ad6265SDimitry Andric   ///   The stats object of all the events.
21781ad6265SDimitry Andric   const EventsStats &GetEventsStats() const;
21881ad6265SDimitry Andric 
21981ad6265SDimitry Andric   /// Record an error decoding a TSC timestamp.
22081ad6265SDimitry Andric   ///
22181ad6265SDimitry Andric   /// See \a GetTscErrors() for more documentation.
22281ad6265SDimitry Andric   ///
22381ad6265SDimitry Andric   /// \param[in] libipt_error_code
22481ad6265SDimitry Andric   ///   An error returned by the libipt library.
22581ad6265SDimitry Andric   void RecordTscError(int libipt_error_code);
22681ad6265SDimitry Andric 
22781ad6265SDimitry Andric   /// The approximate size in bytes used by this instance,
22881ad6265SDimitry Andric   /// including all the already decoded instructions.
22981ad6265SDimitry Andric   size_t CalculateApproximateMemoryUsage() const;
23081ad6265SDimitry Andric 
23181ad6265SDimitry Andric   lldb::ThreadSP GetThread();
23281ad6265SDimitry Andric 
23381ad6265SDimitry Andric   /// Notify this object that a new tsc has been seen.
234753f127fSDimitry Andric   /// If this a new TSC, an event will be created.
235*972a253aSDimitry Andric   void NotifyTsc(TSC tsc);
23681ad6265SDimitry Andric 
237753f127fSDimitry Andric   /// Notify this object that a CPU has been seen.
238753f127fSDimitry Andric   /// If this a new CPU, an event will be created.
239753f127fSDimitry Andric   void NotifyCPU(lldb::cpu_id_t cpu_id);
240753f127fSDimitry Andric 
24181ad6265SDimitry Andric   /// Append a decoding error.
24281ad6265SDimitry Andric   void AppendError(const IntelPTError &error);
24381ad6265SDimitry Andric 
24481ad6265SDimitry Andric   /// Append a custom decoding.
24581ad6265SDimitry Andric   void AppendCustomError(llvm::StringRef error);
24681ad6265SDimitry Andric 
24781ad6265SDimitry Andric   /// Append an event.
24881ad6265SDimitry Andric   void AppendEvent(lldb::TraceEvent);
24981ad6265SDimitry Andric 
25081ad6265SDimitry Andric   /// Append an instruction.
25181ad6265SDimitry Andric   void AppendInstruction(const pt_insn &insn);
25281ad6265SDimitry Andric 
25381ad6265SDimitry Andric private:
25481ad6265SDimitry Andric   /// When adding new members to this class, make sure
25581ad6265SDimitry Andric   /// to update \a CalculateApproximateMemoryUsage() accordingly.
256fe6060f1SDimitry Andric   lldb::ThreadSP m_thread_sp;
25781ad6265SDimitry Andric 
25881ad6265SDimitry Andric   /// We use a union to optimize the memory usage for the different kinds of
25981ad6265SDimitry Andric   /// trace items.
26081ad6265SDimitry Andric   union TraceItemStorage {
26181ad6265SDimitry Andric     /// The load addresses of this item if it's an instruction.
26281ad6265SDimitry Andric     uint64_t load_address;
26381ad6265SDimitry Andric 
26481ad6265SDimitry Andric     /// The event kind of this item if it's an event
26581ad6265SDimitry Andric     lldb::TraceEvent event;
26681ad6265SDimitry Andric 
26781ad6265SDimitry Andric     /// The string message of this item if it's an error
26881ad6265SDimitry Andric     const char *error;
26981ad6265SDimitry Andric   };
27081ad6265SDimitry Andric 
27181ad6265SDimitry Andric   /// Create a new trace item.
27281ad6265SDimitry Andric   ///
27381ad6265SDimitry Andric   /// \return
27481ad6265SDimitry Andric   ///   The index of the new item.
27581ad6265SDimitry Andric   DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind);
27681ad6265SDimitry Andric 
27781ad6265SDimitry Andric   /// Most of the trace data is stored here.
27881ad6265SDimitry Andric   std::vector<TraceItemStorage> m_item_data;
27981ad6265SDimitry Andric   /// The TraceItemKind for each trace item encoded as uint8_t. We don't include
28081ad6265SDimitry Andric   /// it in TraceItemStorage to avoid padding.
28181ad6265SDimitry Andric   std::vector<uint8_t> m_item_kinds;
28281ad6265SDimitry Andric 
283*972a253aSDimitry Andric   /// This map contains the TSCs of the decoded trace items. It maps
284*972a253aSDimitry Andric   /// `item index -> TSC`, where `item index` is the first index
285*972a253aSDimitry Andric   /// at which the mapped TSC first appears. We use this representation because
286*972a253aSDimitry Andric   /// TSCs are sporadic and we can think of them as ranges.
287*972a253aSDimitry Andric   std::map<uint64_t, TSCRange> m_tscs;
28881ad6265SDimitry Andric   /// This is the chronologically last TSC that has been added.
289*972a253aSDimitry Andric   llvm::Optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =
290*972a253aSDimitry Andric       llvm::None;
291*972a253aSDimitry Andric   /// This map contains the non-interpolated nanoseconds timestamps of the
292*972a253aSDimitry Andric   /// decoded trace items. It maps `item index -> nanoseconds`, where `item
293*972a253aSDimitry Andric   /// index` is the first index at which the mapped nanoseconds first appears.
294*972a253aSDimitry Andric   /// We use this representation because timestamps are sporadic and we think of
295*972a253aSDimitry Andric   /// them as ranges.
296*972a253aSDimitry Andric   std::map<uint64_t, NanosecondsRange> m_nanoseconds;
297*972a253aSDimitry Andric   llvm::Optional<std::map<uint64_t, NanosecondsRange>::iterator>
298*972a253aSDimitry Andric       m_last_nanoseconds = llvm::None;
29981ad6265SDimitry Andric 
300753f127fSDimitry Andric   // The cpu information is stored as a map. It maps `instruction 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.
305753f127fSDimitry Andric   llvm::Optional<uint64_t> m_last_cpu = llvm::None;
306753f127fSDimitry Andric 
307*972a253aSDimitry Andric   /// TSC -> nanos conversion utility.
308*972a253aSDimitry Andric   llvm::Optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
309*972a253aSDimitry Andric 
31081ad6265SDimitry Andric   /// Statistics of all tracing events.
31181ad6265SDimitry Andric   EventsStats m_events_stats;
31281ad6265SDimitry Andric   /// Statistics of libipt errors when decoding TSCs.
31381ad6265SDimitry Andric   LibiptErrorsStats m_tsc_errors_stats;
31481ad6265SDimitry Andric   /// Total amount of time spent decoding.
31581ad6265SDimitry Andric   std::chrono::milliseconds m_total_decoding_time{0};
316e8d8bef9SDimitry Andric };
317e8d8bef9SDimitry Andric 
318fe6060f1SDimitry Andric using DecodedThreadSP = std::shared_ptr<DecodedThread>;
319fe6060f1SDimitry Andric 
320e8d8bef9SDimitry Andric } // namespace trace_intel_pt
321e8d8bef9SDimitry Andric } // namespace lldb_private
322e8d8bef9SDimitry Andric 
323e8d8bef9SDimitry Andric #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
324