1 //===-- IntelPTCollector.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 "IntelPTCollector.h" 10 11 #include "Perf.h" 12 #include "Procfs.h" 13 14 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 15 #include "lldb/Host/linux/Support.h" 16 #include "lldb/Utility/StreamString.h" 17 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/MathExtras.h" 21 22 #include <algorithm> 23 #include <cstddef> 24 #include <fstream> 25 #include <linux/perf_event.h> 26 #include <sstream> 27 #include <sys/ioctl.h> 28 #include <sys/syscall.h> 29 30 using namespace lldb; 31 using namespace lldb_private; 32 using namespace process_linux; 33 using namespace llvm; 34 35 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process) 36 : m_process(process) { 37 if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = 38 LoadPerfTscConversionParameters()) 39 m_tsc_conversion = 40 std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion); 41 else 42 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(), 43 "unable to load TSC to wall time conversion: {0}"); 44 } 45 46 Error IntelPTCollector::TraceStop(lldb::tid_t tid) { 47 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 48 return m_process_trace_up->TraceStop(tid); 49 return m_thread_traces.TraceStop(tid); 50 } 51 52 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { 53 if (request.IsProcessTracing()) { 54 Clear(); 55 return Error::success(); 56 } else { 57 Error error = Error::success(); 58 for (int64_t tid : *request.tids) 59 error = joinErrors(std::move(error), 60 TraceStop(static_cast<lldb::tid_t>(tid))); 61 return error; 62 } 63 } 64 65 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { 66 if (request.IsProcessTracing()) { 67 if (m_process_trace_up) { 68 return createStringError( 69 inconvertibleErrorCode(), 70 "Process currently traced. Stop process tracing first"); 71 } 72 if (request.IsPerCoreTracing()) { 73 if (m_thread_traces.GetTracedThreadsCount() > 0) 74 return createStringError( 75 inconvertibleErrorCode(), 76 "Threads currently traced. Stop tracing them first."); 77 if (Expected<IntelPTProcessTraceUP> trace = 78 IntelPTMultiCoreTrace::StartOnAllCores(request, m_process)) { 79 m_process_trace_up = std::move(*trace); 80 return Error::success(); 81 } else { 82 return trace.takeError(); 83 } 84 } else { 85 std::vector<lldb::tid_t> process_threads; 86 for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) 87 process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID()); 88 89 // per-thread process tracing 90 if (Expected<IntelPTProcessTraceUP> trace = 91 IntelPTPerThreadProcessTrace::Start(request, process_threads)) { 92 m_process_trace_up = std::move(trace.get()); 93 return Error::success(); 94 } else { 95 return trace.takeError(); 96 } 97 } 98 } else { 99 // individual thread tracing 100 Error error = Error::success(); 101 for (int64_t tid : *request.tids) { 102 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 103 error = joinErrors( 104 std::move(error), 105 createStringError(inconvertibleErrorCode(), 106 formatv("Thread with tid {0} is currently " 107 "traced. Stop tracing it first.", 108 tid) 109 .str() 110 .c_str())); 111 else 112 error = joinErrors(std::move(error), 113 m_thread_traces.TraceStart(tid, request)); 114 } 115 return error; 116 } 117 } 118 119 void IntelPTCollector::ProcessWillResume() { 120 if (m_process_trace_up) 121 m_process_trace_up->ProcessWillResume(); 122 } 123 124 void IntelPTCollector::ProcessDidStop() { 125 if (m_process_trace_up) 126 m_process_trace_up->ProcessDidStop(); 127 } 128 129 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 130 if (m_process_trace_up) 131 return m_process_trace_up->TraceStart(tid); 132 133 return Error::success(); 134 } 135 136 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 137 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 138 return m_process_trace_up->TraceStop(tid); 139 else if (m_thread_traces.TracesThread(tid)) 140 return m_thread_traces.TraceStop(tid); 141 return Error::success(); 142 } 143 144 Expected<json::Value> IntelPTCollector::GetState() { 145 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 146 if (!cpu_info) 147 return cpu_info.takeError(); 148 149 TraceGetStateResponse state; 150 if (m_process_trace_up) 151 state = m_process_trace_up->GetState(); 152 153 state.process_binary_data.push_back( 154 {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); 155 156 m_thread_traces.ForEachThread( 157 [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { 158 state.traced_threads.push_back({tid, 159 {{IntelPTDataKinds::kTraceBuffer, 160 thread_trace.GetTraceBufferSize()}}}); 161 }); 162 return toJSON(state); 163 } 164 165 Expected<std::vector<uint8_t>> 166 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { 167 if (request.kind == IntelPTDataKinds::kTraceBuffer) { 168 if (!request.tid) 169 return createStringError( 170 inconvertibleErrorCode(), 171 "Getting a trace buffer without a tid is currently unsupported"); 172 173 if (m_process_trace_up && m_process_trace_up->TracesThread(*request.tid)) 174 return m_process_trace_up->GetBinaryData(request); 175 176 if (Expected<IntelPTSingleBufferTrace &> trace = 177 m_thread_traces.GetTracedThread(*request.tid)) 178 return trace->GetTraceBuffer(request.offset, request.size); 179 else 180 return trace.takeError(); 181 } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) { 182 return GetProcfsCpuInfo(); 183 } 184 return createStringError(inconvertibleErrorCode(), 185 "Unsuported trace binary data kind: %s", 186 request.kind.c_str()); 187 } 188 189 bool IntelPTCollector::IsSupported() { 190 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 191 return true; 192 } else { 193 llvm::consumeError(intel_pt_type.takeError()); 194 return false; 195 } 196 } 197 198 void IntelPTCollector::Clear() { 199 m_process_trace_up.reset(); 200 m_thread_traces.Clear(); 201 } 202