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   void log(llvm::raw_ostream &OS) const override;
57 
58 private:
59   int m_libipt_error_code;
60   lldb::addr_t m_address;
61 };
62 
63 /// Helper struct for building an instruction or error from the decoder.
64 /// It holds associated events and timing information.
65 struct DecodedInstruction {
66   DecodedInstruction() {
67     pt_insn.ip = LLDB_INVALID_ADDRESS;
68     libipt_error = pte_ok;
69   }
70 
71   DecodedInstruction(int libipt_error_code) : DecodedInstruction() {
72     libipt_error = libipt_error_code;
73   }
74 
75   /// \return \b true if and only if this struct holds a libipt error.
76   explicit operator bool() const;
77 
78   int libipt_error;
79   lldb::TraceEvents events = (lldb::TraceEvents)0;
80   llvm::Optional<uint64_t> tsc = llvm::None;
81   pt_insn pt_insn;
82 };
83 
84 /// \class DecodedThread
85 /// Class holding the instructions and function call hierarchy obtained from
86 /// decoding a trace, as well as a position cursor used when reverse debugging
87 /// the trace.
88 ///
89 /// Each decoded thread contains a cursor to the current position the user is
90 /// stopped at. See \a Trace::GetCursorPosition for more information.
91 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
92 public:
93   /// \class TscRange
94   /// Class that represents the trace range associated with a given TSC.
95   /// It provides efficient iteration to the previous or next TSC range in the
96   /// decoded trace.
97   ///
98   /// TSC timestamps are emitted by the decoder infrequently, which means
99   /// that each TSC covers a range of instruction indices, which can be used to
100   /// speed up TSC lookups.
101   class TscRange {
102   public:
103     /// Check if this TSC range includes the given instruction index.
104     bool InRange(size_t insn_index) const;
105 
106     /// Get the next range chronologically.
107     llvm::Optional<TscRange> Next() const;
108 
109     /// Get the previous range chronologically.
110     llvm::Optional<TscRange> Prev() const;
111 
112     /// Get the TSC value.
113     size_t GetTsc() const;
114     /// Get the smallest instruction index that has this TSC.
115     size_t GetStartInstructionIndex() const;
116     /// Get the largest instruction index that has this TSC.
117     size_t GetEndInstructionIndex() const;
118 
119   private:
120     friend class DecodedThread;
121 
122     TscRange(std::map<size_t, uint64_t>::const_iterator it,
123              const DecodedThread &decoded_thread);
124 
125     /// The iterator pointing to the beginning of the range.
126     std::map<size_t, uint64_t>::const_iterator m_it;
127     /// The largest instruction index that has this TSC.
128     size_t m_end_index;
129 
130     const DecodedThread *m_decoded_thread;
131   };
132 
133   // Struct holding counts for libipts errors;
134   struct LibiptErrorsStats {
135     // libipt error -> count
136     llvm::DenseMap<const char *, int> libipt_errors_counts;
137     size_t total_count = 0;
138 
139     void RecordError(int libipt_error_code);
140   };
141 
142   // Struct holding counts for events;
143   struct EventsStats {
144     /// A count for each individual event kind. We use an unordered map instead
145     /// of a DenseMap because DenseMap can't understand enums.
146     std::unordered_map<lldb::TraceEvents, size_t> events_counts;
147     size_t total_count = 0;
148     size_t total_instructions_with_events = 0;
149 
150     void RecordEventsForInstruction(lldb::TraceEvents events);
151   };
152 
153   DecodedThread(lldb::ThreadSP thread_sp);
154 
155   /// Utility constructor that initializes the trace with a provided error.
156   DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err);
157 
158   /// Append an instruction or a libipt error.
159   void Append(const DecodedInstruction &insn);
160 
161   /// Append an error signaling that decoding completely failed.
162   void SetAsFailed(llvm::Error &&error);
163 
164   /// Get a bitmask with the events that happened chronologically right before
165   /// the instruction pointed by the given instruction index, but after the
166   /// previous instruction.
167   lldb::TraceEvents GetEvents(int insn_index);
168 
169   /// Get the total number of instruction pointers from the decoded trace.
170   /// This will include instructions that indicate errors (or gaps) in the
171   /// trace. For an instruction error, you can access its underlying error
172   /// message with the \a GetErrorByInstructionIndex() method.
173   size_t GetInstructionsCount() const;
174 
175   /// \return
176   ///     The load address of the instruction at the given index, or \a
177   ///     LLDB_INVALID_ADDRESS if it is an error.
178   lldb::addr_t GetInstructionLoadAddress(size_t insn_index) const;
179 
180   /// Get the \a lldb::TraceInstructionControlFlowType categories of the
181   /// instruction.
182   ///
183   /// \return
184   ///     The control flow categories, or \b 0 if the instruction is an error.
185   lldb::TraceInstructionControlFlowType
186   GetInstructionControlFlowType(size_t insn_index) const;
187 
188   /// Construct the TSC range that covers the given instruction index.
189   /// This operation is O(logn) and should be used sparingly.
190   /// If the trace was collected with TSC support, all the instructions of
191   /// the trace will have associated TSCs. This means that this method will
192   /// only return \b llvm::None if there are no TSCs whatsoever in the trace.
193   ///
194   /// \param[in] insn_index
195   ///   The instruction index in question.
196   ///
197   /// \param[in] hint_range
198   ///   An optional range that might include the given index or might be a
199   ///   neighbor of it. It might help speed it traversals of the trace with
200   ///   short jumps.
201   llvm::Optional<TscRange> CalculateTscRange(
202       size_t insn_index,
203       const llvm::Optional<DecodedThread::TscRange> &hint_range) const;
204 
205   /// Check if an instruction given by its index is an error.
206   bool IsInstructionAnError(size_t insn_idx) const;
207 
208   /// Get the error associated with a given instruction index.
209   ///
210   /// \return
211   ///   The error message of \b nullptr if the given index
212   ///   points to a valid instruction.
213   const char *GetErrorByInstructionIndex(size_t ins_idx);
214 
215   /// Get a new cursor for the decoded thread.
216   lldb::TraceCursorUP GetCursor();
217 
218   /// Return an object with statistics of the TSC decoding errors that happened.
219   /// A TSC error is not a fatal error and doesn't create gaps in the trace.
220   /// Instead we only keep track of them as statistics.
221   ///
222   /// \return
223   ///   An object with the statistics of TSC decoding errors.
224   const LibiptErrorsStats &GetTscErrorsStats() const;
225 
226   /// Return an object with statistics of the trace events that happened.
227   ///
228   /// \return
229   ///   The stats object of all the events.
230   const EventsStats &GetEventsStats() const;
231 
232   /// Record an error decoding a TSC timestamp.
233   ///
234   /// See \a GetTscErrors() for more documentation.
235   ///
236   /// \param[in] libipt_error_code
237   ///   An error returned by the libipt library.
238   void RecordTscError(int libipt_error_code);
239 
240   /// The approximate size in bytes used by this instance,
241   /// including all the already decoded instructions.
242   size_t CalculateApproximateMemoryUsage() const;
243 
244   lldb::ThreadSP GetThread();
245 
246 private:
247   /// Append a decoding error given an llvm::Error.
248   void AppendError(llvm::Error &&error);
249 
250   /// Notify this class that the last added instruction or error has
251   /// an associated TSC.
252   void RecordTscForLastInstruction(uint64_t tsc);
253 
254   /// When adding new members to this class, make sure
255   /// to update \a CalculateApproximateMemoryUsage() accordingly.
256   lldb::ThreadSP m_thread_sp;
257   /// The low level storage of all instruction addresses. Each instruction has
258   /// an index in this vector and it will be used in other parts of the code.
259   std::vector<lldb::addr_t> m_instruction_ips;
260   /// The size in bytes of each instruction.
261   std::vector<uint8_t> m_instruction_sizes;
262   /// The libipt instruction class for each instruction.
263   std::vector<pt_insn_class> m_instruction_classes;
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_instruction_timestamps;
272   /// This is the chronologically last TSC that has been added.
273   llvm::Optional<uint64_t> m_last_tsc = llvm::None;
274   // This variables stores the messages of all the error instructions in the
275   // trace. It maps `instruction index -> error message`.
276   llvm::DenseMap<uint64_t, std::string> m_errors;
277   /// This variable stores the bitmask of events that happened right before
278   /// the instruction given as a key. It maps `instruction index -> events`.
279   llvm::DenseMap<uint64_t, lldb::TraceEvents> m_events;
280 
281   /// Statistics of all tracing events.
282   EventsStats m_events_stats;
283   /// Statistics of libipt errors when decoding TSCs.
284   LibiptErrorsStats m_tsc_errors_stats;
285   /// Total amount of time spent decoding.
286   std::chrono::milliseconds m_total_decoding_time{0};
287 };
288 
289 using DecodedThreadSP = std::shared_ptr<DecodedThread>;
290 
291 } // namespace trace_intel_pt
292 } // namespace lldb_private
293 
294 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
295