290 lines · cpp
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-exception6//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 20using namespace lldb;21using namespace lldb_private;22using namespace process_linux;23using namespace llvm;24 25const char kOSEventIntelPTTypeFile[] =26 "/sys/bus/event_source/devices/intel_pt/type";27 28const char kPSBPeriodCapFile[] =29 "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";30 31const char kPSBPeriodValidValuesFile[] =32 "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";33 34const char kPSBPeriodBitOffsetFile[] =35 "/sys/bus/event_source/devices/intel_pt/format/psb_period";36 37const char kTSCBitOffsetFile[] =38 "/sys/bus/event_source/devices/intel_pt/format/tsc";39 40enum IntelPTConfigFileType {41 Hex = 0,42 // 0 or 143 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 to47 // store a given configuration.48 BitOffset49};50 51static 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.107Expected<uint32_t> process_linux::GetIntelPTOSEventType() {108 return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,109 IntelPTConfigFileType::Decimal);110}111 112static 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 value131 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 bit138 mask &= mask - 1;139 }140 error << ".";141 return createStringError(inconvertibleErrorCode(), error.str().c_str());142}143 144#ifdef PERF_ATTR_SIZE_VER5145static Expected<uint64_t>146GeneratePerfEventConfigValue(bool enable_tsc,147 std::optional<uint64_t> psb_period) {148 uint64_t config = 0;149 // tsc is always supported150 if (enable_tsc) {151 if (Expected<uint32_t> offset = ReadIntelPTConfigFile(152 kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))153 config |= 1 << *offset;154 else155 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 else165 return offset.takeError();166 }167 return config;168}169 170/// Create a \a perf_event_attr configured for171/// an IntelPT event.172///173/// \return174/// A \a perf_event_attr if successful,175/// or an \a llvm::Error otherwise.176static Expected<perf_event_attr>177CreateIntelPTPerfEventConfiguration(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 else190 return config_value.takeError();191 192 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType())193 attr.type = *intel_pt_type;194 else195 return intel_pt_type.takeError();196 197 return attr;198}199#endif200 201size_t IntelPTSingleBufferTrace::GetIptTraceSize() const {202 return m_perf_event.GetAuxBuffer().size();203}204 205Error IntelPTSingleBufferTrace::Pause() {206 return m_perf_event.DisableWithIoctl();207}208 209Error IntelPTSingleBufferTrace::Resume() {210 return m_perf_event.EnableWithIoctl();211}212 213Expected<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 are216 // reading the buffer.217 // The Intel documentation says:218 //219 // Packets are first buffered internally and then written out220 // asynchronously. To collect packet output for postprocessing, a collector221 // needs first to ensure that all packet data has been flushed from internal222 // buffers. Software can ensure this by stopping packet generation by223 // clearing IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in224 // Section 35.2.7.2).225 //226 // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as227 // mentioned in the man page of perf_event_open.228 return m_perf_event.GetReadOnlyAuxBuffer();229}230 231Expected<IntelPTSingleBufferTrace>232IntelPTSingleBufferTrace::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_VER5237 return createStringError(inconvertibleErrorCode(),238 "Intel PT Linux perf event not supported");239#else240 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#endif285}286 287const PerfEvent &IntelPTSingleBufferTrace::GetPerfEvent() const {288 return m_perf_event;289}290