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