1 //===-- IntelPTCollector.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 liblldb_IntelPTCollector_H_
10 #define liblldb_IntelPTCollector_H_
11 
12 #include "lldb/Utility/Status.h"
13 #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
14 #include "lldb/lldb-types.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/DenseSet.h"
17 
18 #include <linux/perf_event.h>
19 #include <sys/mman.h>
20 #include <unistd.h>
21 
22 namespace lldb_private {
23 
24 namespace process_linux {
25 
26 /// This class keeps track of one tracing instance of
27 /// Intel(R) Processor Trace on Linux OS at thread level.
28 ///
29 /// The kernel interface for us is the perf_event_open.
30 class IntelPTThreadTrace;
31 typedef std::unique_ptr<IntelPTThreadTrace> IntelPTThreadTraceUP;
32 
33 class IntelPTThreadTrace {
34 
35   class munmap_delete {
36     size_t m_length;
37 
38   public:
39     munmap_delete(size_t length) : m_length(length) {}
40     void operator()(void *ptr) {
41       if (m_length)
42         munmap(ptr, m_length);
43     }
44   };
45 
46   class file_close {
47 
48   public:
49     file_close() = default;
50     void operator()(int *ptr) {
51       if (ptr == nullptr)
52         return;
53       if (*ptr == -1)
54         return;
55       close(*ptr);
56       std::default_delete<int>()(ptr);
57     }
58   };
59 
60   std::unique_ptr<perf_event_mmap_page, munmap_delete> m_mmap_meta;
61   std::unique_ptr<uint8_t, munmap_delete> m_mmap_aux;
62   std::unique_ptr<int, file_close> m_fd;
63   lldb::tid_t m_tid;
64 
65   /// Start tracing a thread
66   ///
67   /// \param[in] pid
68   ///     The pid of the process whose thread will be traced.
69   ///
70   /// \param[in] buffer_size
71   ///     Size of the thread buffer in bytes.
72   ///
73   /// \param[in] enable_tsc
74   ///     Whether to use enable TSC timestamps or not.
75   ///     More information in TraceIntelPT::GetStartConfigurationHelp().
76   ///
77   /// \param[in] psb_period
78   ///     This value defines the period in which PSB packets will be generated.
79   ///     More information in TraceIntelPT::GetStartConfigurationHelp().
80   ///
81   /// \return
82   ///     \a llvm::Error::success if tracing was successful, or an
83   ///     \a llvm::Error otherwise.
84   llvm::Error StartTrace(lldb::pid_t pid, lldb::tid_t tid, uint64_t buffer_size,
85                          bool enable_tsc, llvm::Optional<size_t> psb_period);
86 
87   llvm::MutableArrayRef<uint8_t> GetAuxBuffer() const;
88   llvm::MutableArrayRef<uint8_t> GetDataBuffer() const;
89 
90   IntelPTThreadTrace()
91       : m_mmap_meta(nullptr, munmap_delete(0)),
92         m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()) {}
93 
94 public:
95   /// Get the content of /proc/cpuinfo that can be later used to decode traces.
96   static llvm::Expected<llvm::ArrayRef<uint8_t>> GetCPUInfo();
97 
98   /// Start tracing a thread.
99   ///
100   /// See \a StartTrace.
101   ///
102   /// \return
103   ///   A \a IntelPTThreadTrace instance if tracing was successful, or
104   ///   an \a llvm::Error otherwise.
105   static llvm::Expected<IntelPTThreadTraceUP>
106   Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size, bool enable_tsc,
107          llvm::Optional<size_t> psb_period);
108 
109   /// Read the trace buffer of the currently traced thread.
110   ///
111   /// \param[in] offset
112   ///     Offset of the data to read.
113   ///
114   /// \param[in] size
115   ///     Number of bytes to read.
116   ///
117   /// \return
118   ///     A vector with the requested binary data. The vector will have the
119   ///     size of the requested \a size. Non-available positions will be
120   ///     filled with zeroes.
121   llvm::Expected<std::vector<uint8_t>> GetIntelPTBuffer(size_t offset,
122                                                         size_t size) const;
123 
124   Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
125                           size_t offset = 0) const;
126 
127   Status ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
128                            size_t offset = 0) const;
129 
130   /// Get the size in bytes of the aux section of the thread or process traced
131   /// by this object.
132   size_t GetTraceBufferSize() const;
133 
134   /// Read data from a cyclic buffer
135   ///
136   /// \param[in] [out] buf
137   ///     Destination buffer, the buffer will be truncated to written size.
138   ///
139   /// \param[in] src
140   ///     Source buffer which must be a cyclic buffer.
141   ///
142   /// \param[in] src_cyc_index
143   ///     The index pointer (start of the valid data in the cyclic
144   ///     buffer).
145   ///
146   /// \param[in] offset
147   ///     The offset to begin reading the data in the cyclic buffer.
148   static void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
149                                llvm::MutableArrayRef<uint8_t> src,
150                                size_t src_cyc_index, size_t offset);
151 
152   /// Return the thread-specific part of the jLLDBTraceGetState packet.
153   TraceThreadState GetState() const;
154 };
155 
156 /// Manages a list of thread traces.
157 class IntelPTThreadTraceCollection {
158 public:
159   IntelPTThreadTraceCollection(lldb::pid_t pid) : m_pid(pid) {}
160 
161   /// Dispose of all traces
162   void Clear();
163 
164   bool TracesThread(lldb::tid_t tid) const;
165 
166   size_t GetTotalBufferSize() const;
167 
168   std::vector<TraceThreadState> GetThreadStates() const;
169 
170   llvm::Expected<const IntelPTThreadTrace &>
171   GetTracedThread(lldb::tid_t tid) const;
172 
173   llvm::Error TraceStart(lldb::tid_t tid,
174                          const TraceIntelPTStartRequest &request);
175 
176   llvm::Error TraceStop(lldb::tid_t tid);
177 
178 private:
179   lldb::pid_t m_pid;
180   llvm::DenseMap<lldb::tid_t, IntelPTThreadTraceUP> m_thread_traces;
181   /// Total actual thread buffer size in bytes
182   size_t m_total_buffer_size = 0;
183 };
184 
185 /// Manages a "process trace" instance.
186 class IntelPTProcessTrace {
187 public:
188   IntelPTProcessTrace(lldb::pid_t pid, const TraceIntelPTStartRequest &request)
189       : m_thread_traces(pid), m_tracing_params(request) {}
190 
191   bool TracesThread(lldb::tid_t tid) const;
192 
193   const IntelPTThreadTraceCollection &GetThreadTraces() const;
194 
195   llvm::Error TraceStart(lldb::tid_t tid);
196 
197   llvm::Error TraceStop(lldb::tid_t tid);
198 
199 private:
200   IntelPTThreadTraceCollection m_thread_traces;
201   /// Params used to trace threads when the user started "process tracing".
202   TraceIntelPTStartRequest m_tracing_params;
203 };
204 
205 /// Main class that manages intel-pt process and thread tracing.
206 class IntelPTCollector {
207 public:
208   IntelPTCollector(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {}
209 
210   static bool IsSupported();
211 
212   /// If "process tracing" is enabled, then trace the given thread.
213   llvm::Error OnThreadCreated(lldb::tid_t tid);
214 
215   /// Stops tracing a tracing upon a destroy event.
216   llvm::Error OnThreadDestroyed(lldb::tid_t tid);
217 
218   /// Implementation of the jLLDBTraceStop packet
219   llvm::Error TraceStop(const TraceStopRequest &request);
220 
221   /// Implementation of the jLLDBTraceStart packet
222   ///
223   /// \param[in] process_threads
224   ///     A list of all threads owned by the process.
225   llvm::Error TraceStart(const TraceIntelPTStartRequest &request,
226                          const std::vector<lldb::tid_t> &process_threads);
227 
228   /// Implementation of the jLLDBTraceGetState packet
229   llvm::Expected<llvm::json::Value> GetState() const;
230 
231   /// Implementation of the jLLDBTraceGetBinaryData packet
232   llvm::Expected<std::vector<uint8_t>>
233   GetBinaryData(const TraceGetBinaryDataRequest &request) const;
234 
235   /// Dispose of all traces
236   void Clear();
237 
238 private:
239   llvm::Error TraceStop(lldb::tid_t tid);
240 
241   /// Start tracing a specific thread.
242   llvm::Error TraceStart(lldb::tid_t tid,
243                          const TraceIntelPTStartRequest &request);
244 
245   llvm::Expected<const IntelPTThreadTrace &>
246   GetTracedThread(lldb::tid_t tid) const;
247 
248   bool IsProcessTracingEnabled() const;
249 
250   void ClearProcessTracing();
251 
252   lldb::pid_t m_pid;
253   /// Threads traced due to "thread tracing"
254   IntelPTThreadTraceCollection m_thread_traces;
255   /// Threads traced due to "process tracing". Only one active "process tracing"
256   /// instance is assumed for a single process.
257   llvm::Optional<IntelPTProcessTrace> m_process_trace;
258 };
259 
260 } // namespace process_linux
261 } // namespace lldb_private
262 
263 #endif // liblldb_IntelPTCollector_H_
264