2017-06-28 15:58:31 +08:00
|
|
|
//===-- ProcessorTrace.cpp ------------------------------------ -*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/Support/Error.h"
|
|
|
|
#include "llvm/Support/MathExtras.h"
|
|
|
|
|
|
|
|
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
|
|
|
|
#include "ProcessorTrace.h"
|
|
|
|
#include "lldb/Host/linux/Support.h"
|
|
|
|
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
using namespace process_linux;
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
lldb::user_id_t ProcessorTraceMonitor::m_trace_num = 1;
|
|
|
|
|
|
|
|
Status ProcessorTraceMonitor::GetTraceConfig(TraceOptions &config) const {
|
|
|
|
#ifndef PERF_ATTR_SIZE_VER5
|
|
|
|
llvm_unreachable("perf event not supported");
|
|
|
|
#else
|
|
|
|
Status error;
|
|
|
|
|
|
|
|
config.setType(lldb::TraceType::eTraceTypeProcessorTrace);
|
|
|
|
config.setMetaDataBufferSize(m_mmap_meta->data_size);
|
|
|
|
|
|
|
|
config.setTraceBufferSize(m_mmap_meta->aux_size);
|
|
|
|
|
|
|
|
error = GetCPUType(config);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
|
|
|
|
const TraceOptions &config) {
|
|
|
|
#ifndef PERF_ATTR_SIZE_VER5
|
|
|
|
llvm_unreachable("perf event not supported");
|
|
|
|
#else
|
2017-07-03 23:07:36 +08:00
|
|
|
Status error;
|
|
|
|
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
|
2017-06-28 15:58:31 +08:00
|
|
|
|
|
|
|
LLDB_LOG(log, "called thread id {0}", tid);
|
|
|
|
uint64_t page_size = getpagesize();
|
|
|
|
uint64_t bufsize = config.getTraceBufferSize();
|
|
|
|
uint64_t metabufsize = config.getMetaDataBufferSize();
|
|
|
|
|
|
|
|
uint64_t numpages = static_cast<uint64_t>(
|
|
|
|
llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
|
2017-07-05 22:54:46 +08:00
|
|
|
numpages = std::max<uint64_t>(1, numpages);
|
2017-06-28 15:58:31 +08:00
|
|
|
bufsize = page_size * numpages;
|
|
|
|
|
|
|
|
numpages = static_cast<uint64_t>(
|
|
|
|
llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
|
|
|
|
metabufsize = page_size * numpages;
|
|
|
|
|
|
|
|
perf_event_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
|
attr.size = sizeof(attr);
|
|
|
|
attr.exclude_kernel = 1;
|
|
|
|
attr.sample_type = PERF_SAMPLE_TIME;
|
|
|
|
attr.sample_id_all = 1;
|
|
|
|
attr.exclude_hv = 1;
|
|
|
|
attr.exclude_idle = 1;
|
|
|
|
attr.mmap = 1;
|
|
|
|
|
|
|
|
int intel_pt_type = 0;
|
|
|
|
|
|
|
|
auto ret = llvm::MemoryBuffer::getFileAsStream(
|
|
|
|
"/sys/bus/event_source/devices/intel_pt/type");
|
|
|
|
if (!ret) {
|
|
|
|
LLDB_LOG(log, "failed to open Config file");
|
|
|
|
return ret.getError();
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef rest = ret.get()->getBuffer();
|
|
|
|
if (rest.empty() || rest.trim().getAsInteger(10, intel_pt_type)) {
|
|
|
|
LLDB_LOG(log, "failed to read Config file");
|
|
|
|
error.SetErrorString("invalid file");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
rest.trim().getAsInteger(10, intel_pt_type);
|
|
|
|
LLDB_LOG(log, "intel pt type {0}", intel_pt_type);
|
|
|
|
attr.type = intel_pt_type;
|
|
|
|
|
|
|
|
LLDB_LOG(log, "meta buffer size {0}", metabufsize);
|
|
|
|
LLDB_LOG(log, "buffer size {0} ", bufsize);
|
|
|
|
|
|
|
|
if (error.Fail()) {
|
|
|
|
LLDB_LOG(log, "Status in custom config");
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
auto fd =
|
|
|
|
syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
|
|
|
|
if (fd == -1) {
|
|
|
|
LLDB_LOG(log, "syscall error {0}", errno);
|
|
|
|
error.SetErrorString("perf event syscall Failed");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2017-07-03 23:07:36 +08:00
|
|
|
m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
|
2017-06-28 15:58:31 +08:00
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
auto base =
|
|
|
|
mmap(NULL, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
|
|
|
|
|
|
|
|
if (base == MAP_FAILED) {
|
|
|
|
LLDB_LOG(log, "mmap base error {0}", errno);
|
|
|
|
error.SetErrorString("Meta buffer allocation failed");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2017-07-03 23:07:36 +08:00
|
|
|
m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
|
2017-06-28 15:58:31 +08:00
|
|
|
reinterpret_cast<perf_event_mmap_page *>(base),
|
2017-07-03 23:07:36 +08:00
|
|
|
munmap_delete(metabufsize + page_size));
|
2017-06-28 15:58:31 +08:00
|
|
|
|
|
|
|
m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
|
|
|
|
m_mmap_meta->aux_size = bufsize;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
auto mmap_aux = mmap(NULL, bufsize, PROT_READ, MAP_SHARED, fd,
|
|
|
|
static_cast<long int>(m_mmap_meta->aux_offset));
|
|
|
|
|
|
|
|
if (mmap_aux == MAP_FAILED) {
|
|
|
|
LLDB_LOG(log, "second mmap done {0}", errno);
|
|
|
|
error.SetErrorString("Trace buffer allocation failed");
|
|
|
|
return error;
|
|
|
|
}
|
2017-07-03 23:07:36 +08:00
|
|
|
m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
|
|
|
|
reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize));
|
2017-06-28 15:58:31 +08:00
|
|
|
return error;
|
2017-07-03 23:07:36 +08:00
|
|
|
#endif
|
2017-06-28 15:58:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetDataBuffer() {
|
|
|
|
#ifndef PERF_ATTR_SIZE_VER5
|
|
|
|
llvm_unreachable("perf event not supported");
|
|
|
|
#else
|
|
|
|
return MutableArrayRef<uint8_t>(
|
|
|
|
(reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
|
|
|
|
m_mmap_meta->data_offset),
|
|
|
|
m_mmap_meta->data_size);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetAuxBuffer() {
|
|
|
|
#ifndef PERF_ATTR_SIZE_VER5
|
|
|
|
llvm_unreachable("perf event not supported");
|
|
|
|
#else
|
|
|
|
return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
|
|
|
|
|
|
|
|
Status error;
|
|
|
|
uint64_t cpu_family = -1;
|
|
|
|
uint64_t model = -1;
|
|
|
|
uint64_t stepping = -1;
|
|
|
|
std::string vendor_id;
|
|
|
|
|
|
|
|
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
|
|
|
|
|
|
|
|
auto BufferOrError = getProcFile("cpuinfo");
|
|
|
|
if (!BufferOrError)
|
|
|
|
return BufferOrError.getError();
|
|
|
|
|
|
|
|
LLDB_LOG(log, "GetCPUType Function");
|
|
|
|
|
|
|
|
StringRef Rest = BufferOrError.get()->getBuffer();
|
|
|
|
while (!Rest.empty()) {
|
|
|
|
StringRef Line;
|
|
|
|
std::tie(Line, Rest) = Rest.split('\n');
|
|
|
|
|
|
|
|
SmallVector<StringRef, 2> columns;
|
|
|
|
Line.split(columns, StringRef(":"), -1, false);
|
|
|
|
|
|
|
|
if (columns.size() < 2)
|
|
|
|
continue; // continue searching
|
|
|
|
|
|
|
|
columns[1] = columns[1].trim(" ");
|
|
|
|
if (columns[0].contains("cpu family") &&
|
|
|
|
columns[1].getAsInteger(10, cpu_family))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
else if (columns[0].contains("stepping") &&
|
|
|
|
columns[1].getAsInteger(10, stepping))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
else if (columns[0].contains("vendor_id")) {
|
|
|
|
vendor_id = columns[1].str();
|
|
|
|
if (!vendor_id.empty())
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id);
|
|
|
|
|
|
|
|
if ((cpu_family != static_cast<uint64_t>(-1)) &&
|
|
|
|
(model != static_cast<uint64_t>(-1)) &&
|
|
|
|
(stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
|
|
|
|
auto params_dict = std::make_shared<StructuredData::Dictionary>();
|
|
|
|
params_dict->AddIntegerItem("cpu_family", cpu_family);
|
|
|
|
params_dict->AddIntegerItem("cpu_model", model);
|
|
|
|
params_dict->AddIntegerItem("cpu_stepping", stepping);
|
|
|
|
params_dict->AddStringItem("cpu_vendor", vendor_id);
|
|
|
|
|
|
|
|
llvm::StringRef intel_custom_params_key("intel-pt");
|
|
|
|
|
|
|
|
auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
|
|
|
|
intel_custom_params->AddItem(
|
|
|
|
intel_custom_params_key,
|
|
|
|
StructuredData::ObjectSP(std::move(params_dict)));
|
|
|
|
|
|
|
|
config.setTraceParams(intel_custom_params);
|
|
|
|
return error; // we are done
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error.SetErrorString("cpu info not found");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Expected<ProcessorTraceMonitorUP>
|
|
|
|
ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
|
|
|
|
const TraceOptions &config,
|
|
|
|
bool useProcessSettings) {
|
|
|
|
|
|
|
|
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
|
|
|
|
|
|
|
|
Status error;
|
|
|
|
if (tid == LLDB_INVALID_THREAD_ID) {
|
|
|
|
error.SetErrorString("thread not specified");
|
2017-07-03 23:07:36 +08:00
|
|
|
return error.ToError();
|
2017-06-28 15:58:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
|
|
|
|
|
|
|
|
error = pt_monitor_up->StartTrace(pid, tid, config);
|
|
|
|
if (error.Fail())
|
2017-07-03 23:07:36 +08:00
|
|
|
return error.ToError();
|
2017-06-28 15:58:31 +08:00
|
|
|
|
|
|
|
pt_monitor_up->SetThreadID(tid);
|
|
|
|
|
|
|
|
if (useProcessSettings) {
|
|
|
|
pt_monitor_up->SetTraceID(0);
|
|
|
|
} else {
|
|
|
|
pt_monitor_up->SetTraceID(m_trace_num++);
|
|
|
|
LLDB_LOG(log, "Trace ID {0}", m_trace_num);
|
|
|
|
}
|
|
|
|
return std::move(pt_monitor_up);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status
|
|
|
|
ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
|
|
|
|
size_t offset) {
|
|
|
|
#ifndef PERF_ATTR_SIZE_VER5
|
|
|
|
llvm_unreachable("perf event not supported");
|
|
|
|
#else
|
2017-07-03 23:07:36 +08:00
|
|
|
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
|
|
|
|
Status error;
|
2017-06-28 15:58:31 +08:00
|
|
|
uint64_t head = m_mmap_meta->aux_head;
|
|
|
|
|
|
|
|
LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When configured as ring buffer, the aux buffer keeps wrapping around
|
|
|
|
* the buffer and its not possible to detect how many times the buffer
|
|
|
|
* wrapped. Initially the buffer is filled with zeros,as shown below
|
|
|
|
* so in order to get complete buffer we first copy firstpartsize, followed
|
|
|
|
* by any left over part from beginning to aux_head
|
|
|
|
*
|
|
|
|
* aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
|
|
|
|
* aux_head->||<- firstpartsize ->|
|
|
|
|
*
|
|
|
|
* */
|
|
|
|
|
|
|
|
ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
|
|
|
|
LLDB_LOG(log, "ReadCyclic BUffer Done");
|
|
|
|
return error;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
Status
|
|
|
|
ProcessorTraceMonitor::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
|
|
|
|
size_t offset) {
|
|
|
|
#ifndef PERF_ATTR_SIZE_VER5
|
|
|
|
llvm_unreachable("perf event not supported");
|
|
|
|
#else
|
2017-07-03 23:07:36 +08:00
|
|
|
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
|
|
|
|
uint64_t bytes_remaining = buffer.size();
|
|
|
|
Status error;
|
2017-06-28 15:58:31 +08:00
|
|
|
|
|
|
|
uint64_t head = m_mmap_meta->data_head;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The data buffer and aux buffer have different implementations
|
|
|
|
* with respect to their definition of head pointer. In the case
|
|
|
|
* of Aux data buffer the head always wraps around the aux buffer
|
|
|
|
* and we don't need to care about it, whereas the data_head keeps
|
|
|
|
* increasing and needs to be wrapped by modulus operator
|
|
|
|
*/
|
|
|
|
|
|
|
|
LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
|
|
|
|
|
|
|
|
auto data_buffer = GetDataBuffer();
|
|
|
|
|
|
|
|
if (head > data_buffer.size()) {
|
|
|
|
head = head % data_buffer.size();
|
|
|
|
LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
|
|
|
|
|
|
|
|
ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
|
|
|
|
bytes_remaining -= buffer.size();
|
|
|
|
} else {
|
|
|
|
LLDB_LOG(log, "Head - {0}", head);
|
|
|
|
if (offset >= head) {
|
|
|
|
LLDB_LOG(log, "Invalid Offset ");
|
|
|
|
error.SetErrorString("invalid offset");
|
|
|
|
buffer = buffer.slice(buffer.size());
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto data = data_buffer.slice(offset, (head - offset));
|
|
|
|
auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
|
|
|
|
bytes_remaining -= (remaining - buffer.begin());
|
|
|
|
}
|
|
|
|
buffer = buffer.drop_back(bytes_remaining);
|
|
|
|
return error;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessorTraceMonitor::ReadCyclicBuffer(
|
|
|
|
llvm::MutableArrayRef<uint8_t> &dst, llvm::MutableArrayRef<uint8_t> src,
|
|
|
|
size_t src_cyc_index, size_t offset) {
|
|
|
|
|
|
|
|
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
|
|
|
|
|
|
|
|
if (dst.empty() || src.empty()) {
|
|
|
|
dst = dst.drop_back(dst.size());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dst.data() == nullptr || src.data() == nullptr) {
|
|
|
|
dst = dst.drop_back(dst.size());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src_cyc_index > src.size()) {
|
|
|
|
dst = dst.drop_back(dst.size());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset >= src.size()) {
|
|
|
|
LLDB_LOG(log, "Too Big offset ");
|
|
|
|
dst = dst.drop_back(dst.size());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
|
|
|
|
src.slice(src_cyc_index), src.take_front(src_cyc_index)};
|
|
|
|
|
|
|
|
if (offset > parts[0].size()) {
|
|
|
|
parts[1] = parts[1].slice(offset - parts[0].size());
|
|
|
|
parts[0] = parts[0].drop_back(parts[0].size());
|
|
|
|
} else if (offset == parts[0].size()) {
|
|
|
|
parts[0] = parts[0].drop_back(parts[0].size());
|
|
|
|
} else {
|
|
|
|
parts[0] = parts[0].slice(offset);
|
|
|
|
}
|
|
|
|
auto next = dst.begin();
|
|
|
|
auto bytes_left = dst.size();
|
|
|
|
for (auto part : parts) {
|
|
|
|
size_t chunk_size = std::min(part.size(), bytes_left);
|
|
|
|
next = std::copy_n(part.begin(), chunk_size, next);
|
|
|
|
bytes_left -= chunk_size;
|
|
|
|
}
|
|
|
|
dst = dst.drop_back(bytes_left);
|
|
|
|
}
|