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