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<core_id_t> cores, 21 const TraceIntelPTStartRequest &request) { 22 uint64_t required = cores.size() * request.trace_buffer_size; 23 uint64_t limit = request.process_buffer_size_limit.getValueOr( 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<core_id_t>> core_ids = GetAvailableLogicalCoreIDs(); 40 if (!core_ids) 41 return core_ids.takeError(); 42 43 if (IsTotalBufferLimitReached(*core_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<core_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>> 50 traces; 51 52 for (core_id_t core_id : *core_ids) { 53 Expected<IntelPTSingleBufferTrace> core_trace = 54 IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id, 55 /*disabled=*/true); 56 if (!core_trace) 57 return IncludePerfEventParanoidMessageInError(core_trace.takeError()); 58 59 if (Expected<PerfEvent> context_switch_trace = 60 CreateContextSwitchTracePerfEvent(core_id, 61 &core_trace->GetPerfEvent())) { 62 traces.try_emplace(core_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(core_id_t core_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(core_id_t core_id, 83 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 90 void IntelPTMultiCoreTrace::ProcessDidStop() { 91 ForEachCore([](core_id_t core_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}", core_id); 95 } 96 }); 97 } 98 99 void IntelPTMultiCoreTrace::ProcessWillResume() { 100 ForEachCore([](core_id_t core_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}", core_id); 104 } 105 }); 106 } 107 108 TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() { 109 TraceIntelPTGetStateResponse state; 110 111 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 112 state.traced_threads.push_back( 113 TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}}); 114 115 state.cores.emplace(); 116 ForEachCore([&](lldb::core_id_t core_id, 117 const IntelPTSingleBufferTrace &core_trace, 118 const ContextSwitchTrace &context_switch_trace) { 119 state.cores->push_back( 120 {core_id, 121 {{IntelPTDataKinds::kTraceBuffer, core_trace.GetTraceBufferSize()}, 122 {IntelPTDataKinds::kPerfContextSwitchTrace, 123 context_switch_trace.GetEffectiveDataBufferSize()}}}); 124 }); 125 126 return state; 127 } 128 129 bool 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 134 llvm::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 143 Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) { 144 return createStringError(inconvertibleErrorCode(), 145 "Can't stop tracing an individual thread when " 146 "per-core process tracing is enabled."); 147 } 148 149 Expected<Optional<std::vector<uint8_t>>> 150 IntelPTMultiCoreTrace::TryGetBinaryData( 151 const TraceGetBinaryDataRequest &request) { 152 if (!request.core_id) 153 return None; 154 auto it = m_traces_per_core.find(*request.core_id); 155 if (it == m_traces_per_core.end()) 156 return createStringError( 157 inconvertibleErrorCode(), 158 formatv("Core {0} is not being traced", *request.core_id)); 159 160 if (request.kind == IntelPTDataKinds::kTraceBuffer) 161 return it->second.first.GetTraceBuffer(request.offset, request.size); 162 if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace) 163 return it->second.second.ReadFlushedOutDataCyclicBuffer(request.offset, 164 request.size); 165 return None; 166 } 167