LLDB  mainline
ProcessorTrace.cpp
Go to the documentation of this file.
1 //===-- ProcessorTrace.cpp ------------------------------------ -*- C++ -*-===//
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 <algorithm>
10 #include <fstream>
11 
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/Support/Error.h"
14 #include "llvm/Support/MathExtras.h"
15 
17 #include "ProcessorTrace.h"
19 
20 #include <sys/syscall.h>
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 using namespace process_linux;
25 using namespace llvm;
26 
27 lldb::user_id_t ProcessorTraceMonitor::m_trace_num = 1;
28 
29 Status ProcessorTraceMonitor::GetTraceConfig(TraceOptions &config) const {
30 #ifndef PERF_ATTR_SIZE_VER5
31  llvm_unreachable("perf event not supported");
32 #else
33  Status error;
34 
36  config.setMetaDataBufferSize(m_mmap_meta->data_size);
37 
38  config.setTraceBufferSize(m_mmap_meta->aux_size);
39 
40  error = GetCPUType(config);
41 
42  return error;
43 #endif
44 }
45 
46 Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
47  const TraceOptions &config) {
48 #ifndef PERF_ATTR_SIZE_VER5
49  llvm_unreachable("perf event not supported");
50 #else
51  Status error;
53 
54  LLDB_LOG(log, "called thread id {0}", tid);
55  uint64_t page_size = getpagesize();
56  uint64_t bufsize = config.getTraceBufferSize();
57  uint64_t metabufsize = config.getMetaDataBufferSize();
58 
59  uint64_t numpages = static_cast<uint64_t>(
60  llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
61  numpages = std::max<uint64_t>(1, numpages);
62  bufsize = page_size * numpages;
63 
64  numpages = static_cast<uint64_t>(
65  llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
66  metabufsize = page_size * numpages;
67 
68  perf_event_attr attr;
69  memset(&attr, 0, sizeof(attr));
70  attr.size = sizeof(attr);
71  attr.exclude_kernel = 1;
72  attr.sample_type = PERF_SAMPLE_TIME;
73  attr.sample_id_all = 1;
74  attr.exclude_hv = 1;
75  attr.exclude_idle = 1;
76  attr.mmap = 1;
77 
78  int intel_pt_type = 0;
79 
80  auto ret = llvm::MemoryBuffer::getFileAsStream(
81  "/sys/bus/event_source/devices/intel_pt/type");
82  if (!ret) {
83  LLDB_LOG(log, "failed to open Config file");
84  return ret.getError();
85  }
86 
87  StringRef rest = ret.get()->getBuffer();
88  if (rest.empty() || rest.trim().getAsInteger(10, intel_pt_type)) {
89  LLDB_LOG(log, "failed to read Config file");
90  error.SetErrorString("invalid file");
91  return error;
92  }
93 
94  rest.trim().getAsInteger(10, intel_pt_type);
95  LLDB_LOG(log, "intel pt type {0}", intel_pt_type);
96  attr.type = intel_pt_type;
97 
98  LLDB_LOG(log, "meta buffer size {0}", metabufsize);
99  LLDB_LOG(log, "buffer size {0} ", bufsize);
100 
101  if (error.Fail()) {
102  LLDB_LOG(log, "Status in custom config");
103 
104  return error;
105  }
106 
107  errno = 0;
108  auto fd =
109  syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
110  if (fd == -1) {
111  LLDB_LOG(log, "syscall error {0}", errno);
112  error.SetErrorString("perf event syscall Failed");
113  return error;
114  }
115 
116  m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
117 
118  errno = 0;
119  auto base =
120  mmap(NULL, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
121 
122  if (base == MAP_FAILED) {
123  LLDB_LOG(log, "mmap base error {0}", errno);
124  error.SetErrorString("Meta buffer allocation failed");
125  return error;
126  }
127 
128  m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
129  reinterpret_cast<perf_event_mmap_page *>(base),
130  munmap_delete(metabufsize + page_size));
131 
132  m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
133  m_mmap_meta->aux_size = bufsize;
134 
135  errno = 0;
136  auto mmap_aux = mmap(NULL, bufsize, PROT_READ, MAP_SHARED, fd,
137  static_cast<long int>(m_mmap_meta->aux_offset));
138 
139  if (mmap_aux == MAP_FAILED) {
140  LLDB_LOG(log, "second mmap done {0}", errno);
141  error.SetErrorString("Trace buffer allocation failed");
142  return error;
143  }
144  m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
145  reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize));
146  return error;
147 #endif
148 }
149 
150 llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetDataBuffer() {
151 #ifndef PERF_ATTR_SIZE_VER5
152  llvm_unreachable("perf event not supported");
153 #else
154  return MutableArrayRef<uint8_t>(
155  (reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
156  m_mmap_meta->data_offset),
157  m_mmap_meta->data_size);
158 #endif
159 }
160 
161 llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetAuxBuffer() {
162 #ifndef PERF_ATTR_SIZE_VER5
163  llvm_unreachable("perf event not supported");
164 #else
165  return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
166 #endif
167 }
168 
169 Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
170 
171  Status error;
172  uint64_t cpu_family = -1;
173  uint64_t model = -1;
174  uint64_t stepping = -1;
175  std::string vendor_id;
176 
178 
179  auto BufferOrError = getProcFile("cpuinfo");
180  if (!BufferOrError)
181  return BufferOrError.getError();
182 
183  LLDB_LOG(log, "GetCPUType Function");
184 
185  StringRef Rest = BufferOrError.get()->getBuffer();
186  while (!Rest.empty()) {
187  StringRef Line;
188  std::tie(Line, Rest) = Rest.split('\n');
189 
190  SmallVector<StringRef, 2> columns;
191  Line.split(columns, StringRef(":"), -1, false);
192 
193  if (columns.size() < 2)
194  continue; // continue searching
195 
196  columns[1] = columns[1].trim(" ");
197  if (columns[0].contains("cpu family") &&
198  columns[1].getAsInteger(10, cpu_family))
199  continue;
200 
201  else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
202  continue;
203 
204  else if (columns[0].contains("stepping") &&
205  columns[1].getAsInteger(10, stepping))
206  continue;
207 
208  else if (columns[0].contains("vendor_id")) {
209  vendor_id = columns[1].str();
210  if (!vendor_id.empty())
211  continue;
212  }
213  LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id);
214 
215  if ((cpu_family != static_cast<uint64_t>(-1)) &&
216  (model != static_cast<uint64_t>(-1)) &&
217  (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
218  auto params_dict = std::make_shared<StructuredData::Dictionary>();
219  params_dict->AddIntegerItem("cpu_family", cpu_family);
220  params_dict->AddIntegerItem("cpu_model", model);
221  params_dict->AddIntegerItem("cpu_stepping", stepping);
222  params_dict->AddStringItem("cpu_vendor", vendor_id);
223 
224  llvm::StringRef intel_custom_params_key("intel-pt");
225 
226  auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
227  intel_custom_params->AddItem(
228  intel_custom_params_key,
229  StructuredData::ObjectSP(std::move(params_dict)));
230 
231  config.setTraceParams(intel_custom_params);
232  return error; // we are done
233  }
234  }
235 
236  error.SetErrorString("cpu info not found");
237  return error;
238 }
239 
240 llvm::Expected<ProcessorTraceMonitorUP>
241 ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
242  const TraceOptions &config,
243  bool useProcessSettings) {
244 
246 
247  Status error;
248  if (tid == LLDB_INVALID_THREAD_ID) {
249  error.SetErrorString("thread not specified");
250  return error.ToError();
251  }
252 
254 
255  error = pt_monitor_up->StartTrace(pid, tid, config);
256  if (error.Fail())
257  return error.ToError();
258 
259  pt_monitor_up->SetThreadID(tid);
260 
261  if (useProcessSettings) {
262  pt_monitor_up->SetTraceID(0);
263  } else {
264  pt_monitor_up->SetTraceID(m_trace_num++);
265  LLDB_LOG(log, "Trace ID {0}", m_trace_num);
266  }
267  return std::move(pt_monitor_up);
268 }
269 
270 Status
271 ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
272  size_t offset) {
273 #ifndef PERF_ATTR_SIZE_VER5
274  llvm_unreachable("perf event not supported");
275 #else
277  Status error;
278  uint64_t head = m_mmap_meta->aux_head;
279 
280  LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
281 
282  /**
283  * When configured as ring buffer, the aux buffer keeps wrapping around
284  * the buffer and its not possible to detect how many times the buffer
285  * wrapped. Initially the buffer is filled with zeros,as shown below
286  * so in order to get complete buffer we first copy firstpartsize, followed
287  * by any left over part from beginning to aux_head
288  *
289  * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
290  * aux_head->||<- firstpartsize ->|
291  *
292  * */
293 
294  ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
295  LLDB_LOG(log, "ReadCyclic BUffer Done");
296  return error;
297 #endif
298 }
299 
300 Status
301 ProcessorTraceMonitor::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
302  size_t offset) {
303 #ifndef PERF_ATTR_SIZE_VER5
304  llvm_unreachable("perf event not supported");
305 #else
307  uint64_t bytes_remaining = buffer.size();
308  Status error;
309 
310  uint64_t head = m_mmap_meta->data_head;
311 
312  /*
313  * The data buffer and aux buffer have different implementations
314  * with respect to their definition of head pointer. In the case
315  * of Aux data buffer the head always wraps around the aux buffer
316  * and we don't need to care about it, whereas the data_head keeps
317  * increasing and needs to be wrapped by modulus operator
318  */
319 
320  LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
321 
322  auto data_buffer = GetDataBuffer();
323 
324  if (head > data_buffer.size()) {
325  head = head % data_buffer.size();
326  LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
327 
328  ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
329  bytes_remaining -= buffer.size();
330  } else {
331  LLDB_LOG(log, "Head - {0}", head);
332  if (offset >= head) {
333  LLDB_LOG(log, "Invalid Offset ");
334  error.SetErrorString("invalid offset");
335  buffer = buffer.slice(buffer.size());
336  return error;
337  }
338 
339  auto data = data_buffer.slice(offset, (head - offset));
340  auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
341  bytes_remaining -= (remaining - buffer.begin());
342  }
343  buffer = buffer.drop_back(bytes_remaining);
344  return error;
345 #endif
346 }
347 
348 void ProcessorTraceMonitor::ReadCyclicBuffer(
349  llvm::MutableArrayRef<uint8_t> &dst, llvm::MutableArrayRef<uint8_t> src,
350  size_t src_cyc_index, size_t offset) {
351 
353 
354  if (dst.empty() || src.empty()) {
355  dst = dst.drop_back(dst.size());
356  return;
357  }
358 
359  if (dst.data() == nullptr || src.data() == nullptr) {
360  dst = dst.drop_back(dst.size());
361  return;
362  }
363 
364  if (src_cyc_index > src.size()) {
365  dst = dst.drop_back(dst.size());
366  return;
367  }
368 
369  if (offset >= src.size()) {
370  LLDB_LOG(log, "Too Big offset ");
371  dst = dst.drop_back(dst.size());
372  return;
373  }
374 
375  llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
376  src.slice(src_cyc_index), src.take_front(src_cyc_index)};
377 
378  if (offset > parts[0].size()) {
379  parts[1] = parts[1].slice(offset - parts[0].size());
380  parts[0] = parts[0].drop_back(parts[0].size());
381  } else if (offset == parts[0].size()) {
382  parts[0] = parts[0].drop_back(parts[0].size());
383  } else {
384  parts[0] = parts[0].slice(offset);
385  }
386  auto next = dst.begin();
387  auto bytes_left = dst.size();
388  for (auto part : parts) {
389  size_t chunk_size = std::min(part.size(), bytes_left);
390  next = std::copy_n(part.begin(), chunk_size, next);
391  bytes_left -= chunk_size;
392  }
393  dst = dst.drop_back(bytes_left);
394 }
llvm::Error ToError() const
Definition: Status.cpp:88
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
Definition: Debugger.h:71
void setMetaDataBufferSize(uint64_t size)
Definition: TraceOptions.h:40
void setType(lldb::TraceType type)
Definition: TraceOptions.h:36
void setTraceParams(const StructuredData::DictionarySP &dict_obj)
Definition: TraceOptions.h:32
#define LLDB_LOG(log,...)
Definition: Log.h:209
uint64_t user_id_t
Definition: lldb-types.h:84
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:57
uint64_t tid_t
Definition: lldb-types.h:86
uint64_t getTraceBufferSize() const
Definition: TraceOptions.h:28
void SetErrorString(llvm::StringRef err_str)
Set the current error string to err_str.
Definition: Status.cpp:241
uint64_t getMetaDataBufferSize() const
Definition: TraceOptions.h:30
std::unique_ptr< ProcessorTraceMonitor > ProcessorTraceMonitorUP
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getProcFile(::pid_t pid, ::pid_t tid, const llvm::Twine &file)
Definition: Support.cpp:14
#define LLDB_INVALID_THREAD_ID
Definition: lldb-defines.h:93
bool Fail() const
Test for error condition.
Definition: Status.cpp:181
void setTraceBufferSize(uint64_t size)
Definition: TraceOptions.h:38
Definition: SBAddress.h:15
uint64_t pid_t
Definition: lldb-types.h:85
std::shared_ptr< Object > ObjectSP
#define POSIX_LOG_PTRACE
An error handling class.
Definition: Status.h:44