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