LLDB  mainline
IntelPTCollector.cpp
Go to the documentation of this file.
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 #include "Perf.h"
12 #include "Procfs.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/MathExtras.h"
18 #include <algorithm>
19 #include <cstddef>
20 #include <fcntl.h>
21 #include <fstream>
22 #include <linux/perf_event.h>
23 #include <sstream>
24 #include <sys/ioctl.h>
25 #include <sys/syscall.h>
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 using namespace process_linux;
30 using namespace llvm;
31 
32 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process)
33  : m_process(process) {}
34 
35 llvm::Expected<LinuxPerfZeroTscConversion &>
37  if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
39  return *tsc_conversion;
40  else
41  return createStringError(inconvertibleErrorCode(),
42  "Unable to load TSC to wall time conversion: %s",
43  toString(tsc_conversion.takeError()).c_str());
44 }
45 
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 
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 /// \return
66 /// some file descriptor in /sys/fs/ associated with the cgroup of the given
67 /// pid, or \a llvm::None if the pid is not part of a cgroup.
68 static Optional<int> GetCGroupFileDescriptor(lldb::pid_t pid) {
69  static Optional<int> fd;
70  if (fd)
71  return fd;
72 
73  std::ifstream ifile;
74  ifile.open(formatv("/proc/{0}/cgroup", pid));
75  if (!ifile)
76  return None;
77 
78  std::string line;
79  while (std::getline(ifile, line)) {
80  if (line.find("0:") != 0)
81  continue;
82 
83  std::string slice = line.substr(line.find_first_of("/"));
84  if (slice.empty())
85  return None;
86  std::string cgroup_file = formatv("/sys/fs/cgroup/{0}", slice);
87  // This cgroup should for the duration of the target, so we don't need to
88  // invoke close ourselves.
89  int maybe_fd = open(cgroup_file.c_str(), O_RDONLY);
90  if (maybe_fd != -1) {
91  fd = maybe_fd;
92  return fd;
93  }
94  }
95  return None;
96 }
97 
99  if (request.IsProcessTracing()) {
100  if (m_process_trace_up) {
101  return createStringError(
102  inconvertibleErrorCode(),
103  "Process currently traced. Stop process tracing first");
104  }
105  if (request.IsPerCpuTracing()) {
107  return createStringError(
108  inconvertibleErrorCode(),
109  "Threads currently traced. Stop tracing them first.");
110  // CPU tracing is useless if we can't convert tsc to nanos.
111  Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
113  if (!tsc_conversion)
114  return tsc_conversion.takeError();
115 
116  // We force the enablement of TSCs, which is needed for correlating the
117  // cpu traces.
118  TraceIntelPTStartRequest effective_request = request;
119  effective_request.enable_tsc = true;
120 
121  // We try to use cgroup filtering whenever possible
122  Optional<int> cgroup_fd;
123  if (!request.disable_cgroup_filtering.value_or(false))
124  cgroup_fd = GetCGroupFileDescriptor(m_process.GetID());
125 
126  if (Expected<IntelPTProcessTraceUP> trace =
127  IntelPTMultiCoreTrace::StartOnAllCores(effective_request,
128  m_process, cgroup_fd)) {
129  m_process_trace_up = std::move(*trace);
130  return Error::success();
131  } else {
132  return trace.takeError();
133  }
134  } else {
135  std::vector<lldb::tid_t> process_threads;
136  for (NativeThreadProtocol &thread : m_process.Threads())
137  process_threads.push_back(thread.GetID());
138 
139  // per-thread process tracing
140  if (Expected<IntelPTProcessTraceUP> trace =
141  IntelPTPerThreadProcessTrace::Start(request, process_threads)) {
142  m_process_trace_up = std::move(trace.get());
143  return Error::success();
144  } else {
145  return trace.takeError();
146  }
147  }
148  } else {
149  // individual thread tracing
150  Error error = Error::success();
151  for (int64_t tid : *request.tids) {
152  if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
153  error = joinErrors(
154  std::move(error),
155  createStringError(inconvertibleErrorCode(),
156  formatv("Thread with tid {0} is currently "
157  "traced. Stop tracing it first.",
158  tid)
159  .str()
160  .c_str()));
161  else
162  error = joinErrors(std::move(error),
163  m_thread_traces.TraceStart(tid, request));
164  }
165  return error;
166  }
167 }
168 
170  if (m_process_trace_up)
171  m_process_trace_up->ProcessWillResume();
172 }
173 
175  if (m_process_trace_up)
176  m_process_trace_up->ProcessDidStop();
177 }
178 
180  if (m_process_trace_up)
181  return m_process_trace_up->TraceStart(tid);
182 
183  return Error::success();
184 }
185 
187  if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
188  return m_process_trace_up->TraceStop(tid);
189  else if (m_thread_traces.TracesThread(tid))
190  return m_thread_traces.TraceStop(tid);
191  return Error::success();
192 }
193 
194 Expected<json::Value> IntelPTCollector::GetState() {
195  Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo();
196  if (!cpu_info)
197  return cpu_info.takeError();
198 
200  if (m_process_trace_up)
201  state = m_process_trace_up->GetState();
202 
203  state.process_binary_data.push_back(
204  {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()});
205 
207  [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) {
208  state.traced_threads.push_back(
209  {tid,
210  {{IntelPTDataKinds::kIptTrace, thread_trace.GetIptTraceSize()}}});
211  });
212 
213  if (Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
215  state.tsc_perf_zero_conversion = *tsc_conversion;
216  else
217  state.AddWarning(toString(tsc_conversion.takeError()));
218  return toJSON(state);
219 }
220 
221 Expected<std::vector<uint8_t>>
223  if (request.kind == IntelPTDataKinds::kProcFsCpuInfo)
224  return GetProcfsCpuInfo();
225 
226  if (m_process_trace_up) {
227  Expected<Optional<std::vector<uint8_t>>> data =
228  m_process_trace_up->TryGetBinaryData(request);
229  if (!data)
230  return data.takeError();
231  if (*data)
232  return **data;
233  }
234 
235  {
236  Expected<Optional<std::vector<uint8_t>>> data =
238  if (!data)
239  return data.takeError();
240  if (*data)
241  return **data;
242  }
243 
244  return createStringError(
245  inconvertibleErrorCode(),
246  formatv("Can't fetch data kind {0} for cpu_id {1}, tid {2} and "
247  "\"process tracing\" mode {3}",
248  request.kind, request.cpu_id, request.tid,
249  m_process_trace_up ? "enabled" : "not enabled"));
250 }
251 
253  if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
254  return true;
255  } else {
256  llvm::consumeError(intel_pt_type.takeError());
257  return false;
258  }
259 }
260 
262  m_process_trace_up.reset();
264 }
lldb_private::toString
const char * toString(AppleArm64ExceptionClass EC)
Definition: AppleArm64ExceptionClass.h:38
lldb_private::toJSON
llvm::json::Value toJSON(const TraceSupportedResponse &packet)
Definition: TraceGDBRemotePackets.cpp:24
lldb_private::TraceStopRequest::IsProcessTracing
bool IsProcessTracing() const
Definition: TraceGDBRemotePackets.cpp:54
lldb_private::TraceGetStateResponse::traced_threads
std::vector< TraceThreadState > traced_threads
Definition: TraceGDBRemotePackets.h:133
lldb_private::TraceGetBinaryDataRequest::kind
std::string kind
Identifier for the data.
Definition: TraceGDBRemotePackets.h:153
llvm
Definition: Debugger.h:50
lldb_private::process_linux::IntelPTCollector::IsSupported
static bool IsSupported()
Definition: IntelPTCollector.cpp:252
lldb_private::TraceGetBinaryDataRequest
jLLDBTraceGetBinaryData gdb-remote packet
Definition: TraceGDBRemotePackets.h:149
lldb_private::process_linux::LoadPerfTscConversionParameters
llvm::Expected< LinuxPerfZeroTscConversion > LoadPerfTscConversionParameters()
Load PerfTscConversionParameters from perf_event_mmap_page, if available.
Definition: Perf.cpp:27
lldb_private::NativeThreadProtocol
Definition: NativeThreadProtocol.h:24
lldb_private::NativeProcessProtocol::Threads
ThreadIterable Threads() const
Definition: NativeProcessProtocol.h:224
lldb_private::TraceIntelPTStartRequest::IsPerCpuTracing
bool IsPerCpuTracing() const
Definition: TraceIntelPTGDBRemotePackets.cpp:21
lldb_private::TraceStopRequest::tids
llvm::Optional< std::vector< lldb::tid_t > > tids
If llvm::None, then this stops tracing the whole process.
Definition: TraceGDBRemotePackets.h:77
lldb_private::TraceIntelPTGetStateResponse
Definition: TraceIntelPTGDBRemotePackets.h:111
lldb_private::TraceGetStateResponse::process_binary_data
std::vector< TraceBinaryData > process_binary_data
Definition: TraceGDBRemotePackets.h:134
lldb_private::TraceStartRequest::tids
llvm::Optional< std::vector< lldb::tid_t > > tids
If llvm::None, then this starts tracing the whole process.
Definition: TraceGDBRemotePackets.h:49
lldb_private::process_linux::IntelPTCollector::TraceStop
llvm::Error TraceStop(const TraceStopRequest &request)
Implementation of the jLLDBTraceStop packet.
Definition: IntelPTCollector.cpp:52
Procfs.h
lldb_private::process_linux::IntelPTCollector::m_process
NativeProcessProtocol & m_process
The target process.
Definition: IntelPTCollector.h:79
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
lldb_private::TraceStartRequest::IsProcessTracing
bool IsProcessTracing() const
jLLDBTraceStart
Definition: TraceGDBRemotePackets.cpp:32
lldb_private::process_linux::IntelPTCollector::GetBinaryData
llvm::Expected< std::vector< uint8_t > > GetBinaryData(const TraceGetBinaryDataRequest &request)
Implementation of the jLLDBTraceGetBinaryData packet.
Definition: IntelPTCollector.cpp:222
lldb_private::process_linux::IntelPTThreadTraceCollection::TracesThread
bool TracesThread(lldb::tid_t tid) const
Definition: IntelPTThreadTraceCollection.cpp:16
lldb_private::process_linux::IntelPTSingleBufferTrace::GetIptTraceSize
size_t GetIptTraceSize() const
Definition: IntelPTSingleBufferTrace.cpp:200
lldb_private::process_linux::IntelPTCollector::Clear
void Clear()
Dispose of all traces.
Definition: IntelPTCollector.cpp:261
lldb_private::process_linux::IntelPTSingleBufferTrace
This class wraps a single perf event collecting intel pt data in a single buffer.
Definition: IntelPTSingleBufferTrace.h:25
lldb_private::process_linux::IntelPTCollector::TraceStart
llvm::Error TraceStart(const TraceIntelPTStartRequest &request)
Implementation of the jLLDBTraceStart packet.
Definition: IntelPTCollector.cpp:98
StreamString.h
lldb_private::NativeProcessProtocol::GetID
lldb::pid_t GetID() const
Definition: NativeProcessProtocol.h:183
lldb_private::process_linux::IntelPTCollector::OnThreadCreated
llvm::Error OnThreadCreated(lldb::tid_t tid)
If "process tracing" is enabled, then trace the given thread.
Definition: IntelPTCollector.cpp:179
lldb_private::process_linux::IntelPTPerThreadProcessTrace::Start
static llvm::Expected< std::unique_ptr< IntelPTPerThreadProcessTrace > > Start(const TraceIntelPTStartRequest &request, llvm::ArrayRef< lldb::tid_t > current_tids)
Start tracing the current process by tracing each of its tids individually.
Definition: IntelPTPerThreadProcessTrace.cpp:55
lldb_private::TraceGetStateResponse::AddWarning
void AddWarning(llvm::StringRef warning)
Definition: TraceGDBRemotePackets.cpp:113
lldb_private::TraceIntelPTStartRequest
jLLDBTraceStart gdb-remote packet
Definition: TraceIntelPTGDBRemotePackets.h:33
lldb_private::process_linux::IntelPTMultiCoreTrace::StartOnAllCores
static llvm::Expected< std::unique_ptr< IntelPTMultiCoreTrace > > StartOnAllCores(const TraceIntelPTStartRequest &request, NativeProcessProtocol &process, llvm::Optional< int > cgroup_fd=llvm::None)
Start tracing all CPU cores.
Definition: IntelPTMultiCoreTrace.cpp:36
ProcessPOSIXLog.h
lldb_private::process_linux::IntelPTCollector::ProcessWillResume
void ProcessWillResume()
To be invoked before the process will resume, so that we can capture the first instructions after the...
Definition: IntelPTCollector.cpp:169
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::process_linux::GetProcfsCpuInfo
llvm::Expected< llvm::ArrayRef< uint8_t > > GetProcfsCpuInfo()
Definition: Procfs.cpp:19
lldb_private::IntelPTDataKinds::kIptTrace
static const char * kIptTrace
Definition: TraceIntelPTGDBRemotePackets.h:27
lldb_private::process_linux::IntelPTThreadTraceCollection::ForEachThread
void ForEachThread(std::function< void(lldb::tid_t tid, IntelPTSingleBufferTrace &thread_trace)> callback)
Execute the provided callback on each thread that is being traced.
Definition: IntelPTThreadTraceCollection.cpp:50
IntelPTCollector.h
lldb_private::TraceStopRequest
jLLDBTraceStop gdb-remote packet
Definition: TraceGDBRemotePackets.h:64
lldb_private::process_linux::IntelPTCollector::OnThreadDestroyed
llvm::Error OnThreadDestroyed(lldb::tid_t tid)
Stops tracing a tracing upon a destroy event.
Definition: IntelPTCollector.cpp:186
lldb_private::process_linux::IntelPTThreadTraceCollection::TraceStart
llvm::Error TraceStart(lldb::tid_t tid, const TraceIntelPTStartRequest &request)
Start tracing the thread given by its tid.
Definition: IntelPTThreadTraceCollection.cpp:30
lldb_private::process_linux::IntelPTThreadTraceCollection::Clear
void Clear()
Dispose of all traces.
Definition: IntelPTThreadTraceCollection.cpp:66
lldb::pid_t
uint64_t pid_t
Definition: lldb-types.h:85
Support.h
lldb_private::NativeProcessProtocol
Definition: NativeProcessProtocol.h:48
lldb_private::process_linux::IntelPTCollector::m_thread_traces
IntelPTThreadTraceCollection m_thread_traces
Threads traced due to "thread tracing".
Definition: IntelPTCollector.h:81
lldb_private::TraceGetBinaryDataRequest::tid
llvm::Optional< lldb::tid_t > tid
Optional tid if the data is related to a thread.
Definition: TraceGDBRemotePackets.h:155
lldb_private::TraceIntelPTStartRequest::enable_tsc
bool enable_tsc
Whether to enable TSC.
Definition: TraceIntelPTGDBRemotePackets.h:38
Perf.h
lldb_private::TraceGetBinaryDataRequest::cpu_id
llvm::Optional< lldb::cpu_id_t > cpu_id
Optional core id if the data is related to a cpu core.
Definition: TraceGDBRemotePackets.h:157
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::process_linux::IntelPTCollector::ProcessDidStop
void ProcessDidStop()
To be invoked as soon as we know the process stopped.
Definition: IntelPTCollector.cpp:174
lldb_private::process_linux::IntelPTThreadTraceCollection::TraceStop
llvm::Error TraceStop(lldb::tid_t tid)
Stop tracing the thread given by its tid.
Definition: IntelPTThreadTraceCollection.cpp:20
lldb_private::process_linux::IntelPTThreadTraceCollection::GetTracedThreadsCount
size_t GetTracedThreadsCount() const
Definition: IntelPTThreadTraceCollection.cpp:71
Error
llvm::Error Error
Definition: UdtRecordCompleter.cpp:34
lldb_private::process_linux::IntelPTCollector::GetState
llvm::Expected< llvm::json::Value > GetState()
Implementation of the jLLDBTraceGetState packet.
Definition: IntelPTCollector.cpp:194
lldb_private::process_linux::GetIntelPTOSEventType
llvm::Expected< uint32_t > GetIntelPTOSEventType()
Return the Linux perf event type for Intel PT.
Definition: IntelPTSingleBufferTrace.cpp:107
lldb_private::process_linux::IntelPTCollector::m_process_trace_up
IntelPTProcessTraceUP m_process_trace_up
Only one instance of "process trace" can be active at a given time.
Definition: IntelPTCollector.h:85
lldb_private::TraceIntelPTGetStateResponse::tsc_perf_zero_conversion
llvm::Optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
The TSC to wall time conversion if it exists, otherwise nullptr.
Definition: TraceIntelPTGDBRemotePackets.h:113
GetCGroupFileDescriptor
static Optional< int > GetCGroupFileDescriptor(lldb::pid_t pid)
Definition: IntelPTCollector.cpp:68
lldb_private::process_linux::IntelPTCollector::FetchPerfTscConversionParameters
llvm::Expected< LinuxPerfZeroTscConversion & > FetchPerfTscConversionParameters()
Definition: IntelPTCollector.cpp:36
lldb
Definition: SBAddress.h:15
lldb_private::TraceIntelPTStartRequest::disable_cgroup_filtering
llvm::Optional< bool > disable_cgroup_filtering
Disable the cgroup filtering that is automatically applied in per cpu mode.
Definition: TraceIntelPTGDBRemotePackets.h:55
lldb_private::process_linux::IntelPTThreadTraceCollection::TryGetBinaryData
llvm::Expected< llvm::Optional< std::vector< uint8_t > > > TryGetBinaryData(const TraceGetBinaryDataRequest &request)
Definition: IntelPTThreadTraceCollection.cpp:76
lldb_private::IntelPTDataKinds::kProcFsCpuInfo
static const char * kProcFsCpuInfo
Definition: TraceIntelPTGDBRemotePackets.h:26
lldb::tid_t
uint64_t tid_t
Definition: lldb-types.h:86