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
ReadIntelPTConfigFile(const char * file,IntelPTConfigFileType type)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.
GetIntelPTOSEventType()111 Expected<uint32_t> process_linux::GetIntelPTOSEventType() {
112 return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
113 IntelPTConfigFileType::Decimal);
114 }
115
CheckPsbPeriod(size_t psb_period)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>
GeneratePerfEventConfigValue(bool enable_tsc,Optional<uint64_t> psb_period)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>
CreateIntelPTPerfEventConfiguration(bool enable_tsc,llvm::Optional<uint64_t> psb_period)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.exclude_hv = 1;
187 attr.exclude_idle = 1;
188
189 if (Expected<uint64_t> config_value =
190 GeneratePerfEventConfigValue(enable_tsc, psb_period))
191 attr.config = *config_value;
192 else
193 return config_value.takeError();
194
195 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType())
196 attr.type = *intel_pt_type;
197 else
198 return intel_pt_type.takeError();
199
200 return attr;
201 }
202 #endif
203
GetIptTraceSize() const204 size_t IntelPTSingleBufferTrace::GetIptTraceSize() const {
205 return m_perf_event.GetAuxBuffer().size();
206 }
207
Pause()208 Error IntelPTSingleBufferTrace::Pause() {
209 return m_perf_event.DisableWithIoctl();
210 }
211
Resume()212 Error IntelPTSingleBufferTrace::Resume() {
213 return m_perf_event.EnableWithIoctl();
214 }
215
GetIptTrace()216 Expected<std::vector<uint8_t>> IntelPTSingleBufferTrace::GetIptTrace() {
217 // Disable the perf event to force a flush out of the CPU's internal buffer.
218 // Besides, we can guarantee that the CPU won't override any data as we are
219 // reading the buffer.
220 // The Intel documentation says:
221 //
222 // Packets are first buffered internally and then written out
223 // asynchronously. To collect packet output for postprocessing, a collector
224 // needs first to ensure that all packet data has been flushed from internal
225 // buffers. Software can ensure this by stopping packet generation by
226 // clearing 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
230 // mentioned in the man page of perf_event_open.
231 return m_perf_event.GetReadOnlyAuxBuffer();
232 }
233
Start(const TraceIntelPTStartRequest & request,Optional<lldb::tid_t> tid,Optional<cpu_id_t> cpu_id,bool disabled,Optional<int> cgroup_fd)234 Expected<IntelPTSingleBufferTrace> IntelPTSingleBufferTrace::Start(
235 const TraceIntelPTStartRequest &request, Optional<lldb::tid_t> tid,
236 Optional<cpu_id_t> cpu_id, bool disabled, Optional<int> cgroup_fd) {
237 #ifndef PERF_ATTR_SIZE_VER5
238 return createStringError(inconvertibleErrorCode(),
239 "Intel PT Linux perf event not supported");
240 #else
241 Log *log = GetLog(POSIXLog::Trace);
242
243 LLDB_LOG(log, "Will start tracing thread id {0} and cpu id {1}", tid, cpu_id);
244
245 if (__builtin_popcount(request.ipt_trace_size) != 1 ||
246 request.ipt_trace_size < 4096) {
247 return createStringError(
248 inconvertibleErrorCode(),
249 "The intel pt trace size must be a power of 2 greater than or equal to "
250 "4096 (2^12) bytes. It was %" PRIu64 ".",
251 request.ipt_trace_size);
252 }
253 uint64_t page_size = getpagesize();
254 uint64_t aux_buffer_numpages = static_cast<uint64_t>(llvm::PowerOf2Floor(
255 (request.ipt_trace_size + page_size - 1) / page_size));
256
257 Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration(
258 request.enable_tsc, request.psb_period.map([](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