Skip to content

Commit e6c84f8

Browse files
committed
Add thin wrapper for perf_event_open API
- Add PerfEvent class to handle creating ring buffers and handle the resources associated with a perf_event - Refactor IntelPT collection code to use this new API - Add TSC to timestamp conversion logic with unittest Differential Revision: https://reviews.llvm.org/D121734
1 parent 9b1fa6f commit e6c84f8

File tree

8 files changed

+650
-184
lines changed

8 files changed

+650
-184
lines changed

lldb/source/Plugins/Process/Linux/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_lldb_library(lldbPluginProcessLinux
88
NativeRegisterContextLinux_s390x.cpp
99
NativeRegisterContextLinux_x86_64.cpp
1010
NativeThreadLinux.cpp
11+
Perf.cpp
1112
SingleStepCheck.cpp
1213

1314
LINK_LIBS

lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp

Lines changed: 80 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include <algorithm>
10-
#include <fstream>
11-
#include <sstream>
9+
#include "IntelPTCollector.h"
1210

13-
#include "llvm/ADT/StringRef.h"
14-
#include "llvm/Support/Error.h"
15-
#include "llvm/Support/MathExtras.h"
11+
#include "Perf.h"
1612

17-
#include "IntelPTCollector.h"
1813
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
1914
#include "lldb/Host/linux/Support.h"
2015
#include "lldb/Utility/StreamString.h"
2116

17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Support/Error.h"
19+
#include "llvm/Support/MathExtras.h"
20+
21+
#include <algorithm>
22+
#include <cstddef>
23+
#include <fstream>
24+
#include <linux/perf_event.h>
25+
#include <sstream>
2226
#include <sys/ioctl.h>
2327
#include <sys/syscall.h>
2428

@@ -53,6 +57,21 @@ enum IntelPTConfigFileType {
5357
BitOffset
5458
};
5559

60+
/// Get the content of /proc/cpuinfo that can be later used to decode traces.
61+
static Expected<ArrayRef<uint8_t>> GetCPUInfo() {
62+
static llvm::Optional<std::vector<uint8_t>> cpu_info;
63+
if (!cpu_info) {
64+
auto buffer_or_error = errorOrToExpected(getProcFile("cpuinfo"));
65+
if (!buffer_or_error)
66+
return buffer_or_error.takeError();
67+
MemoryBuffer &buffer = **buffer_or_error;
68+
cpu_info = std::vector<uint8_t>(
69+
reinterpret_cast<const uint8_t *>(buffer.getBufferStart()),
70+
reinterpret_cast<const uint8_t *>(buffer.getBufferEnd()));
71+
}
72+
return *cpu_info;
73+
}
74+
5675
static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
5776
IntelPTConfigFileType type) {
5877
ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
@@ -106,6 +125,7 @@ static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
106125
}
107126
return value;
108127
}
128+
109129
/// Return the Linux perf event type for Intel PT.
110130
static Expected<uint32_t> GetOSEventType() {
111131
return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
@@ -148,7 +168,7 @@ size_t IntelPTThreadTrace::GetTraceBufferSize() const {
148168
#ifndef PERF_ATTR_SIZE_VER5
149169
llvm_unreachable("Intel PT Linux perf event not supported");
150170
#else
151-
return m_mmap_meta->aux_size;
171+
return m_perf_event.GetAuxBuffer().size();
152172
#endif
153173
}
154174

@@ -176,30 +196,9 @@ GeneratePerfEventConfigValue(bool enable_tsc, Optional<size_t> psb_period) {
176196
return config;
177197
}
178198

179-
Error IntelPTThreadTrace::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
180-
uint64_t buffer_size, bool enable_tsc,
181-
Optional<size_t> psb_period) {
182-
#ifndef PERF_ATTR_SIZE_VER5
183-
llvm_unreachable("Intel PT Linux perf event not supported");
184-
#else
185-
Log *log = GetLog(POSIXLog::Ptrace);
186-
187-
m_tid = tid;
188-
LLDB_LOG(log, "called thread id {0}", tid);
189-
uint64_t page_size = getpagesize();
190-
191-
if (__builtin_popcount(buffer_size) != 1 || buffer_size < 4096) {
192-
return createStringError(
193-
inconvertibleErrorCode(),
194-
"The trace buffer size must be a power of 2 greater than or equal to "
195-
"4096 (2^12) bytes. It was %" PRIu64 ".",
196-
buffer_size);
197-
}
198-
uint64_t numpages = static_cast<uint64_t>(
199-
llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
200-
numpages = std::max<uint64_t>(1, numpages);
201-
buffer_size = page_size * numpages;
202-
199+
llvm::Expected<perf_event_attr>
200+
IntelPTThreadTrace::CreateIntelPTPerfEventConfiguration(
201+
bool enable_tsc, Optional<size_t> psb_period) {
203202
perf_event_attr attr;
204203
memset(&attr, 0, sizeof(attr));
205204
attr.size = sizeof(attr);
@@ -213,106 +212,59 @@ Error IntelPTThreadTrace::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
213212
if (Expected<uint64_t> config_value =
214213
GeneratePerfEventConfigValue(enable_tsc, psb_period)) {
215214
attr.config = *config_value;
216-
LLDB_LOG(log, "intel pt config {0}", attr.config);
217215
} else {
218216
return config_value.takeError();
219217
}
220218

221219
if (Expected<uint32_t> intel_pt_type = GetOSEventType()) {
222220
attr.type = *intel_pt_type;
223-
LLDB_LOG(log, "intel pt type {0}", attr.type);
224221
} else {
225222
return intel_pt_type.takeError();
226223
}
227224

228-
LLDB_LOG(log, "buffer size {0} ", buffer_size);
229-
230-
errno = 0;
231-
auto fd =
232-
syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
233-
if (fd == -1) {
234-
LLDB_LOG(log, "syscall error {0}", errno);
235-
return createStringError(inconvertibleErrorCode(),
236-
"perf event syscall failed");
237-
}
238-
239-
m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
240-
241-
errno = 0;
242-
auto base =
243-
mmap(nullptr, (buffer_size + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
244-
245-
if (base == MAP_FAILED) {
246-
LLDB_LOG(log, "mmap base error {0}", errno);
247-
return createStringError(inconvertibleErrorCode(),
248-
"Meta buffer allocation failed");
249-
}
250-
251-
m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
252-
reinterpret_cast<perf_event_mmap_page *>(base),
253-
munmap_delete(buffer_size + page_size));
254-
255-
m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
256-
m_mmap_meta->aux_size = buffer_size;
257-
258-
errno = 0;
259-
auto mmap_aux = mmap(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd,
260-
static_cast<long int>(m_mmap_meta->aux_offset));
261-
262-
if (mmap_aux == MAP_FAILED) {
263-
LLDB_LOG(log, "second mmap done {0}", errno);
264-
return createStringError(inconvertibleErrorCode(),
265-
"Trace buffer allocation failed");
266-
}
267-
m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
268-
reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(buffer_size));
269-
return Error::success();
270-
#endif
225+
return attr;
271226
}
272227

273-
llvm::MutableArrayRef<uint8_t> IntelPTThreadTrace::GetDataBuffer() const {
228+
llvm::Expected<IntelPTThreadTraceUP>
229+
IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size,
230+
bool enable_tsc, Optional<size_t> psb_period) {
274231
#ifndef PERF_ATTR_SIZE_VER5
275232
llvm_unreachable("Intel PT Linux perf event not supported");
276233
#else
277-
return MutableArrayRef<uint8_t>(
278-
(reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
279-
m_mmap_meta->data_offset),
280-
m_mmap_meta->data_size);
281-
#endif
282-
}
234+
Log *log = GetLog(POSIXLog::Ptrace);
283235

284-
llvm::MutableArrayRef<uint8_t> IntelPTThreadTrace::GetAuxBuffer() const {
285-
#ifndef PERF_ATTR_SIZE_VER5
286-
llvm_unreachable("Intel PT Linux perf event not supported");
287-
#else
288-
return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
289-
#endif
290-
}
236+
LLDB_LOG(log, "called thread id {0}", tid);
291237

292-
Expected<ArrayRef<uint8_t>> IntelPTThreadTrace::GetCPUInfo() {
293-
static llvm::Optional<std::vector<uint8_t>> cpu_info;
294-
if (!cpu_info) {
295-
auto buffer_or_error = getProcFile("cpuinfo");
296-
if (!buffer_or_error)
297-
return Status(buffer_or_error.getError()).ToError();
298-
MemoryBuffer &buffer = **buffer_or_error;
299-
cpu_info = std::vector<uint8_t>(
300-
reinterpret_cast<const uint8_t *>(buffer.getBufferStart()),
301-
reinterpret_cast<const uint8_t *>(buffer.getBufferEnd()));
238+
if (__builtin_popcount(buffer_size) != 1 || buffer_size < 4096) {
239+
return createStringError(
240+
inconvertibleErrorCode(),
241+
"The trace buffer size must be a power of 2 greater than or equal to "
242+
"4096 (2^12) bytes. It was %" PRIu64 ".",
243+
buffer_size);
302244
}
303-
return *cpu_info;
304-
}
245+
uint64_t page_size = getpagesize();
246+
uint64_t buffer_numpages = static_cast<uint64_t>(
247+
llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
305248

306-
llvm::Expected<IntelPTThreadTraceUP>
307-
IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size,
308-
bool enable_tsc, Optional<size_t> psb_period) {
309-
IntelPTThreadTraceUP thread_trace_up(new IntelPTThreadTrace());
249+
Expected<perf_event_attr> attr =
250+
IntelPTThreadTrace::CreateIntelPTPerfEventConfiguration(enable_tsc,
251+
psb_period);
252+
if (!attr)
253+
return attr.takeError();
310254

311-
if (llvm::Error err = thread_trace_up->StartTrace(pid, tid, buffer_size,
312-
enable_tsc, psb_period))
313-
return std::move(err);
255+
LLDB_LOG(log, "buffer size {0} ", buffer_size);
314256

315-
return std::move(thread_trace_up);
257+
if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
258+
if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
259+
buffer_numpages)) {
260+
return std::move(mmap_err);
261+
}
262+
return IntelPTThreadTraceUP(
263+
new IntelPTThreadTrace(std::move(*perf_event), tid));
264+
} else {
265+
return perf_event.takeError();
266+
}
267+
#endif
316268
}
317269

