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 <utility>
13 #include <vector>
14 
15 #include "llvm/Support/Errc.h"
16 #include "llvm/Support/Error.h"
17 
18 #include "lldb/Target/Trace.h"
19 #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
20 
21 #include "intel-pt.h"
22 
23 namespace lldb_private {
24 namespace trace_intel_pt {
25 
26 /// libipt status utils
27 /// \{
28 bool IsLibiptError(int libipt_status);
29 
30 bool IsEndOfStream(int libipt_status);
31 
32 bool IsTscUnavailable(int libipt_status);
33 /// \}
34 
35 /// Class for representing a libipt decoding error.
36 class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
37 public:
38   static char ID;
39 
40   /// \param[in] libipt_error_code
41   ///     Negative number returned by libipt when decoding the trace and
42   ///     signaling errors.
43   ///
44   /// \param[in] address
45   ///     Optional instruction address. When decoding an individual instruction,
46   ///     its address might be available in the \a pt_insn object, and should be
47   ///     passed to this constructor. Other errors don't have an associated
48   ///     address.
49   IntelPTError(int libipt_error_code,
50                lldb::addr_t address = LLDB_INVALID_ADDRESS);
51 
52   std::error_code convertToErrorCode() const override {
53     return llvm::errc::not_supported;
54   }
55 
56   int GetLibiptErrorCode() const { return m_libipt_error_code; }
57 
58   void log(llvm::raw_ostream &OS) const override;
59 
60 private:
61   int m_libipt_error_code;
62   lldb::addr_t m_address;
63 };
64 
65 /// \class DecodedThread
66 /// Class holding the instructions and function call hierarchy obtained from
67 /// decoding a trace, as well as a position cursor used when reverse debugging
68 /// the trace.
69 ///
70 /// Each decoded thread contains a cursor to the current position the user is
71 /// stopped at. See \a Trace::GetCursorPosition for more information.
72 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
73 public:
74   /// \class TscRange
75   /// Class that represents the trace range associated with a given TSC.
76   /// It provides efficient iteration to the previous or next TSC range in the
77   /// decoded trace.
78   ///
79   /// TSC timestamps are emitted by the decoder infrequently, which means
80   /// that each TSC covers a range of instruction indices, which can be used to
81   /// speed up TSC lookups.
82   class TscRange {
83   public:
84     /// Check if this TSC range includes the given instruction index.
85     bool InRange(size_t insn_index) const;
86 
87     /// Get the next range chronologically.
88     llvm::Optional<TscRange> Next() const;
89 
90     /// Get the previous range chronologically.
91     llvm::Optional<TscRange> Prev() const;
92 
93     /// Get the TSC value.
94     size_t GetTsc() const;
95     /// Get the smallest instruction index that has this TSC.
96     size_t GetStartInstructionIndex() const;
97     /// Get the largest instruction index that has this TSC.
98     size_t GetEndInstructionIndex() const;
99 
100   private:
101     friend class DecodedThread;
102 
103     TscRange(std::map<size_t, uint64_t>::const_iterator it,
104              const DecodedThread &decoded_thread);
105 
106     /// The iterator pointing to the beginning of the range.
107     std::map<size_t, uint64_t>::const_iterator m_it;
108     /// The largest instruction index that has this TSC.
109     size_t m_end_index;
110 
111     const DecodedThread *m_decoded_thread;
112   };
113 
114   // Struct holding counts for libipts errors;
115   struct LibiptErrorsStats {
116     // libipt error -> count
117     llvm::DenseMap<const char *, int> libipt_errors_counts;
118     size_t total_count = 0;
119 
120     void RecordError(int libipt_error_code);
121   };
122 
123   // Struct holding counts for events;
124   struct EventsStats {
125     /// A count for each individual event kind. We use an unordered map instead
126     /// of a DenseMap because DenseMap can't understand enums.
127     std::unordered_map<lldb::TraceEvent, size_t> events_counts;
128     size_t total_count = 0;
129 
130     void RecordEvent(lldb::TraceEvent event);
131   };
132 
133   DecodedThread(lldb::ThreadSP thread_sp);
134 
135   /// Utility constructor that initializes the trace with a provided error.
136   DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err);
137 
138   /// Get the total number of instruction, errors and events from the decoded
139   /// trace.
140   int64_t GetItemsCount() const;
141 
142   /// Construct the TSC range that covers the given instruction index.
143   /// This operation is O(logn) and should be used sparingly.
144   /// If the trace was collected with TSC support, all the instructions of
145   /// the trace will have associated TSCs. This means that this method will
146   /// only return \b llvm::None if there are no TSCs whatsoever in the trace.
147   ///
148   /// \param[in] insn_index
149   ///   The instruction index in question.
150   ///
151   /// \param[in] hint_range
152   ///   An optional range that might include the given index or might be a
153   ///   neighbor of it. It might help speed it traversals of the trace with
154   ///   short jumps.
155   llvm::Optional<TscRange> CalculateTscRange(
156       size_t insn_index,
157       const llvm::Optional<DecodedThread::TscRange> &hint_range) const;
158 
159   /// \return
160   ///   The error associated with a given trace item.
161   const char *GetErrorByIndex(size_t item_index) const;
162 
163   /// \return
164   ///   The trace item kind given an item index.
165   lldb::TraceItemKind GetItemKindByIndex(size_t item_index) const;
166 
167   /// \return
168   ///   The underlying event type for the given trace item index.
169   lldb::TraceEvent GetEventByIndex(int item_index) const;
170 
171   /// Get the most recent CPU id before or at the given trace item index.
172   ///
173   /// \param[in] item_index
174   ///   The trace item index to compare with.
175   ///
176   /// \return
177   ///   The requested cpu id, or \a llvm::None if not available.
178   llvm::Optional<lldb::cpu_id_t> GetCPUByIndex(uint64_t item_index) const;
179 
180   /// \return
181   ///     The load address of the instruction at the given index.
182   lldb::addr_t GetInstructionLoadAddress(size_t item_index) const;
183 
184   /// Get a new cursor for the decoded thread.
185   lldb::TraceCursorUP CreateNewCursor();
186 
187   /// Return an object with statistics of the TSC decoding errors that happened.
188   /// A TSC error is not a fatal error and doesn't create gaps in the trace.
189   /// Instead we only keep track of them as statistics.
190   ///
191   /// \return
192   ///   An object with the statistics of TSC decoding errors.
193   const LibiptErrorsStats &GetTscErrorsStats() const;
194 
195   /// Return an object with statistics of the trace events that happened.
196   ///
197   /// \return
198   ///   The stats object of all the events.
199   const EventsStats &GetEventsStats() const;
200 
201   /// Record an error decoding a TSC timestamp.
202   ///
203   /// See \a GetTscErrors() for more documentation.
204   ///
205   /// \param[in] libipt_error_code
206   ///   An error returned by the libipt library.
207   void RecordTscError(int libipt_error_code);
208 
209   /// The approximate size in bytes used by this instance,
210   /// including all the already decoded instructions.
211   size_t CalculateApproximateMemoryUsage() const;
212 
213   lldb::ThreadSP GetThread();
214 
215   /// Notify this object that a new tsc has been seen.
216   /// If this a new TSC, an event will be created.
217   void NotifyTsc(uint64_t tsc);
218 
219   /// Notify this object that a CPU has been seen.
220   /// If this a new CPU, an event will be created.
221   void NotifyCPU(lldb::cpu_id_t cpu_id);
222 
223   /// Append a decoding error.
224   void AppendError(const IntelPTError &error);
225 
226   /// Append a custom decoding.
227   void AppendCustomError(llvm::StringRef error);
228 
229   /// Append an event.
230   void AppendEvent(lldb::TraceEvent);
231 
232   /// Append an instruction.
233   void AppendInstruction(const pt_insn &insn);
234 
235 private:
236   /// When adding new members to this class, make sure
237   /// to update \a CalculateApproximateMemoryUsage() accordingly.
238   lldb::ThreadSP m_thread_sp;
239 
240   /// We use a union to optimize the memory usage for the different kinds of
241   /// trace items.
242   union TraceItemStorage {
243     /// The load addresses of this item if it's an instruction.
244     uint64_t load_address;
245 
246     /// The event kind of this item if it's an event
247     lldb::TraceEvent event;
248 
249     /// The string message of this item if it's an error
250     const char *error;
251   };
252 
253   /// Create a new trace item.
254   ///
255   /// \return
256   ///   The index of the new item.
257   DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind);
258 
259   /// Most of the trace data is stored here.
260   std::vector<TraceItemStorage> m_item_data;
261   /// The TraceItemKind for each trace item encoded as uint8_t. We don't include
262   /// it in TraceItemStorage to avoid padding.
263   std::vector<uint8_t> m_item_kinds;
264 
265   /// This map contains the TSCs of the decoded instructions. It maps
266   /// `instruction index -> TSC`, where `instruction index` is the first index
267   /// at which the mapped TSC appears. We use this representation because TSCs
268   /// are sporadic and we can think of them as ranges. If TSCs are present in
269   /// the trace, all instructions will have an associated TSC, including the
270   /// first one. Otherwise, this map will be empty.
271   std::map<uint64_t, uint64_t> m_timestamps;
272   /// This is the chronologically last TSC that has been added.
273   llvm::Optional<uint64_t> m_last_tsc = llvm::None;
274 
275   // The cpu information is stored as a map. It maps `instruction index -> CPU`
276   // A CPU is associated with the next instructions that follow until the next
277   // cpu is seen.
278   std::map<uint64_t, lldb::cpu_id_t> m_cpus;
279   /// This is the chronologically last CPU ID.
280   llvm::Optional<uint64_t> m_last_cpu = llvm::None;
281 
282   /// Statistics of all tracing events.
283   EventsStats m_events_stats;
284   /// Statistics of libipt errors when decoding TSCs.
285   LibiptErrorsStats m_tsc_errors_stats;
286   /// Total amount of time spent decoding.
287   std::chrono::milliseconds m_total_decoding_time{0};
288 };
289 
290 using DecodedThreadSP = std::shared_ptr<DecodedThread>;
291 
292 } // namespace trace_intel_pt
293 } // namespace lldb_private
294 
295 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
296