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