Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.h =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTCollector.h +++ lldb/source/Plugins/Process/Linux/IntelPTCollector.h @@ -9,6 +9,7 @@ #ifndef liblldb_IntelPTCollector_H_ #define liblldb_IntelPTCollector_H_ +#include "Perf.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "lldb/lldb-types.h" @@ -32,34 +33,10 @@ class IntelPTThreadTrace { - class munmap_delete { - size_t m_length; - - public: - munmap_delete(size_t length) : m_length(length) {} - void operator()(void *ptr) { - if (m_length) - munmap(ptr, m_length); - } - }; - - class file_close { - - public: - file_close() = default; - void operator()(int *ptr) { - if (ptr == nullptr) - return; - if (*ptr == -1) - return; - close(*ptr); - std::default_delete()(ptr); - } - }; - - std::unique_ptr m_mmap_meta; - std::unique_ptr m_mmap_aux; - std::unique_ptr m_fd; + std::unique_ptr + m_mmap_meta; + std::unique_ptr m_mmap_aux; + std::unique_ptr m_fd; lldb::tid_t m_tid; /// Start tracing a thread @@ -88,8 +65,9 @@ llvm::MutableArrayRef GetDataBuffer() const; IntelPTThreadTrace() - : m_mmap_meta(nullptr, munmap_delete(0)), - m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()) {} + : m_mmap_meta(nullptr, resource_handle::munmap_delete(0)), + m_mmap_aux(nullptr, resource_handle::munmap_delete(0)), + m_fd(nullptr, resource_handle::file_close()) {} public: /// Get the content of /proc/cpuinfo that can be later used to decode traces. Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp +++ lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "llvm/ADT/StringRef.h" @@ -15,6 +16,7 @@ #include "llvm/Support/MathExtras.h" #include "IntelPTCollector.h" +#include "Perf.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Host/linux/Support.h" #include "lldb/Utility/StreamString.h" @@ -143,6 +145,52 @@ error << "."; return createStringError(inconvertibleErrorCode(), error.str().c_str()); } +llvm::Expected +FetchPerfTscConversionParameters(lldb::pid_t pid) { + perf_event_attr attr; + memset(&attr, 0, sizeof(attr)); + // Set the minimum attributes necessary to access the mmap metadata page. + attr.size = sizeof(attr); + attr.type = PERF_TYPE_SOFTWARE; + attr.config = PERF_COUNT_SW_DUMMY; + + errno = 0; + auto fd = syscall(SYS_perf_event_open, &attr, pid, -1, -1, 0); + if (fd == -1) { + std::string err_msg = + llvm::formatv("perf event syscall failed: {0}", std::strerror(errno)); + return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg); + } + auto fd_handle = std::unique_ptr( + new int(fd), resource_handle::file_close()); + + uint64_t page_size = getpagesize(); + errno = 0; + auto base = mmap(nullptr, page_size, PROT_READ, MAP_SHARED, fd, 0); + + if (base == MAP_FAILED) { + std::string err_msg = llvm::formatv("perf mmap meta allocation failed: {0}", + std::strerror(errno)); + return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg); + } + auto perf_mmap_meta_handle = + std::unique_ptr( + reinterpret_cast(base), + resource_handle::munmap_delete(page_size)); + + if (perf_mmap_meta_handle->cap_user_time && + perf_mmap_meta_handle->cap_user_time_zero) { + return PerfTscConversionParameters{perf_mmap_meta_handle->time_mult, + perf_mmap_meta_handle->time_shift, + perf_mmap_meta_handle->time_zero}; + } else { + auto err_cap = !perf_mmap_meta_handle->cap_user_time ? "cap_user_time" + : "cap_user_time_zero"; + std::string err_msg = llvm::formatv( + "{0} not supported. TSC cannot be converted to time unit", err_cap); + return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg); + } +} size_t IntelPTThreadTrace::GetTraceBufferSize() const { #ifndef PERF_ATTR_SIZE_VER5 @@ -236,7 +284,8 @@ "perf event syscall failed"); } - m_fd = std::unique_ptr(new int(fd), file_close()); + m_fd = std::unique_ptr( + new int(fd), resource_handle::file_close()); errno = 0; auto base = @@ -248,9 +297,10 @@ "Meta buffer allocation failed"); } - m_mmap_meta = std::unique_ptr( - reinterpret_cast(base), - munmap_delete(buffer_size + page_size)); + m_mmap_meta = + std::unique_ptr( + reinterpret_cast(base), + resource_handle::munmap_delete(buffer_size + page_size)); m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size; m_mmap_meta->aux_size = buffer_size; @@ -264,8 +314,9 @@ return createStringError(inconvertibleErrorCode(), "Trace buffer allocation failed"); } - m_mmap_aux = std::unique_ptr( - reinterpret_cast(mmap_aux), munmap_delete(buffer_size)); + m_mmap_aux = std::unique_ptr( + reinterpret_cast(mmap_aux), + resource_handle::munmap_delete(buffer_size)); return Error::success(); #endif } Index: lldb/source/Plugins/Process/Linux/Perf.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Linux/Perf.h @@ -0,0 +1,78 @@ +//===-- Perf.h --------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Perf_H_ +#define lldb_Perf_H_ + +#include "lldb/lldb-types.h" +#include "llvm/Support/Error.h" + +#include +#include + +namespace lldb_private { +namespace process_linux { +namespace resource_handle { + +/// Custom deleter for the pointer returned by \a mmap. +/// +/// This functor type is provided to \a unique_ptr to properly +/// unmap the region at destruction time. +class munmap_delete { +public: + munmap_delete(size_t length) : m_length(length) {} + void operator()(void *ptr) { + if (m_length) + munmap(ptr, m_length); + } + +private: + size_t m_length; +}; + +/// Custom deleter for a file descriptor. +/// +/// This functor type is provided to \a unique_ptr to properly release +/// the resources associated with the file descriptor at destruction time. +class file_close { +public: + file_close() = default; + void operator()(int *ptr) { + if (ptr == nullptr) + return; + if (*ptr == -1) + return; + close(*ptr); + std::default_delete()(ptr); + } +}; +} // namespace resource_handle + +/// TSC to nanoseconds conversion values defined by the Linux perf_event API +/// when the capibilities cap_user_time and cap_user_time_zero are set. See the +/// documentation of `time_zero` in +/// https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more +/// information. +struct PerfTscConversionParameters { + uint32_t m_time_mult; + uint16_t m_time_shift; + uint64_t m_time_zero; +}; + +/// Fetch \a PerfTscConversionParameters from \a perf_event_mmap_page, if +/// available. +/// +/// \param[in] pid +/// pid used to make the perf_event_open syscall. +llvm::Expected +FetchPerfTscConversionParameters(lldb::pid_t pid); + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_Perf_H_