1 //===-- ProcessorTrace.cpp ------------------------------------------------===//
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 #include <algorithm>
10 #include <fstream>
11 
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/Support/Error.h"
14 #include "llvm/Support/MathExtras.h"
15 
16 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
17 #include "ProcessorTrace.h"
18 #include "lldb/Host/linux/Support.h"
19 
20 #include <sys/ioctl.h>
21 #include <sys/syscall.h>
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 using namespace process_linux;
26 using namespace llvm;
27 
28 lldb::user_id_t ProcessorTraceMonitor::m_trace_num = 1;
29 
GetTraceConfig(TraceOptions & config) const30 Status ProcessorTraceMonitor::GetTraceConfig(TraceOptions &config) const {
31 #ifndef PERF_ATTR_SIZE_VER5
32   llvm_unreachable("perf event not supported");
33 #else
34   Status error;
35 
36   config.setType(lldb::TraceType::eTraceTypeProcessorTrace);
37   config.setMetaDataBufferSize(m_mmap_meta->data_size);
38 
39   config.setTraceBufferSize(m_mmap_meta->aux_size);
40 
41   error = GetCPUType(config);
42 
43   return error;
44 #endif
45 }
46 
StartTrace(lldb::pid_t pid,lldb::tid_t tid,const TraceOptions & config)47 Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
48                                          const TraceOptions &config) {
49 #ifndef PERF_ATTR_SIZE_VER5
50   llvm_unreachable("perf event not supported");
51 #else
52   Status error;
53   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
54 
55   LLDB_LOG(log, "called thread id {0}", tid);
56   uint64_t page_size = getpagesize();
57   uint64_t bufsize = config.getTraceBufferSize();
58   uint64_t metabufsize = config.getMetaDataBufferSize();
59 
60   uint64_t numpages = static_cast<uint64_t>(
61       llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
62   numpages = std::max<uint64_t>(1, numpages);
63   bufsize = page_size * numpages;
64 
65   numpages = static_cast<uint64_t>(
66       llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
67   metabufsize = page_size * numpages;
68 
69   perf_event_attr attr;
70   memset(&attr, 0, sizeof(attr));
71   attr.size = sizeof(attr);
72   attr.exclude_kernel = 1;
73   attr.sample_type = PERF_SAMPLE_TIME;
74   attr.sample_id_all = 1;
75   attr.exclude_hv = 1;
76   attr.exclude_idle = 1;
77   attr.mmap = 1;
78 
79   int intel_pt_type = 0;
80 
81   auto ret = llvm::MemoryBuffer::getFileAsStream(
82       "/sys/bus/event_source/devices/intel_pt/type");
83   if (!ret) {
84     LLDB_LOG(log, "failed to open Config file");
85     return ret.getError();
86   }
87 
88   StringRef rest = ret.get()->getBuffer();
89   if (rest.empty() || rest.trim().getAsInteger(10, intel_pt_type)) {
90     LLDB_LOG(log, "failed to read Config file");
91     error.SetErrorString("invalid file");
92     return error;
93   }
94 
95   rest.trim().getAsInteger(10, intel_pt_type);
96   LLDB_LOG(log, "intel pt type {0}", intel_pt_type);
97   attr.type = intel_pt_type;
98 
99   LLDB_LOG(log, "meta buffer size {0}", metabufsize);
100   LLDB_LOG(log, "buffer size {0} ", bufsize);
101 
102   if (error.Fail()) {
103     LLDB_LOG(log, "Status in custom config");
104 
105     return error;
106   }
107 
108   errno = 0;
109   auto fd =
110       syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
111   if (fd == -1) {
112     LLDB_LOG(log, "syscall error {0}", errno);
113     error.SetErrorString("perf event syscall Failed");
114     return error;
115   }
116 
117   m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
118 
119   errno = 0;
120   auto base =
121       mmap(nullptr, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
122 
123   if (base == MAP_FAILED) {
124     LLDB_LOG(log, "mmap base error {0}", errno);
125     error.SetErrorString("Meta buffer allocation failed");
126     return error;
127   }
128 
129   m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
130       reinterpret_cast<perf_event_mmap_page *>(base),
131       munmap_delete(metabufsize + page_size));
132 
133   m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
134   m_mmap_meta->aux_size = bufsize;
135 
136   errno = 0;
137   auto mmap_aux = mmap(nullptr, bufsize, PROT_READ, MAP_SHARED, fd,
138                        static_cast<long int>(m_mmap_meta->aux_offset));
139 
140   if (mmap_aux == MAP_FAILED) {
141     LLDB_LOG(log, "second mmap done {0}", errno);
142     error.SetErrorString("Trace buffer allocation failed");
143     return error;
144   }
145   m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
146       reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize));
147   return error;
148 #endif
149 }
150 
GetDataBuffer()151 llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetDataBuffer() {
152 #ifndef PERF_ATTR_SIZE_VER5
153   llvm_unreachable("perf event not supported");
154 #else
155   return MutableArrayRef<uint8_t>(
156       (reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
157        m_mmap_meta->data_offset),
158       m_mmap_meta->data_size);
159 #endif
160 }
161 
GetAuxBuffer()162 llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetAuxBuffer() {
163 #ifndef PERF_ATTR_SIZE_VER5
164   llvm_unreachable("perf event not supported");
165 #else
166   return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
167 #endif
168 }
169 
GetCPUType(TraceOptions & config)170 Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
171 
172   Status error;
173   uint64_t cpu_family = -1;
174   uint64_t model = -1;
175   uint64_t stepping = -1;
176   std::string vendor_id;
177 
178   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
179 
180   auto BufferOrError = getProcFile("cpuinfo");
181   if (!BufferOrError)
182     return BufferOrError.getError();
183 
184   LLDB_LOG(log, "GetCPUType Function");
185 
186   StringRef Rest = BufferOrError.get()->getBuffer();
187   while (!Rest.empty()) {
188     StringRef Line;
189     std::tie(Line, Rest) = Rest.split('\n');
190 
191     SmallVector<StringRef, 2> columns;
192     Line.split(columns, StringRef(":"), -1, false);
193 
194     if (columns.size() < 2)
195       continue; // continue searching
196 
197     columns[1] = columns[1].trim(" ");
198     if (columns[0].contains("cpu family") &&
199         columns[1].getAsInteger(10, cpu_family))
200       continue;
201 
202     else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
203       continue;
204 
205     else if (columns[0].contains("stepping") &&
206              columns[1].getAsInteger(10, stepping))
207       continue;
208 
209     else if (columns[0].contains("vendor_id")) {
210       vendor_id = columns[1].str();
211       if (!vendor_id.empty())
212         continue;
213     }
214     LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id);
215 
216     if ((cpu_family != static_cast<uint64_t>(-1)) &&
217         (model != static_cast<uint64_t>(-1)) &&
218         (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
219       auto params_dict = std::make_shared<StructuredData::Dictionary>();
220       params_dict->AddIntegerItem("cpu_family", cpu_family);
221       params_dict->AddIntegerItem("cpu_model", model);
222       params_dict->AddIntegerItem("cpu_stepping", stepping);
223       params_dict->AddStringItem("cpu_vendor", vendor_id);
224 
225       llvm::StringRef intel_custom_params_key("intel-pt");
226 
227       auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
228       intel_custom_params->AddItem(
229           intel_custom_params_key,
230           StructuredData::ObjectSP(std::move(params_dict)));
231 
232       config.setTraceParams(intel_custom_params);
233       return error; // we are done
234     }
235   }
236 
237   error.SetErrorString("cpu info not found");
238   return error;
239 }
240 
241 llvm::Expected<ProcessorTraceMonitorUP>
Create(lldb::pid_t pid,lldb::tid_t tid,const TraceOptions & config,bool useProcessSettings)242 ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
243                               const TraceOptions &config,
244                               bool useProcessSettings) {
245 
246   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
247 
248   Status error;
249   if (tid == LLDB_INVALID_THREAD_ID) {
250     error.SetErrorString("thread not specified");
251     return error.ToError();
252   }
253 
254   ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
255 
256   error = pt_monitor_up->StartTrace(pid, tid, config);
257   if (error.Fail())
258     return error.ToError();
259 
260   pt_monitor_up->SetThreadID(tid);
261 
262   if (useProcessSettings) {
263     pt_monitor_up->SetTraceID(0);
264   } else {
265     pt_monitor_up->SetTraceID(m_trace_num++);
266     LLDB_LOG(log, "Trace ID {0}", m_trace_num);
267   }
268   return std::move(pt_monitor_up);
269 }
270 
271 Status
ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> & buffer,size_t offset)272 ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
273                                         size_t offset) {
274 #ifndef PERF_ATTR_SIZE_VER5
275   llvm_unreachable("perf event not supported");
276 #else
277   // Disable the perf event to force a flush out of the CPU's internal buffer.
278   // Besides, we can guarantee that the CPU won't override any data as we are
279   // reading the buffer.
280   //
281   // The Intel documentation says:
282   //
283   // Packets are first buffered internally and then written out asynchronously.
284   // To collect packet output for postprocessing, a collector needs first to
285   // ensure that all packet data has been flushed from internal buffers.
286   // Software can ensure this by stopping packet generation by clearing
287   // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
288   // Section 35.2.7.2).
289   //
290   // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
291   // in the man page of perf_event_open.
292   ioctl(*m_fd, PERF_EVENT_IOC_DISABLE);
293 
294   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
295   Status error;
296   uint64_t head = m_mmap_meta->aux_head;
297 
298   LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
299 
300   /**
301    * When configured as ring buffer, the aux buffer keeps wrapping around
302    * the buffer and its not possible to detect how many times the buffer
303    * wrapped. Initially the buffer is filled with zeros,as shown below
304    * so in order to get complete buffer we first copy firstpartsize, followed
305    * by any left over part from beginning to aux_head
306    *
307    * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
308    *                 aux_head->||<- firstpartsize  ->|
309    *
310    * */
311 
312   ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
313   LLDB_LOG(log, "ReadCyclic BUffer Done");
314 
315   // Reenable tracing now we have read the buffer
316   ioctl(*m_fd, PERF_EVENT_IOC_ENABLE);
317   return error;
318 #endif
319 }
320 
321 Status
ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> & buffer,size_t offset)322 ProcessorTraceMonitor::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
323                                          size_t offset) {
324 #ifndef PERF_ATTR_SIZE_VER5
325   llvm_unreachable("perf event not supported");
326 #else
327   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
328   uint64_t bytes_remaining = buffer.size();
329   Status error;
330 
331   uint64_t head = m_mmap_meta->data_head;
332 
333   /*
334    * The data buffer and aux buffer have different implementations
335    * with respect to their definition of head pointer. In the case
336    * of Aux data buffer the head always wraps around the aux buffer
337    * and we don't need to care about it, whereas the data_head keeps
338    * increasing and needs to be wrapped by modulus operator
339    */
340 
341   LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
342 
343   auto data_buffer = GetDataBuffer();
344 
345   if (head > data_buffer.size()) {
346     head = head % data_buffer.size();
347     LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
348 
349     ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
350     bytes_remaining -= buffer.size();
351   } else {
352     LLDB_LOG(log, "Head - {0}", head);
353     if (offset >= head) {
354       LLDB_LOG(log, "Invalid Offset ");
355       error.SetErrorString("invalid offset");
356       buffer = buffer.slice(buffer.size());
357       return error;
358     }
359 
360     auto data = data_buffer.slice(offset, (head - offset));
361     auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
362     bytes_remaining -= (remaining - buffer.begin());
363   }
364   buffer = buffer.drop_back(bytes_remaining);
365   return error;
366 #endif
367 }
368 
ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> & dst,llvm::MutableArrayRef<uint8_t> src,size_t src_cyc_index,size_t offset)369 void ProcessorTraceMonitor::ReadCyclicBuffer(
370     llvm::MutableArrayRef<uint8_t> &dst, llvm::MutableArrayRef<uint8_t> src,
371     size_t src_cyc_index, size_t offset) {
372 
373   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
374 
375   if (dst.empty() || src.empty()) {
376     dst = dst.drop_back(dst.size());
377     return;
378   }
379 
380   if (dst.data() == nullptr || src.data() == nullptr) {
381     dst = dst.drop_back(dst.size());
382     return;
383   }
384 
385   if (src_cyc_index > src.size()) {
386     dst = dst.drop_back(dst.size());
387     return;
388   }
389 
390   if (offset >= src.size()) {
391     LLDB_LOG(log, "Too Big offset ");
392     dst = dst.drop_back(dst.size());
393     return;
394   }
395 
396   llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
397       src.slice(src_cyc_index), src.take_front(src_cyc_index)};
398 
399   if (offset > parts[0].size()) {
400     parts[1] = parts[1].slice(offset - parts[0].size());
401     parts[0] = parts[0].drop_back(parts[0].size());
402   } else if (offset == parts[0].size()) {
403     parts[0] = parts[0].drop_back(parts[0].size());
404   } else {
405     parts[0] = parts[0].slice(offset);
406   }
407   auto next = dst.begin();
408   auto bytes_left = dst.size();
409   for (auto part : parts) {
410     size_t chunk_size = std::min(part.size(), bytes_left);
411     next = std::copy_n(part.begin(), chunk_size, next);
412     bytes_left -= chunk_size;
413   }
414   dst = dst.drop_back(bytes_left);
415 }
416