brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.1 KiB · 8c69989 Raw
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