318270
Expected<std::vector<uint8_t>>
@@ -331,6 +283,8 @@ IntelPTThreadTrace::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
331283
#ifndef PERF_ATTR_SIZE_VER5
332284
llvm_unreachable("perf event not supported");
333285
#else
286+
auto fd = m_perf_event.GetFd();
287+
perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
334288
// Disable the perf event to force a flush out of the CPU's internal buffer.
335289
// Besides, we can guarantee that the CPU won't override any data as we are
336290
// reading the buffer.
@@ -346,13 +300,13 @@ IntelPTThreadTrace::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
346300
//
347301
// This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
348302
// in the man page of perf_event_open.
349-
ioctl(*m_fd, PERF_EVENT_IOC_DISABLE);
303+
ioctl(fd, PERF_EVENT_IOC_DISABLE);
350304

351305
Log *log = GetLog(POSIXLog::Ptrace);
352306
Status error;
353-
uint64_t head = m_mmap_meta->aux_head;
307+
uint64_t head = mmap_metadata.aux_head;
354308

355-
LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
309+
LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.aux_size, head);
356310

357311
/**
358312
* When configured as ring buffer, the aux buffer keeps wrapping around
@@ -366,11 +320,12 @@ IntelPTThreadTrace::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
366320
*
367321
* */
368322

