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 /// Class for representing a libipt decoding error.
27 class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
28 public:
29   static char ID;
30 
31   /// \param[in] libipt_error_code
32   ///     Negative number returned by libipt when decoding the trace and
33   ///     signaling errors.
34   ///
35   /// \param[in] address
36   ///     Optional instruction address. When decoding an individual instruction,
37   ///     its address might be available in the \a pt_insn object, and should be
38   ///     passed to this constructor. Other errors don't have an associated
39   ///     address.
40   IntelPTError(int libipt_error_code,
41                lldb::addr_t address = LLDB_INVALID_ADDRESS);
42 
43   std::error_code convertToErrorCode() const override {
44     return llvm::errc::not_supported;
45   }
46 
47   void log(llvm::raw_ostream &OS) const override;
48 
49 private:
50   int m_libipt_error_code;
51   lldb::addr_t m_address;
52 };
53 
54 /// \class IntelPTInstruction
55 /// An instruction obtained from decoding a trace. It is either an actual
56 /// instruction or an error indicating a gap in the trace.
57 ///
58 /// Gaps in the trace can come in a few flavors:
59 ///   - tracing gaps (e.g. tracing was paused and then resumed)
60 ///   - tracing errors (e.g. buffer overflow)
61 ///   - decoding errors (e.g. some memory region couldn't be decoded)
62 /// As mentioned, any gap is represented as an error in this class.
63 class IntelPTInstruction {
64 public:
65   IntelPTInstruction(const pt_insn &pt_insn)
66       : m_pt_insn(pt_insn), m_is_error(false) {}
67 
68   /// Error constructor
69   IntelPTInstruction();
70 
71   /// Check if this object represents an error (i.e. a gap).
72   ///
73   /// \return
74   ///     Whether this object represents an error.
75   bool IsError() const;
76 
77   /// \return
78   ///     The instruction pointer address, or \a LLDB_INVALID_ADDRESS if it is
79   ///     an error.
80   lldb::addr_t GetLoadAddress() const;
81 
82   /// Get the size in bytes of an instance of this class
83   static size_t GetMemoryUsage();
84 
85   /// Get the \a lldb::TraceInstructionControlFlowType categories of the
86   /// instruction.
87   ///
88   /// \param[in] next_load_address
89   ///     The address of the next instruction in the trace or \b
90   ///     LLDB_INVALID_ADDRESS if not available.
91   ///
92   /// \return
93   ///     The control flow categories, or \b 0 if the instruction is an error.
94   lldb::TraceInstructionControlFlowType
95   GetControlFlowType(lldb::addr_t next_load_address) const;
96 
97   IntelPTInstruction(IntelPTInstruction &&other) = default;
98 
99 private:
100   IntelPTInstruction(const IntelPTInstruction &other) = delete;
101   const IntelPTInstruction &operator=(const IntelPTInstruction &other) = delete;
102 
103   // When adding new members to this class, make sure to update
104   // IntelPTInstruction::GetMemoryUsage() if needed.
105   pt_insn m_pt_insn;
106   bool m_is_error;
107 };
108 
109 /// \class DecodedThread
110 /// Class holding the instructions and function call hierarchy obtained from
111 /// decoding a trace, as well as a position cursor used when reverse debugging
112 /// the trace.
113 ///
114 /// Each decoded thread contains a cursor to the current position the user is
115 /// stopped at. See \a Trace::GetCursorPosition for more information.
116 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
117 public:
118   /// \class TscRange
119   /// Class that represents the trace range associated with a given TSC.
120   /// It provides efficient iteration to the previous or next TSC range in the
121   /// decoded trace.
122   ///
123   /// TSC timestamps are emitted by the decoder infrequently, which means
124   /// that each TSC covers a range of instruction indices, which can be used to
125   /// speed up TSC lookups.
126   class TscRange {
127   public:
128     /// Check if this TSC range includes the given instruction index.
129     bool InRange(size_t insn_index);
130 
131     /// Get the next range chronologically.
132     llvm::Optional<TscRange> Next();
133 
134     /// Get the previous range chronologically.
135     llvm::Optional<TscRange> Prev();
136 
137     /// Get the TSC value.
138     size_t GetTsc() const;
139     /// Get the smallest instruction index that has this TSC.
140     size_t GetStartInstructionIndex() const;
141     /// Get the largest instruction index that has this TSC.
142     size_t GetEndInstructionIndex() const;
143 
144   private:
145     friend class DecodedThread;
146 
147     TscRange(std::map<size_t, uint64_t>::const_iterator it,
148              const DecodedThread &decoded_thread);
149 
150     /// The iterator pointing to the beginning of the range.
151     std::map<size_t, uint64_t>::const_iterator m_it;
152     /// The largest instruction index that has this TSC.
153     size_t m_end_index;
154 
155     const DecodedThread *m_decoded_thread;
156   };
157 
158   // Struct holding counts for libipts errors;
159   struct LibiptErrors {
160     // libipt error -> count
161     llvm::DenseMap<const char *, int> libipt_errors;
162     int total_count = 0;
163 
164     void RecordError(int libipt_error_code);
165   };
166 
167   DecodedThread(lldb::ThreadSP thread_sp);
168 
169   /// Utility constructor that initializes the trace with a provided error.
170   DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err);
171 
172   /// Append a successfully decoded instruction.
173   void AppendInstruction(const pt_insn &instruction);
174 
175   /// Append a sucessfully decoded instruction with an associated TSC timestamp.
176   void AppendInstruction(const pt_insn &instruction, uint64_t tsc);
177 
178   /// Append a decoding error (i.e. an instruction that failed to be decoded).
179   void AppendError(llvm::Error &&error);
180 
181   /// Get the instructions from the decoded trace. Some of them might indicate
182   /// errors (i.e. gaps) in the trace. For an instruction error, you can access
183   /// its underlying error message with the \a GetErrorByInstructionIndex()
184   /// method.
185   ///
186   /// \return
187   ///   The instructions of the trace.
188   llvm::ArrayRef<IntelPTInstruction> GetInstructions() const;
189 
190   /// Construct the TSC range that covers the given instruction index.
191   /// This operation is O(logn) and should be used sparingly.
192   /// If the trace was collected with TSC support, all the instructions of
193   /// the trace will have associated TSCs. This means that this method will
194   /// only return \b llvm::None if there are no TSCs whatsoever in the trace.
195   llvm::Optional<TscRange> CalculateTscRange(size_t insn_index) const;
196 
197   /// Check if an instruction given by its index is an error.
198   bool IsInstructionAnError(size_t insn_idx) const;
199 
200   /// Get the error associated with a given instruction index.
201   ///
202   /// \return
203   ///   The error message of \b nullptr if the given index
204   ///   points to a valid instruction.
205   const char *GetErrorByInstructionIndex(size_t ins_idx);
206 
207   /// Append a decoding error with a corresponding TSC.
208   void AppendError(llvm::Error &&error, uint64_t TSC);
209 
210   /// Record an error decoding a TSC timestamp.
211   ///
212   /// See \a GetTscErrors() for more documentation.
213   ///
214   /// \param[in] libipt_error_code
215   ///   An error returned by the libipt library.
216   void RecordTscError(int libipt_error_code);
217 
218   /// Get a new cursor for the decoded thread.
219   lldb::TraceCursorUP GetCursor();
220 
221   /// Set the size in bytes of the corresponding Intel PT raw trace.
222   void SetRawTraceSize(size_t size);
223 
224   /// Get the size in bytes of the corresponding Intel PT raw trace.
225   ///
226   /// \return
227   ///   The size of the trace, or \b llvm::None if not available.
228   llvm::Optional<size_t> GetRawTraceSize() const;
229 
230   /// Return the number of TSC decoding errors that happened. A TSC error
231   /// is not a fatal error and doesn't create gaps in the trace. Instead
232   /// we only keep track of them as a statistic.
233   ///
234   /// \return
235   ///   The number of TSC decoding errors.
236   const LibiptErrors &GetTscErrors() const;
237 
238   /// The approximate size in bytes used by this instance,
239   /// including all the already decoded instructions.
240   size_t CalculateApproximateMemoryUsage() const;
241 
242   lldb::ThreadSP GetThread();
243 
244 private:
245   /// Notify this class that the last added instruction or error has
246   /// an associated TSC.
247   void RecordTscForLastInstruction(uint64_t tsc);
248 
249   /// When adding new members to this class, make sure
250   /// to update \a CalculateApproximateMemoryUsage() accordingly.
251   lldb::ThreadSP m_thread_sp;
252   /// The low level storage of all instruction addresses. Each instruction has
253   /// an index in this vector and it will be used in other parts of the code.
254   std::vector<IntelPTInstruction> m_instructions;
255   /// This map contains the TSCs of the decoded instructions. It maps
256   /// `instruction index -> TSC`, where `instruction index` is the first index
257   /// at which the mapped TSC appears. We use this representation because TSCs
258   /// are sporadic and we can think of them as ranges. If TSCs are present in
259   /// the trace, all instructions will have an associated TSC, including the
260   /// first one. Otherwise, this map will be empty.
261   std::map<size_t, uint64_t> m_instruction_timestamps;
262   /// This is the chronologically last TSC that has been added.
263   llvm::Optional<uint64_t> m_last_tsc = llvm::None;
264   // This variables stores the messages of all the error instructions in the
265   // trace. It maps `instruction index -> error message`.
266   llvm::DenseMap<uint64_t, std::string> m_errors;
267   /// The size in bytes of the raw buffer before decoding. It might be None if
268   /// the decoding failed.
269   llvm::Optional<size_t> m_raw_trace_size;
270   /// All occurrences of libipt errors when decoding TSCs.
271   LibiptErrors m_tsc_errors;
272 };
273 
274 using DecodedThreadSP = std::shared_ptr<DecodedThread>;
275 
276 } // namespace trace_intel_pt
277 } // namespace lldb_private
278 
279 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
280