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