1 //===-- IntelPTSingleBufferTrace.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 "IntelPTSingleBufferTrace.h"
10 
11 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
12 #include "lldb/Utility/Status.h"
13 #include "lldb/Utility/StreamString.h"
14 
15 #include "llvm/Support/Host.h"
16 #include "llvm/Support/MemoryBuffer.h"
17 
18 #include <sstream>
19 
20 #include <linux/perf_event.h>
21 #include <sys/ioctl.h>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 using namespace process_linux;
28 using namespace llvm;
29 
30 const char *kOSEventIntelPTTypeFile =
31     "/sys/bus/event_source/devices/intel_pt/type";
32 
33 const char *kPSBPeriodCapFile =
34     "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";
35 
36 const char *kPSBPeriodValidValuesFile =
37     "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";
38 
39 const char *kPSBPeriodBitOffsetFile =
40     "/sys/bus/event_source/devices/intel_pt/format/psb_period";
41 
42 const char *kTSCBitOffsetFile =
43     "/sys/bus/event_source/devices/intel_pt/format/tsc";
44 
45 enum IntelPTConfigFileType {
46   Hex = 0,
47   // 0 or 1
48   ZeroOne,
49   Decimal,
50   // a bit index file always starts with the prefix config: following by an int,
51   // which represents the offset of the perf_event_attr.config value where to
52   // store a given configuration.
53   BitOffset
54 };
55 
56 static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
57                                                 IntelPTConfigFileType type) {
58   ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
59       MemoryBuffer::getFileAsStream(file);
60 
61   if (!stream)
62     return createStringError(inconvertibleErrorCode(),
63                              "Can't open the file '%s'", file);
64 
65   uint32_t value = 0;
66   StringRef text_buffer = stream.get()->getBuffer();
67 
68   if (type == BitOffset) {
69     const char *prefix = "config:";
70     if (!text_buffer.startswith(prefix))
71       return createStringError(inconvertibleErrorCode(),
72                                "The file '%s' contents doesn't start with '%s'",
73                                file, prefix);
74     text_buffer = text_buffer.substr(strlen(prefix));
75   }
76 
77   auto getRadix = [&]() {
78     switch (type) {
79     case Hex:
80       return 16;
81     case ZeroOne:
82     case Decimal:
83     case BitOffset:
84       return 10;
85     }
86     llvm_unreachable("Fully covered switch above!");
87   };
88 
89   auto createError = [&](const char *expected_value_message) {
90     return createStringError(
91         inconvertibleErrorCode(),
92         "The file '%s' has an invalid value. It should be %s.", file,
93         expected_value_message);
94   };
95 
96   if (text_buffer.trim().consumeInteger(getRadix(), value) ||
97       (type == ZeroOne && value != 0 && value != 1)) {
98     switch (type) {
99     case Hex:
100       return createError("an unsigned hexadecimal int");
101     case ZeroOne:
102       return createError("0 or 1");
103     case Decimal:
104     case BitOffset:
105       return createError("an unsigned decimal int");
106     }
107   }
108   return value;
109 }
110 
111 /// Return the Linux perf event type for Intel PT.
112 Expected<uint32_t> process_linux::GetIntelPTOSEventType() {
113   return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
114                                IntelPTConfigFileType::Decimal);
115 }
116 
117 static Error CheckPsbPeriod(size_t psb_period) {
118   Expected<uint32_t> cap =
119       ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne);
120   if (!cap)
121     return cap.takeError();
122   if (*cap == 0)
123     return createStringError(inconvertibleErrorCode(),
124                              "psb_period is unsupported in the system.");
125 
126   Expected<uint32_t> valid_values = ReadIntelPTConfigFile(
127       kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex);
128   if (!valid_values)
129     return valid_values.takeError();
130 
131   if (valid_values.get() & (1 << psb_period))
132     return Error::success();
133 
134   std::ostringstream error;
135   // 0 is always a valid value
136   error << "Invalid psb_period. Valid values are: 0";
137   uint32_t mask = valid_values.get();
138   while (mask) {
139     int index = __builtin_ctz(mask);
140     if (index > 0)
141       error << ", " << index;
142     // clear the lowest bit
143     mask &= mask - 1;
144   }
145   error << ".";
146   return createStringError(inconvertibleErrorCode(), error.str().c_str());
147 }
148 
149 #ifdef PERF_ATTR_SIZE_VER5
150 static Expected<uint64_t>
151 GeneratePerfEventConfigValue(bool enable_tsc, Optional<uint64_t> psb_period) {
152   uint64_t config = 0;
153   // tsc is always supported
154   if (enable_tsc) {
155     if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
156             kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))
157       config |= 1 << *offset;
158     else
159       return offset.takeError();
160   }
161   if (psb_period) {
162     if (Error error = CheckPsbPeriod(*psb_period))
163       return std::move(error);
164 
165     if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
166             kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset))
167       config |= *psb_period << *offset;
168     else
169       return offset.takeError();
170   }
171   return config;
172 }
173 
174 /// Create a \a perf_event_attr configured for
175 /// an IntelPT event.
176 ///
177 /// \return
178 ///   A \a perf_event_attr if successful,
179 ///   or an \a llvm::Error otherwise.
180 static Expected<perf_event_attr>
181 CreateIntelPTPerfEventConfiguration(bool enable_tsc,
182                                     llvm::Optional<uint64_t> psb_period) {
183   perf_event_attr attr;
184   memset(&attr, 0, sizeof(attr));
185   attr.size = sizeof(attr);
186   attr.exclude_kernel = 1;
187   attr.sample_type = PERF_SAMPLE_TIME;
188   attr.sample_id_all = 1;
189   attr.exclude_hv = 1;
190   attr.exclude_idle = 1;
191   attr.mmap = 1;
192 
193   if (Expected<uint64_t> config_value =
194           GeneratePerfEventConfigValue(enable_tsc, psb_period))
195     attr.config = *config_value;
196   else
197     return config_value.takeError();
198 
199   if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType())
200     attr.type = *intel_pt_type;
201   else
202     return intel_pt_type.takeError();
203 
204   return attr;
205 }
206 #endif
207 
208 size_t IntelPTSingleBufferTrace::GetTraceBufferSize() const {
209   return m_perf_event.GetAuxBuffer().size();
210 }
211 
212 Expected<std::vector<uint8_t>>
213 IntelPTSingleBufferTrace::GetTraceBuffer(size_t offset, size_t size) const {
214   auto fd = m_perf_event.GetFd();
215   perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
216   // Disable the perf event to force a flush out of the CPU's internal buffer.
217   // Besides, we can guarantee that the CPU won't override any data as we are
218   // reading the buffer.
219   //
220   // The Intel documentation says:
221   //
222   // Packets are first buffered internally and then written out asynchronously.
223   // To collect packet output for postprocessing, a collector needs first to
224   // ensure that all packet data has been flushed from internal buffers.
225   // Software can ensure this by stopping packet generation by clearing
226   // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
227   // Section 35.2.7.2).
228   //
229   // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
230   // in the man page of perf_event_open.
231   ioctl(fd, PERF_EVENT_IOC_DISABLE);
232 
233   Log *log = GetLog(POSIXLog::Trace);
234   Status error;
235   uint64_t head = mmap_metadata.aux_head;
236 
237   LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.aux_size, head);
238 
239   /**
240    * When configured as ring buffer, the aux buffer keeps wrapping around
241    * the buffer and its not possible to detect how many times the buffer
242    * wrapped. Initially the buffer is filled with zeros,as shown below
243    * so in order to get complete buffer we first copy firstpartsize, followed
244    * by any left over part from beginning to aux_head
245    *
246    * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
247    *                 aux_head->||<- firstpartsize  ->|
248    *
249    * */
250 
251   std::vector<uint8_t> data(size, 0);
252   MutableArrayRef<uint8_t> buffer(data);
253   ReadCyclicBuffer(buffer, m_perf_event.GetAuxBuffer(),
254                    static_cast<size_t>(head), offset);
255 
256   // Reenable tracing now we have read the buffer
257   ioctl(fd, PERF_EVENT_IOC_ENABLE);
258   return data;
259 }
260 
261 Expected<IntelPTSingleBufferTraceUP>
262 IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
263                                 lldb::tid_t tid) {
264 #ifndef PERF_ATTR_SIZE_VER5
265   return createStringError(inconvertibleErrorCode(),
266                            "Intel PT Linux perf event not supported");
267 #else
268   Log *log = GetLog(POSIXLog::Trace);
269 
270   LLDB_LOG(log, "Will start tracing thread id {0}", tid);
271 
272   if (__builtin_popcount(request.trace_buffer_size) != 1 ||
273       request.trace_buffer_size < 4096) {
274     return createStringError(
275         inconvertibleErrorCode(),
276         "The trace buffer size must be a power of 2 greater than or equal to "
277         "4096 (2^12) bytes. It was %" PRIu64 ".",
278         request.trace_buffer_size);
279   }
280   uint64_t page_size = getpagesize();
281   uint64_t buffer_numpages = static_cast<uint64_t>(llvm::PowerOf2Floor(
282       (request.trace_buffer_size + page_size - 1) / page_size));
283 
284   Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration(
285       request.enable_tsc, request.psb_period.map([](int value) {
286         return static_cast<uint64_t>(value);
287       }));
288   if (!attr)
289     return attr.takeError();
290 
291   LLDB_LOG(log, "Will create trace buffer of size {0}",
292            request.trace_buffer_size);
293 
294   if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
295     if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
296                                                             buffer_numpages)) {
297       return std::move(mmap_err);
298     }
299     return IntelPTSingleBufferTraceUP(
300         new IntelPTSingleBufferTrace(std::move(*perf_event), tid));
301   } else {
302     return perf_event.takeError();
303   }
304 #endif
305 }
306