369-
ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
370-
LLDB_LOG(log, "ReadCyclic BUffer Done");
323+
ReadCyclicBuffer(buffer, m_perf_event.GetAuxBuffer(),
324+
static_cast<size_t>(head), offset);
325+
LLDB_LOG(log, "ReadCyclic Buffer Done");
371326

372327
// Reenable tracing now we have read the buffer
373-
ioctl(*m_fd, PERF_EVENT_IOC_ENABLE);
328+
ioctl(fd, PERF_EVENT_IOC_ENABLE);
374329
return error;
375330
#endif
376331
}
@@ -385,7 +340,8 @@ IntelPTThreadTrace::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
385340
uint64_t bytes_remaining = buffer.size();
386341
Status error;
387342

388-
uint64_t head = m_mmap_meta->data_head;
343+
perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
344+
uint64_t head = mmap_metadata.data_head;
389345

390346
/*
391347
* The data buffer and aux buffer have different implementations
@@ -397,11 +353,11 @@ IntelPTThreadTrace::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
397353

398354
LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
399355

400-
auto data_buffer = GetDataBuffer();
356+
auto data_buffer = m_perf_event.GetDataBuffer();
401357

402358
if (head > data_buffer.size()) {
403359
head = head % data_buffer.size();
404-
LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
360+
LLDB_LOG(log, "Data size -{0} Head - {1}", mmap_metadata.data_size, head);
405361

406362
ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
407363
bytes_remaining -= buffer.size();
@@ -424,7 +380,7 @@ IntelPTThreadTrace::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
424380
}
425381

426382
void IntelPTThreadTrace::ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
427-
llvm::MutableArrayRef<uint8_t> src,
383+
llvm::ArrayRef<uint8_t> src,
428384
size_t src_cyc_index, size_t offset) {
429385

430386
Log *log = GetLog(POSIXLog::Ptrace);
@@ -450,7 +406,7 @@ void IntelPTThreadTrace::ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
450406
return;
451407
}
452408

453-
llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
409+
llvm::SmallVector<ArrayRef<uint8_t>, 2> parts = {
454410
src.slice(src_cyc_index), src.take_front(src_cyc_index)};
455411

456412
if (offset > parts[0].size()) {
@@ -624,7 +580,7 @@ Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
624580
}
625581

626582
Expected<json::Value> IntelPTCollector::GetState() const {
627-
Expected<ArrayRef<uint8_t>> cpu_info = IntelPTThreadTrace::GetCPUInfo();
583+
Expected<ArrayRef<uint8_t>> cpu_info = GetCPUInfo();
628584
if (!cpu_info)
629585
return cpu_info.takeError();
630586

@@ -661,7 +617,7 @@ IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const
661617
else
662618
return trace.takeError();
663619
} else if (request.kind == "cpuInfo") {
664-
return IntelPTThreadTrace::GetCPUInfo();
620+
return GetCPUInfo();
665621
}
666622
return createStringError(inconvertibleErrorCode(),
667623
"Unsuported trace binary data kind: %s",

0 commit comments

Comments
 (0)