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 static Expected<uint64_t>
150 GeneratePerfEventConfigValue(bool enable_tsc, Optional<size_t> psb_period) {
151   uint64_t config = 0;
152   // tsc is always supported
153   if (enable_tsc) {
154     if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
155             kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))
156       config |= 1 << *offset;
157     else
158       return offset.takeError();
159   }
160   if (psb_period) {
161     if (Error error = CheckPsbPeriod(*psb_period))
162       return std::move(error);
163 
164     if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
165             kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset))
166       config |= *psb_period << *offset;
167     else
168       return offset.takeError();
169   }
170   return config;
171 }
172 
173 /// Create a \a perf_event_attr configured for
174 /// an IntelPT event.
175 ///
176 /// \return
177 ///   A \a perf_event_attr if successful,
178 ///   or an \a llvm::Error otherwise.
179 static Expected<perf_event_attr>
180 CreateIntelPTPerfEventConfiguration(bool enable_tsc,
181                                     llvm::Optional<size_t> psb_period) {
182 #ifndef PERF_ATTR_SIZE_VER5
183   return llvm_unreachable("Intel PT Linux perf event not supported");
184 #else
185   perf_event_attr attr;
186   memset(&attr, 0, sizeof(attr));
187   attr.size = sizeof(attr);
188   attr.exclude_kernel = 1;
189   attr.sample_type = PERF_SAMPLE_TIME;
190   attr.sample_id_all = 1;
191   attr.exclude_hv = 1;
192   attr.exclude_idle = 1;
193   attr.mmap = 1;
194 
195   if (Expected<uint64_t> config_value =
196           GeneratePerfEventConfigValue(enable_tsc, psb_period))
197     attr.config = *config_value;
198   else
199     return config_value.takeError();
200 
201   if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType())
202     attr.type = *intel_pt_type;
203   else
204     return intel_pt_type.takeError();
205 
206   return attr;
207 #endif
208 }
209 
210 size_t IntelPTSingleBufferTrace::GetTraceBufferSize() const {
211   return m_perf_event.GetAuxBuffer().size();
212 }
213 
214 Expected<std::vector<uint8_t>>
215 IntelPTSingleBufferTrace::GetTraceBuffer(size_t offset, size_t size) const {
216   auto fd = m_perf_event.GetFd();
217   perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
218   // Disable the perf event to force a flush out of the CPU's internal buffer.
219   // Besides, we can guarantee that the CPU won't override any data as we are
220   // reading the buffer.
221   //
222   // The Intel documentation says:
223   //
224   // Packets are first buffered internally and then written out asynchronously.
225   // To collect packet output for postprocessing, a collector needs first to
226   // ensure that all packet data has been flushed from internal buffers.
227   // Software can ensure this by stopping packet generation by clearing
228   // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
229   // Section 35.2.7.2).
230   //
231   // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
232   // in the man page of perf_event_open.
233   ioctl(fd, PERF_EVENT_IOC_DISABLE);
234 
235   Log *log = GetLog(POSIXLog::Trace);
236   Status error;
237   uint64_t head = mmap_metadata.aux_head;
238 
239   LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.aux_size, head);
240 
241   /**
242    * When configured as ring buffer, the aux buffer keeps wrapping around
243    * the buffer and its not possible to detect how many times the buffer
244    * wrapped. Initially the buffer is filled with zeros,as shown below
245    * so in order to get complete buffer we first copy firstpartsize, followed
246    * by any left over part from beginning to aux_head
247    *
248    * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
249    *                 aux_head->||<- firstpartsize  ->|
250    *
251    * */
252 
253   std::vector<uint8_t> data(size, 0);
254   MutableArrayRef<uint8_t> buffer(data);
255   ReadCyclicBuffer(buffer, m_perf_event.GetAuxBuffer(),
256                    static_cast<size_t>(head), offset);
257 
258   // Reenable tracing now we have read the buffer
259   ioctl(fd, PERF_EVENT_IOC_ENABLE);
260   return data;
261 }
262 
263 Expected<IntelPTSingleBufferTraceUP>
264 IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
265                                 lldb::tid_t tid) {
266   Log *log = GetLog(POSIXLog::Trace);
267 
268   LLDB_LOG(log, "Will start tracing thread id {0}", tid);
269 
270   if (__builtin_popcount(request.trace_buffer_size) != 1 ||
271       request.trace_buffer_size < 4096) {
272     return createStringError(
273         inconvertibleErrorCode(),
274         "The trace buffer size must be a power of 2 greater than or equal to "
275         "4096 (2^12) bytes. It was %" PRIu64 ".",
276         request.trace_buffer_size);
277   }
278   uint64_t page_size = getpagesize();
279   uint64_t buffer_numpages = static_cast<uint64_t>(llvm::PowerOf2Floor(
280       (request.trace_buffer_size + page_size - 1) / page_size));
281 
282   Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration(
283       request.enable_tsc, request.psb_period.map([](int value) {
284         return static_cast<uint64_t>(value);
285       }));
286   if (!attr)
287     return attr.takeError();
288 
289   LLDB_LOG(log, "Will create trace buffer of size {0}",
290            request.trace_buffer_size);
291 
292   if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
293     if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
294                                                             buffer_numpages)) {
295       return std::move(mmap_err);
296     }
297     return IntelPTSingleBufferTraceUP(
298         new IntelPTSingleBufferTrace(std::move(*perf_event), tid));
299   } else {
300     return perf_event.takeError();
301   }
302 }
303