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 static Expected<PerfEvent> CreateContextSwitchTracePerfEvent( 37 bool disabled, lldb::core_id_t core_id, 38 IntelPTSingleBufferTrace &intelpt_core_trace) { 39 Log *log = GetLog(POSIXLog::Trace); 40 #ifndef PERF_ATTR_SIZE_VER5 41 return createStringError(inconvertibleErrorCode(), 42 "Intel PT Linux perf event not supported"); 43 #else 44 perf_event_attr attr; 45 memset(&attr, 0, sizeof(attr)); 46 attr.size = sizeof(attr); 47 attr.sample_period = 0; 48 attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME; 49 attr.type = PERF_TYPE_SOFTWARE; 50 attr.context_switch = 1; 51 attr.exclude_kernel = 1; 52 attr.sample_id_all = 1; 53 attr.exclude_hv = 1; 54 attr.disabled = disabled; 55 56 // The given perf configuration will product context switch records of 32 57 // bytes each. Assuming that every context switch will be emitted twice (one 58 // for context switch ins and another one for context switch outs), and that a 59 // context switch will happen at least every half a millisecond per core, we 60 // need 500 * 32 bytes (~16 KB) for a trace of one second, which is much more 61 // than what a regular intel pt trace can get. Pessimistically we pick as 62 // 32KiB for the size of our context switch trace. 63 64 uint64_t data_buffer_size = 32768; 65 uint64_t data_buffer_numpages = data_buffer_size / getpagesize(); 66 67 LLDB_LOG(log, "Will create context switch trace buffer of size {0}", 68 data_buffer_size); 69 70 if (Expected<PerfEvent> perf_event = PerfEvent::Init( 71 attr, /*pid=*/None, core_id, 72 intelpt_core_trace.GetPerfEvent().GetFd(), /*flags=*/0)) { 73 if (Error mmap_err = perf_event->MmapMetadataAndBuffers( 74 data_buffer_numpages, 0, /*data_buffer_write=*/false)) { 75 return std::move(mmap_err); 76 } 77 return perf_event; 78 } else { 79 return perf_event.takeError(); 80 } 81 #endif 82 } 83 84 Expected<IntelPTProcessTraceUP> 85 IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request, 86 NativeProcessProtocol &process) { 87 Expected<ArrayRef<core_id_t>> core_ids = GetAvailableLogicalCoreIDs(); 88 if (!core_ids) 89 return core_ids.takeError(); 90 91 if (IsTotalBufferLimitReached(*core_ids, request)) 92 return createStringError( 93 inconvertibleErrorCode(), 94 "The process can't be traced because the process trace size limit " 95 "has been reached. Consider retracing with a higher limit."); 96 97 DenseMap<core_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>> 98 traces; 99 100 for (core_id_t core_id : *core_ids) { 101 Expected<IntelPTSingleBufferTrace> core_trace = 102 IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id, 103 /*disabled=*/true); 104 if (!core_trace) 105 return IncludePerfEventParanoidMessageInError(core_trace.takeError()); 106 107 if (Expected<PerfEvent> context_switch_trace = 108 CreateContextSwitchTracePerfEvent(/*disabled=*/true, core_id, 109 core_trace.get())) { 110 traces.try_emplace(core_id, 111 std::make_pair(std::move(*core_trace), 112 std::move(*context_switch_trace))); 113 } else { 114 return context_switch_trace.takeError(); 115 } 116 } 117 118 return IntelPTProcessTraceUP( 119 new IntelPTMultiCoreTrace(std::move(traces), process)); 120 } 121 122 void IntelPTMultiCoreTrace::ForEachCore( 123 std::function<void(core_id_t core_id, IntelPTSingleBufferTrace &core_trace)> 124 callback) { 125 for (auto &it : m_traces_per_core) 126 callback(it.first, it.second.first); 127 } 128 129 void IntelPTMultiCoreTrace::ForEachCore( 130 std::function<void(core_id_t core_id, 131 IntelPTSingleBufferTrace &intelpt_trace, 132 PerfEvent &context_switch_trace)> 133 callback) { 134 for (auto &it : m_traces_per_core) 135 callback(it.first, it.second.first, it.second.second); 136 } 137 138 void IntelPTMultiCoreTrace::ProcessDidStop() { 139 ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) { 140 if (Error err = core_trace.Pause()) { 141 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err), 142 "Unable to pause the core trace for core {0}", core_id); 143 } 144 }); 145 } 146 147 void IntelPTMultiCoreTrace::ProcessWillResume() { 148 ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) { 149 if (Error err = core_trace.Resume()) { 150 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err), 151 "Unable to resume the core trace for core {0}", core_id); 152 } 153 }); 154 } 155 156 TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() { 157 TraceIntelPTGetStateResponse state; 158 159 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 160 state.traced_threads.push_back( 161 TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}}); 162 163 state.cores.emplace(); 164 ForEachCore([&](lldb::core_id_t core_id, 165 const IntelPTSingleBufferTrace &core_trace, 166 const PerfEvent &context_switch_trace) { 167 state.cores->push_back( 168 {core_id, 169 {{IntelPTDataKinds::kTraceBuffer, core_trace.GetTraceBufferSize()}, 170 {IntelPTDataKinds::kPerfContextSwitchTrace, 171 context_switch_trace.GetEffectiveDataBufferSize()}}}); 172 }); 173 174 return state; 175 } 176 177 bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const { 178 // All the process' threads are being traced automatically. 179 return (bool)m_process.GetThreadByID(tid); 180 } 181 182 llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) { 183 // This instance is already tracing all threads automatically. 184 return llvm::Error::success(); 185 } 186 187 Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) { 188 return createStringError(inconvertibleErrorCode(), 189 "Can't stop tracing an individual thread when " 190 "per-core process tracing is enabled."); 191 } 192 193 Expected<Optional<std::vector<uint8_t>>> 194 IntelPTMultiCoreTrace::TryGetBinaryData( 195 const TraceGetBinaryDataRequest &request) { 196 if (!request.core_id) 197 return None; 198 auto it = m_traces_per_core.find(*request.core_id); 199 if (it == m_traces_per_core.end()) 200 return createStringError( 201 inconvertibleErrorCode(), 202 formatv("Core {0} is not being traced", *request.core_id)); 203 204 if (request.kind == IntelPTDataKinds::kTraceBuffer) 205 return it->second.first.GetTraceBuffer(request.offset, request.size); 206 if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace) 207 return it->second.second.ReadFlushedOutDataCyclicBuffer(request.offset, 208 request.size); 209 return None; 210 } 211