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 DecodedThread
55 /// Class holding the instructions and function call hierarchy obtained from
56 /// decoding a trace, as well as a position cursor used when reverse debugging
57 /// the trace.
58 ///
59 /// Each decoded thread contains a cursor to the current position the user is
60 /// stopped at. See \a Trace::GetCursorPosition for more information.
61 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
62 public:
63   /// \class TscRange
64   /// Class that represents the trace range associated with a given TSC.
65   /// It provides efficient iteration to the previous or next TSC range in the
66   /// decoded trace.
67   ///
68   /// TSC timestamps are emitted by the decoder infrequently, which means
69   /// that each TSC covers a range of instruction indices, which can be used to
70   /// speed up TSC lookups.
71   class TscRange {
72   public:
73     /// Check if this TSC range includes the given instruction index.
74     bool InRange(size_t insn_index);
75 
76     /// Get the next range chronologically.
77     llvm::Optional<TscRange> Next();
78 
79     /// Get the previous range chronologically.
80     llvm::Optional<TscRange> Prev();
81 
82     /// Get the TSC value.
83     size_t GetTsc() const;
84     /// Get the smallest instruction index that has this TSC.
85     size_t GetStartInstructionIndex() const;
86     /// Get the largest instruction index that has this TSC.
87     size_t GetEndInstructionIndex() const;
88 
89   private:
90     friend class DecodedThread;
91 
92     TscRange(std::map<size_t, uint64_t>::const_iterator it,
93              const DecodedThread &decoded_thread);
94 
95     /// The iterator pointing to the beginning of the range.
96     std::map<size_t, uint64_t>::const_iterator m_it;
97     /// The largest instruction index that has this TSC.
98     size_t m_end_index;
99 
100     const DecodedThread *m_decoded_thread;
101   };
102 
103   // Struct holding counts for libipts errors;
104   struct LibiptErrors {
105     // libipt error -> count
106     llvm::DenseMap<const char *, int> libipt_errors;
107     int total_count = 0;
108 
109     void RecordError(int libipt_error_code);
110   };
111 
112   DecodedThread(lldb::ThreadSP thread_sp);
113 
114   /// Utility constructor that initializes the trace with a provided error.
115   DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err);
116 
117   /// Append a successfully decoded instruction.
118   void AppendInstruction(const pt_insn &instruction);
119 
120   /// Append a sucessfully decoded instruction with an associated TSC timestamp.
121   void AppendInstruction(const pt_insn &instruction, uint64_t tsc);
122 
123   /// Append a decoding error (i.e. an instruction that failed to be decoded).
124   void AppendError(llvm::Error &&error);
125 
126   /// Append a decoding error with a corresponding TSC.
127   void AppendError(llvm::Error &&error, uint64_t tsc);
128 
129   /// Get the total number of instruction pointers from the decoded trace.
130   /// This will include instructions that indicate errors (or gaps) in the
131   /// trace. For an instruction error, you can access its underlying error
132   /// message with the \a GetErrorByInstructionIndex() method.
133   size_t GetInstructionsCount() const;
134 
135   /// \return
136   ///     The load address of the instruction at the given index, or \a
137   ///     LLDB_INVALID_ADDRESS if it is an error.
138   lldb::addr_t GetInstructionLoadAddress(size_t insn_index) const;
139 
140   /// Get the \a lldb::TraceInstructionControlFlowType categories of the
141   /// instruction.
142   ///
143   /// \return
144   ///     The control flow categories, or \b 0 if the instruction is an error.
145   lldb::TraceInstructionControlFlowType
146   GetInstructionControlFlowType(size_t insn_index) const;
147 
148   /// Construct the TSC range that covers the given instruction index.
149   /// This operation is O(logn) and should be used sparingly.
150   /// If the trace was collected with TSC support, all the instructions of
151   /// the trace will have associated TSCs. This means that this method will
152   /// only return \b llvm::None if there are no TSCs whatsoever in the trace.
153   llvm::Optional<TscRange> CalculateTscRange(size_t insn_index) const;
154 
155   /// Check if an instruction given by its index is an error.
156   bool IsInstructionAnError(size_t insn_idx) const;
157 
158   /// Get the error associated with a given instruction index.
159   ///
160   /// \return
161   ///   The error message of \b nullptr if the given index
162   ///   points to a valid instruction.
163   const char *GetErrorByInstructionIndex(size_t ins_idx);
164 
165   /// Get a new cursor for the decoded thread.
166   lldb::TraceCursorUP GetCursor();
167 
168   /// Set the size in bytes of the corresponding Intel PT raw trace.
169   void SetRawTraceSize(size_t size);
170 
171   /// Get the size in bytes of the corresponding Intel PT raw trace.
172   ///
173   /// \return
174   ///   The size of the trace, or \b llvm::None if not available.
175   llvm::Optional<size_t> GetRawTraceSize() const;
176 
177   /// Return the number of TSC decoding errors that happened. A TSC error
178   /// is not a fatal error and doesn't create gaps in the trace. Instead
179   /// we only keep track of them as a statistic.
180   ///
181   /// \return
182   ///   The number of TSC decoding errors.
183   const LibiptErrors &GetTscErrors() const;
184 
185   /// Record an error decoding a TSC timestamp.
186   ///
187   /// See \a GetTscErrors() for more documentation.
188   ///
189   /// \param[in] libipt_error_code
190   ///   An error returned by the libipt library.
191   void RecordTscError(int libipt_error_code);
192 
193   /// The approximate size in bytes used by this instance,
194   /// including all the already decoded instructions.
195   size_t CalculateApproximateMemoryUsage() const;
196 
197   lldb::ThreadSP GetThread();
198 
199 private:
200   /// Notify this class that the last added instruction or error has
201   /// an associated TSC.
202   void RecordTscForLastInstruction(uint64_t tsc);
203 
204   /// When adding new members to this class, make sure
205   /// to update \a CalculateApproximateMemoryUsage() accordingly.
206   lldb::ThreadSP m_thread_sp;
207   /// The low level storage of all instruction addresses. Each instruction has
208   /// an index in this vector and it will be used in other parts of the code.
209   std::vector<lldb::addr_t> m_instruction_ips;
210   /// The size in bytes of each instruction.
211   std::vector<uint8_t> m_instruction_sizes;
212   /// The libipt instruction class for each instruction.
213   std::vector<pt_insn_class> m_instruction_classes;
214 
215   /// This map contains the TSCs of the decoded instructions. It maps
216   /// `instruction index -> TSC`, where `instruction index` is the first index
217   /// at which the mapped TSC appears. We use this representation because TSCs
218   /// are sporadic and we can think of them as ranges. If TSCs are present in
219   /// the trace, all instructions will have an associated TSC, including the
220   /// first one. Otherwise, this map will be empty.
221   std::map<size_t, uint64_t> m_instruction_timestamps;
222   /// This is the chronologically last TSC that has been added.
223   llvm::Optional<uint64_t> m_last_tsc = llvm::None;
224   // This variables stores the messages of all the error instructions in the
225   // trace. It maps `instruction index -> error message`.
226   llvm::DenseMap<uint64_t, std::string> m_errors;
227   /// The size in bytes of the raw buffer before decoding. It might be None if
228   /// the decoding failed.
229   llvm::Optional<size_t> m_raw_trace_size;
230   /// All occurrences of libipt errors when decoding TSCs.
231   LibiptErrors m_tsc_errors;
232 };
233 
234 using DecodedThreadSP = std::shared_ptr<DecodedThread>;
235 
236 } // namespace trace_intel_pt
237 } // namespace lldb_private
238 
239 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
240