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::OnProcessStateChanged(lldb::StateType state) { 120 if (m_process_trace_up) 121 m_process_trace_up->OnProcessStateChanged(state); 122 } 123 124 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { 125 if (m_process_trace_up) 126 return m_process_trace_up->TraceStart(tid); 127 128 return Error::success(); 129 } 130 131 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { 132 if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) 133 return m_process_trace_up->TraceStop(tid); 134 else if (m_thread_traces.TracesThread(tid)) 135 return m_thread_traces.TraceStop(tid); 136 return Error::success(); 137 } 138 139 Expected<json::Value> IntelPTCollector::GetState() { 140 Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo(); 141 if (!cpu_info) 142 return cpu_info.takeError(); 143 144 TraceGetStateResponse state; 145 if (m_process_trace_up) 146 state = m_process_trace_up->GetState(); 147 148 state.process_binary_data.push_back( 149 {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); 150 151 m_thread_traces.ForEachThread( 152 [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { 153 state.traced_threads.push_back({tid, 154 {{IntelPTDataKinds::kTraceBuffer, 155 thread_trace.GetTraceBufferSize()}}}); 156 }); 157 return toJSON(state); 158 } 159 160 Expected<std::vector<uint8_t>> 161 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { 162 if (request.kind == IntelPTDataKinds::kTraceBuffer) { 163 if (!request.tid) 164 return createStringError( 165 inconvertibleErrorCode(), 166 "Getting a trace buffer without a tid is currently unsupported"); 167 168 if (m_process_trace_up && m_process_trace_up->TracesThread(*request.tid)) 169 return m_process_trace_up->GetBinaryData(request); 170 171 if (Expected<IntelPTSingleBufferTrace &> trace = 172 m_thread_traces.GetTracedThread(*request.tid)) 173 return trace->GetTraceBuffer(request.offset, request.size); 174 else 175 return trace.takeError(); 176 } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) { 177 return GetProcfsCpuInfo(); 178 } 179 return createStringError(inconvertibleErrorCode(), 180 "Unsuported trace binary data kind: %s", 181 request.kind.c_str()); 182 } 183 184 bool IntelPTCollector::IsSupported() { 185 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { 186 return true; 187 } else { 188 llvm::consumeError(intel_pt_type.takeError()); 189 return false; 190 } 191 } 192 193 void IntelPTCollector::Clear() { 194 m_process_trace_up.reset(); 195 m_thread_traces.Clear(); 196 } 197