diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp')
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp b/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp new file mode 100644 index 00000000000..7043d50256e --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp @@ -0,0 +1,400 @@ +//===-- 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) { + Status error; + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); + LLDB_LOG(log, "{0}", config.getThreadID()); + +#ifndef PERF_ATTR_SIZE_VER5 + llvm_unreachable("perf event not supported"); +#else + + 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)); + numpages = std::max(1ul, numpages); + bufsize = page_size * numpages; + + numpages = static_cast<uint64_t>( + llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size)); + numpages = std::max(0ul, numpages); + 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; + } + + m_fd = std::move(std::unique_ptr<int, file_close>(new int(fd), file_close())); + + 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; + } + + m_mmap_meta = std::move(std::unique_ptr<perf_event_mmap_page, munmap_delete>( + reinterpret_cast<perf_event_mmap_page *>(base), + munmap_delete(metabufsize + page_size))); + + 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; + } + m_mmap_aux = std::move(std::unique_ptr<uint8_t, munmap_delete>( + reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize))); +#endif + return error; +} + +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"); + return std::move(error.ToError()); + } + + ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor); + + error = pt_monitor_up->StartTrace(pid, tid, config); + if (error.Fail()) + return std::move(error.ToError()); + + 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) { + + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); + Status error; + +#ifndef PERF_ATTR_SIZE_VER5 + llvm_unreachable("perf event not supported"); +#else + 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) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); + uint64_t bytes_remaining = buffer.size(); + Status error; +#ifndef PERF_ATTR_SIZE_VER5 + llvm_unreachable("perf event not supported"); +#else + + 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); +} |

