166 lines · cpp
1//===-- IntelPTMultiCoreTrace.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 "IntelPTMultiCoreTrace.h"10#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"11#include "Procfs.h"12#include <optional>13 14using namespace lldb;15using namespace lldb_private;16using namespace process_linux;17using namespace llvm;18 19static bool IsTotalBufferLimitReached(ArrayRef<cpu_id_t> cores,20 const TraceIntelPTStartRequest &request) {21 uint64_t required = cores.size() * request.ipt_trace_size;22 uint64_t limit = request.process_buffer_size_limit.value_or(23 std::numeric_limits<uint64_t>::max());24 return required > limit;25}26 27static Error IncludePerfEventParanoidMessageInError(Error &&error) {28 return createStringError(29 inconvertibleErrorCode(),30 "%s\nYou might need to rerun as sudo or to set "31 "/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1. You can use "32 "`sudo sysctl -w kernel.perf_event_paranoid=-1` for that.",33 toString(std::move(error)).c_str());34}35 36Expected<std::unique_ptr<IntelPTMultiCoreTrace>>37IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request,38 NativeProcessProtocol &process,39 std::optional<int> cgroup_fd) {40 Expected<ArrayRef<cpu_id_t>> cpu_ids = GetAvailableLogicalCoreIDs();41 if (!cpu_ids)42 return cpu_ids.takeError();43 44 if (IsTotalBufferLimitReached(*cpu_ids, request))45 return createStringError(46 inconvertibleErrorCode(),47 "The process can't be traced because the process trace size limit "48 "has been reached. Consider retracing with a higher limit.");49 50 DenseMap<cpu_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>>51 traces;52 53 for (cpu_id_t cpu_id : *cpu_ids) {54 Expected<IntelPTSingleBufferTrace> core_trace =55 IntelPTSingleBufferTrace::Start(request, /*tid=*/std::nullopt, cpu_id,56 /*disabled=*/true, cgroup_fd);57 if (!core_trace)58 return IncludePerfEventParanoidMessageInError(core_trace.takeError());59 60 if (Expected<PerfEvent> context_switch_trace =61 CreateContextSwitchTracePerfEvent(cpu_id,62 &core_trace->GetPerfEvent())) {63 traces.try_emplace(cpu_id,64 std::make_pair(std::move(*core_trace),65 std::move(*context_switch_trace)));66 } else {67 return context_switch_trace.takeError();68 }69 }70 71 return std::unique_ptr<IntelPTMultiCoreTrace>(72 new IntelPTMultiCoreTrace(std::move(traces), process, (bool)cgroup_fd));73}74 75void IntelPTMultiCoreTrace::ForEachCore(76 std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace)>77 callback) {78 for (auto &it : m_traces_per_core)79 callback(it.first, it.second.first);80}81 82void IntelPTMultiCoreTrace::ForEachCore(83 std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &intelpt_trace,84 ContextSwitchTrace &context_switch_trace)>85 callback) {86 for (auto &it : m_traces_per_core)87 callback(it.first, it.second.first, it.second.second);88}89 90void IntelPTMultiCoreTrace::ProcessDidStop() {91 ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {92 if (Error err = core_trace.Pause()) {93 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),94 "Unable to pause the core trace for core {0}", cpu_id);95 }96 });97}98 99void IntelPTMultiCoreTrace::ProcessWillResume() {100 ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {101 if (Error err = core_trace.Resume()) {102 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),103 "Unable to resume the core trace for core {0}", cpu_id);104 }105 });106}107 108TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() {109 TraceIntelPTGetStateResponse state;110 state.using_cgroup_filtering = m_using_cgroup_filtering;111 112 for (NativeThreadProtocol &thread : m_process.Threads())113 state.traced_threads.push_back(TraceThreadState{thread.GetID(), {}});114 115 state.cpus.emplace();116 ForEachCore([&](lldb::cpu_id_t cpu_id,117 const IntelPTSingleBufferTrace &core_trace,118 const ContextSwitchTrace &context_switch_trace) {119 state.cpus->push_back(120 {cpu_id,121 {{IntelPTDataKinds::kIptTrace, core_trace.GetIptTraceSize()},122 {IntelPTDataKinds::kPerfContextSwitchTrace,123 context_switch_trace.GetEffectiveDataBufferSize()}}});124 });125 126 return state;127}128 129bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const {130 // All the process' threads are being traced automatically.131 return (bool)m_process.GetThreadByID(tid);132}133 134llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) {135 // All the process' threads are being traced automatically.136 if (!TracesThread(tid))137 return createStringError(138 inconvertibleErrorCode(),139 "Thread %" PRIu64 " is not part of the target process", tid);140 return Error::success();141}142 143Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) {144 return createStringError(inconvertibleErrorCode(),145 "Can't stop tracing an individual thread when "146 "per-cpu process tracing is enabled.");147}148 149Expected<std::optional<std::vector<uint8_t>>>150IntelPTMultiCoreTrace::TryGetBinaryData(151 const TraceGetBinaryDataRequest &request) {152 if (!request.cpu_id)153 return std::nullopt;154 auto it = m_traces_per_core.find(*request.cpu_id);155 if (it == m_traces_per_core.end())156 return createStringError(157 inconvertibleErrorCode(),158 formatv("Core {0} is not being traced", *request.cpu_id));159 160 if (request.kind == IntelPTDataKinds::kIptTrace)161 return it->second.first.GetIptTrace();162 if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace)163 return it->second.second.GetReadOnlyDataBuffer();164 return std::nullopt;165}166