[Lldb-commits] [lldb] 7b73de9 - [trace][intelpt] Support system-wide tracing [3] - Refactor IntelPTThreadTrace
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Mon May 9 16:05:36 PDT 2022
Author: Walter Erquinigo
Date: 2022-05-09T16:05:26-07:00
New Revision: 7b73de9ec2b19df040c919d3004dfbead9b6ac59
URL: https://github.com/llvm/llvm-project/commit/7b73de9ec2b19df040c919d3004dfbead9b6ac59
DIFF: https://github.com/llvm/llvm-project/commit/7b73de9ec2b19df040c919d3004dfbead9b6ac59.diff
LOG: [trace][intelpt] Support system-wide tracing [3] - Refactor IntelPTThreadTrace
I'm refactoring IntelPTThreadTrace into IntelPTSingleBufferTrace so that it can
both single threads or single cores. In this diff I'm basically renaming the
class, moving it to its own file, and removing all the pieces that are not used
along with some basic cleanup.
Differential Revision: https://reviews.llvm.org/D124648
Added:
lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h
Modified:
lldb/docs/lldb-gdb-remote.txt
lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
lldb/source/Plugins/Process/Linux/CMakeLists.txt
lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
lldb/source/Plugins/Process/Linux/IntelPTCollector.h
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Linux/Perf.cpp
lldb/source/Plugins/Process/Linux/Perf.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
lldb/unittests/Process/Linux/CMakeLists.txt
lldb/unittests/Process/Linux/PerfTests.cpp
Removed:
lldb/unittests/Process/Linux/IntelPTCollectorTests.cpp
################################################################################
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index 50a47ccf50bfb..17e85a6d0df3f 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -495,7 +495,7 @@ read packet: OK/E<error code>;AAAAAAAAA
// INTEL PT
//
// Binary data kinds:
-// - threadTraceBuffer: trace buffer for a thread.
+// - traceBuffer: trace buffer for a thread or a core.
// - procfsCpuInfo: contents of the /proc/cpuinfo file.
//
// Counter info kinds:
@@ -550,7 +550,7 @@ read packet: {...object}/E<error code>;AAAAAAAAA
// INTEL PT
//
// Binary data kinds:
-// - threadTraceBuffer: trace buffer for a thread.
+// - traceBuffer: trace buffer for a thread or a core.
// - procfsCpuInfo: contents of the /proc/cpuinfo file.
//----------------------------------------------------------------------
diff --git a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
index d331b18c4ae70..ddb758fe95e98 100644
--- a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
@@ -21,7 +21,7 @@ namespace lldb_private {
// List of data kinds used by jLLDBGetState and jLLDBGetBinaryData.
struct IntelPTDataKinds {
static const char *kProcFsCpuInfo;
- static const char *kThreadTraceBuffer;
+ static const char *kTraceBuffer;
};
/// jLLDBTraceStart gdb-remote packet
diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
index 125cc0e38ca21..fb249008146b2 100644
--- a/lldb/source/Plugins/Process/Linux/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
@@ -1,5 +1,6 @@
add_lldb_library(lldbPluginProcessLinux
IntelPTCollector.cpp
+ IntelPTSingleBufferTrace.cpp
NativeProcessLinux.cpp
NativeRegisterContextLinux.cpp
NativeRegisterContextLinux_arm.cpp
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
index bcb270c9fae74..bf0b77b39f9b8 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
@@ -32,394 +32,6 @@ using namespace lldb_private;
using namespace process_linux;
using namespace llvm;
-const char *kOSEventIntelPTTypeFile =
- "/sys/bus/event_source/devices/intel_pt/type";
-
-const char *kPSBPeriodCapFile =
- "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";
-
-const char *kPSBPeriodValidValuesFile =
- "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";
-
-const char *kTSCBitOffsetFile =
- "/sys/bus/event_source/devices/intel_pt/format/tsc";
-
-const char *kPSBPeriodBitOffsetFile =
- "/sys/bus/event_source/devices/intel_pt/format/psb_period";
-
-enum IntelPTConfigFileType {
- Hex = 0,
- // 0 or 1
- ZeroOne,
- Decimal,
- // a bit index file always starts with the prefix config: following by an int,
- // which represents the offset of the perf_event_attr.config value where to
- // store a given configuration.
- BitOffset
-};
-
-static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
- IntelPTConfigFileType type) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
- MemoryBuffer::getFileAsStream(file);
-
- if (!stream)
- return createStringError(inconvertibleErrorCode(),
- "Can't open the file '%s'", file);
-
- uint32_t value = 0;
- StringRef text_buffer = stream.get()->getBuffer();
-
- if (type == BitOffset) {
- const char *prefix = "config:";
- if (!text_buffer.startswith(prefix))
- return createStringError(inconvertibleErrorCode(),
- "The file '%s' contents doesn't start with '%s'",
- file, prefix);
- text_buffer = text_buffer.substr(strlen(prefix));
- }
-
- auto getRadix = [&]() {
- switch (type) {
- case Hex:
- return 16;
- case ZeroOne:
- case Decimal:
- case BitOffset:
- return 10;
- }
- llvm_unreachable("Fully covered switch above!");
- };
-
- auto createError = [&](const char *expected_value_message) {
- return createStringError(
- inconvertibleErrorCode(),
- "The file '%s' has an invalid value. It should be %s.", file,
- expected_value_message);
- };
-
- if (text_buffer.trim().consumeInteger(getRadix(), value) ||
- (type == ZeroOne && value != 0 && value != 1)) {
- switch (type) {
- case Hex:
- return createError("an unsigned hexadecimal int");
- case ZeroOne:
- return createError("0 or 1");
- case Decimal:
- case BitOffset:
- return createError("an unsigned decimal int");
- }
- }
- return value;
-}
-
-/// Return the Linux perf event type for Intel PT.
-static Expected<uint32_t> GetOSEventType() {
- return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
- IntelPTConfigFileType::Decimal);
-}
-
-static Error CheckPsbPeriod(size_t psb_period) {
- Expected<uint32_t> cap =
- ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne);
- if (!cap)
- return cap.takeError();
- if (*cap == 0)
- return createStringError(inconvertibleErrorCode(),
- "psb_period is unsupported in the system.");
-
- Expected<uint32_t> valid_values = ReadIntelPTConfigFile(
- kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex);
- if (!valid_values)
- return valid_values.takeError();
-
- if (valid_values.get() & (1 << psb_period))
- return Error::success();
-
- std::ostringstream error;
- // 0 is always a valid value
- error << "Invalid psb_period. Valid values are: 0";
- uint32_t mask = valid_values.get();
- while (mask) {
- int index = __builtin_ctz(mask);
- if (index > 0)
- error << ", " << index;
- // clear the lowest bit
- mask &= mask - 1;
- }
- error << ".";
- return createStringError(inconvertibleErrorCode(), error.str().c_str());
-}
-
-size_t IntelPTThreadTrace::GetTraceBufferSize() const {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("Intel PT Linux perf event not supported");
-#else
- return m_perf_event.GetAuxBuffer().size();
-#endif
-}
-
-static Expected<uint64_t>
-GeneratePerfEventConfigValue(bool enable_tsc, Optional<size_t> psb_period) {
- uint64_t config = 0;
- // tsc is always supported
- if (enable_tsc) {
- if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
- kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))
- config |= 1 << *offset;
- else
- return offset.takeError();
- }
- if (psb_period) {
- if (Error error = CheckPsbPeriod(*psb_period))
- return std::move(error);
-
- if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
- kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset))
- config |= *psb_period << *offset;
- else
- return offset.takeError();
- }
- return config;
-}
-
-llvm::Expected<perf_event_attr>
-IntelPTThreadTrace::CreateIntelPTPerfEventConfiguration(
- bool enable_tsc, Optional<size_t> psb_period) {
- 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;
-
- if (Expected<uint64_t> config_value =
- GeneratePerfEventConfigValue(enable_tsc, psb_period)) {
- attr.config = *config_value;
- } else {
- return config_value.takeError();
- }
-
- if (Expected<uint32_t> intel_pt_type = GetOSEventType()) {
- attr.type = *intel_pt_type;
- } else {
- return intel_pt_type.takeError();
- }
-
- return attr;
-}
-
-llvm::Expected<IntelPTThreadTraceUP>
-IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size,
- bool enable_tsc, Optional<size_t> psb_period) {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("Intel PT Linux perf event not supported");
-#else
- Log *log = GetLog(POSIXLog::Ptrace);
-
- LLDB_LOG(log, "called thread id {0}", tid);
-
- if (__builtin_popcount(buffer_size) != 1 || buffer_size < 4096) {
- return createStringError(
- inconvertibleErrorCode(),
- "The trace buffer size must be a power of 2 greater than or equal to "
- "4096 (2^12) bytes. It was %" PRIu64 ".",
- buffer_size);
- }
- uint64_t page_size = getpagesize();
- uint64_t buffer_numpages = static_cast<uint64_t>(
- llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
-
- Expected<perf_event_attr> attr =
- IntelPTThreadTrace::CreateIntelPTPerfEventConfiguration(enable_tsc,
- psb_period);
- if (!attr)
- return attr.takeError();
-
- LLDB_LOG(log, "buffer size {0} ", buffer_size);
-
- if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
- if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
- buffer_numpages)) {
- return std::move(mmap_err);
- }
- return IntelPTThreadTraceUP(
- new IntelPTThreadTrace(std::move(*perf_event), tid));
- } else {
- return perf_event.takeError();
- }
-#endif
-}
-
-Expected<std::vector<uint8_t>>
-IntelPTThreadTrace::GetIntelPTBuffer(size_t offset, size_t size) const {
- std::vector<uint8_t> data(size, 0);
- MutableArrayRef<uint8_t> buffer_ref(data);
- Status error = ReadPerfTraceAux(buffer_ref, 0);
- if (error.Fail())
- return error.ToError();
- return data;
-}
-
-Status
-IntelPTThreadTrace::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) const {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- auto fd = m_perf_event.GetFd();
- perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
- // Disable the perf event to force a flush out of the CPU's internal buffer.
- // Besides, we can guarantee that the CPU won't override any data as we are
- // reading the buffer.
- //
- // The Intel documentation says:
- //
- // Packets are first buffered internally and then written out asynchronously.
- // To collect packet output for postprocessing, a collector needs first to
- // ensure that all packet data has been flushed from internal buffers.
- // Software can ensure this by stopping packet generation by clearing
- // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
- // Section 35.2.7.2).
- //
- // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
- // in the man page of perf_event_open.
- ioctl(fd, PERF_EVENT_IOC_DISABLE);
-
- Log *log = GetLog(POSIXLog::Ptrace);
- Status error;
- uint64_t head = mmap_metadata.aux_head;
-
- LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.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, m_perf_event.GetAuxBuffer(),
- static_cast<size_t>(head), offset);
- LLDB_LOG(log, "ReadCyclic Buffer Done");
-
- // Reenable tracing now we have read the buffer
- ioctl(fd, PERF_EVENT_IOC_ENABLE);
- return error;
-#endif
-}
-
-Status
-IntelPTThreadTrace::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) const {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- Log *log = GetLog(POSIXLog::Ptrace);
- uint64_t bytes_remaining = buffer.size();
- Status error;
-
- perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
- uint64_t head = mmap_metadata.data_head;
-
- /*
- * The data buffer and aux buffer have
diff erent 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 = m_perf_event.GetDataBuffer();
-
- if (head > data_buffer.size()) {
- head = head % data_buffer.size();
- LLDB_LOG(log, "Data size -{0} Head - {1}", mmap_metadata.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 IntelPTThreadTrace::ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
- llvm::ArrayRef<uint8_t> src,
- size_t src_cyc_index, size_t offset) {
-
- Log *log = GetLog(POSIXLog::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<ArrayRef<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);
-}
-
-TraceThreadState IntelPTThreadTrace::GetState() const {
- return {static_cast<int64_t>(m_tid),
- {TraceBinaryData{IntelPTDataKinds::kThreadTraceBuffer,
- static_cast<int64_t>(GetTraceBufferSize())}}};
-}
-
/// IntelPTThreadTraceCollection
bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const {
@@ -442,9 +54,8 @@ Error IntelPTThreadTraceCollection::TraceStart(
return createStringError(inconvertibleErrorCode(),
"Thread %" PRIu64 " already traced", tid);
- Expected<IntelPTThreadTraceUP> trace_up = IntelPTThreadTrace::Create(
- m_pid, tid, request.trace_buffer_size, request.enable_tsc,
- request.psb_period.map([](int64_t period) { return (size_t)period; }));
+ Expected<IntelPTSingleBufferTraceUP> trace_up =
+ IntelPTSingleBufferTrace::Start(request, tid);
if (!trace_up)
return trace_up.takeError();
@@ -461,11 +72,14 @@ std::vector<TraceThreadState>
IntelPTThreadTraceCollection::GetThreadStates() const {
std::vector<TraceThreadState> states;
for (const auto &it : m_thread_traces)
- states.push_back(it.second->GetState());
+ states.push_back({static_cast<int64_t>(it.first),
+ {TraceBinaryData{IntelPTDataKinds::kTraceBuffer,
+ static_cast<int64_t>(
+ it.second->GetTraceBufferSize())}}});
return states;
}
-Expected<const IntelPTThreadTrace &>
+Expected<const IntelPTSingleBufferTrace &>
IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) const {
auto it = m_thread_traces.find(tid);
if (it == m_thread_traces.end())
@@ -510,8 +124,7 @@ IntelPTProcessTrace::GetThreadTraces() const {
/// IntelPTCollector
-IntelPTCollector::IntelPTCollector(lldb::pid_t pid)
- : m_pid(pid), m_thread_traces(pid) {
+IntelPTCollector::IntelPTCollector() {
if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
LoadPerfTscConversionParameters())
m_tsc_conversion =
@@ -553,7 +166,7 @@ Error IntelPTCollector::TraceStart(
return createStringError(inconvertibleErrorCode(),
"Per-core tracing is not supported.");
}
- m_process_trace = IntelPTProcessTrace(m_pid, request);
+ m_process_trace = IntelPTProcessTrace(request);
Error error = Error::success();
for (lldb::tid_t tid : process_threads)
@@ -604,7 +217,7 @@ Expected<json::Value> IntelPTCollector::GetState() const {
return toJSON(state);
}
-Expected<const IntelPTThreadTrace &>
+Expected<const IntelPTSingleBufferTrace &>
IntelPTCollector::GetTracedThread(lldb::tid_t tid) const {
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
return m_process_trace->GetThreadTraces().GetTracedThread(tid);
@@ -613,10 +226,10 @@ IntelPTCollector::GetTracedThread(lldb::tid_t tid) const {
Expected<std::vector<uint8_t>>
IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const {
- if (request.kind == IntelPTDataKinds::kThreadTraceBuffer) {
- if (Expected<const IntelPTThreadTrace &> trace =
+ if (request.kind == IntelPTDataKinds::kTraceBuffer) {
+ if (Expected<const IntelPTSingleBufferTrace &> trace =
GetTracedThread(*request.tid))
- return trace->GetIntelPTBuffer(request.offset, request.size);
+ return trace->GetTraceBuffer(request.offset, request.size);
else
return trace.takeError();
} else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) {
@@ -630,12 +243,12 @@ IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const
void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; }
bool IntelPTCollector::IsSupported() {
- Expected<uint32_t> intel_pt_type = GetOSEventType();
- if (!intel_pt_type) {
+ if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
+ return true;
+ } else {
llvm::consumeError(intel_pt_type.takeError());
return false;
}
- return true;
}
bool IntelPTCollector::IsProcessTracingEnabled() const {
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.h b/lldb/source/Plugins/Process/Linux/IntelPTCollector.h
index aa7eba4cd937e..e78a7aa9d07b0 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.h
@@ -11,6 +11,8 @@
#include "Perf.h"
+#include "IntelPTSingleBufferTrace.h"
+
#include "lldb/Utility/Status.h"
#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
#include "lldb/lldb-types.h"
@@ -23,120 +25,10 @@ namespace lldb_private {
namespace process_linux {
-/// This class keeps track of one tracing instance of
-/// Intel(R) Processor Trace on Linux OS at thread level.
-///
-/// The kernel interface for us is the perf_event_open.
-class IntelPTThreadTrace;
-typedef std::unique_ptr<IntelPTThreadTrace> IntelPTThreadTraceUP;
-
-class IntelPTThreadTrace {
-public:
- /// Create a new \a IntelPTThreadTrace and start tracing the thread.
- ///
- /// \param[in] pid
- /// The pid of the process whose thread will be traced.
- ///
- /// \param[in] tid
- /// The tid of the thread to be traced.
- ///
- /// \param[in] buffer_size
- /// Size of the thread buffer in bytes.
- ///
- /// \param[in] enable_tsc
- /// Whether to use enable TSC timestamps or not.
- /// More information in TraceIntelPT::GetStartConfigurationHelp().
- ///
- /// \param[in] psb_period
- /// This value defines the period in which PSB packets will be generated.
- /// More information in TraceIntelPT::GetStartConfigurationHelp().
- ///
- /// \return
- /// A \a IntelPTThreadTrace instance if tracing was successful, or
- /// an \a llvm::Error otherwise.
- static llvm::Expected<IntelPTThreadTraceUP>
- Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size, bool enable_tsc,
- llvm::Optional<size_t> psb_period);
-
- /// Create a \a perf_event_attr configured for
- /// an IntelPT event.
- ///
- /// \return
- /// A \a perf_event_attr if successful,
- /// or an \a llvm::Error otherwise.
- static llvm::Expected<perf_event_attr>
- CreateIntelPTPerfEventConfiguration(bool enable_tsc,
- llvm::Optional<size_t> psb_period);
-
- /// Read the trace buffer of the currently traced thread.
- ///
- /// \param[in] offset
- /// Offset of the data to read.
- ///
- /// \param[in] size
- /// Number of bytes to read.
- ///
- /// \return
- /// A vector with the requested binary data. The vector will have the
- /// size of the requested \a size. Non-available positions will be
- /// filled with zeroes.
- llvm::Expected<std::vector<uint8_t>> GetIntelPTBuffer(size_t offset,
- size_t size) const;
-
- Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) const;
-
- Status ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) const;
-
- /// Get the size in bytes of the aux section of the thread or process traced
- /// by this object.
- size_t GetTraceBufferSize() const;
-
- /// Read data from a cyclic buffer
- ///
- /// \param[in] [out] buf
- /// Destination buffer, the buffer will be truncated to written size.
- ///
- /// \param[in] src
- /// Source buffer which must be a cyclic buffer.
- ///
- /// \param[in] src_cyc_index
- /// The index pointer (start of the valid data in the cyclic
- /// buffer).
- ///
- /// \param[in] offset
- /// The offset to begin reading the data in the cyclic buffer.
- static void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
- llvm::ArrayRef<uint8_t> src,
- size_t src_cyc_index, size_t offset);
-
- /// Return the thread-specific part of the jLLDBTraceGetState packet.
- TraceThreadState GetState() const;
-
-private:
- /// Construct new \a IntelPTThreadTrace. Users are supposed to create
- /// instances of this class via the \a Create() method and not invoke this one
- /// directly.
- ///
- /// \param[in] perf_event
- /// perf event configured for IntelPT.
- ///
- /// \param[in] tid
- /// The thread being traced.
- IntelPTThreadTrace(PerfEvent &&perf_event, lldb::tid_t tid)
- : m_perf_event(std::move(perf_event)), m_tid(tid) {}
-
- /// perf event configured for IntelPT.
- PerfEvent m_perf_event;
- /// The thread being traced.
- lldb::tid_t m_tid;
-};
-
/// Manages a list of thread traces.
class IntelPTThreadTraceCollection {
public:
- IntelPTThreadTraceCollection(lldb::pid_t pid) : m_pid(pid) {}
+ IntelPTThreadTraceCollection() {}
/// Dispose of all traces
void Clear();
@@ -147,7 +39,7 @@ class IntelPTThreadTraceCollection {
std::vector<TraceThreadState> GetThreadStates() const;
- llvm::Expected<const IntelPTThreadTrace &>
+ llvm::Expected<const IntelPTSingleBufferTrace &>
GetTracedThread(lldb::tid_t tid) const;
llvm::Error TraceStart(lldb::tid_t tid,
@@ -156,8 +48,7 @@ class IntelPTThreadTraceCollection {
llvm::Error TraceStop(lldb::tid_t tid);
private:
- lldb::pid_t m_pid;
- llvm::DenseMap<lldb::tid_t, IntelPTThreadTraceUP> m_thread_traces;
+ llvm::DenseMap<lldb::tid_t, IntelPTSingleBufferTraceUP> m_thread_traces;
/// Total actual thread buffer size in bytes
size_t m_total_buffer_size = 0;
};
@@ -165,8 +56,8 @@ class IntelPTThreadTraceCollection {
/// Manages a "process trace" instance.
class IntelPTProcessTrace {
public:
- IntelPTProcessTrace(lldb::pid_t pid, const TraceIntelPTStartRequest &request)
- : m_thread_traces(pid), m_tracing_params(request) {}
+ IntelPTProcessTrace(const TraceIntelPTStartRequest &request)
+ : m_tracing_params(request) {}
bool TracesThread(lldb::tid_t tid) const;
@@ -185,7 +76,7 @@ class IntelPTProcessTrace {
/// Main class that manages intel-pt process and thread tracing.
class IntelPTCollector {
public:
- IntelPTCollector(lldb::pid_t pid);
+ IntelPTCollector();
static bool IsSupported();
@@ -222,14 +113,13 @@ class IntelPTCollector {
llvm::Error TraceStart(lldb::tid_t tid,
const TraceIntelPTStartRequest &request);
- llvm::Expected<const IntelPTThreadTrace &>
+ llvm::Expected<const IntelPTSingleBufferTrace &>
GetTracedThread(lldb::tid_t tid) const;
bool IsProcessTracingEnabled() const;
void ClearProcessTracing();
- lldb::pid_t m_pid;
/// Threads traced due to "thread tracing"
IntelPTThreadTraceCollection m_thread_traces;
/// Threads traced due to "process tracing". Only one active "process tracing"
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
new file mode 100644
index 0000000000000..47d587741dbca
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
@@ -0,0 +1,302 @@
+//===-- IntelPTSingleBufferTrace.cpp --------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "IntelPTSingleBufferTrace.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include <sstream>
+
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace process_linux;
+using namespace llvm;
+
+const char *kOSEventIntelPTTypeFile =
+ "/sys/bus/event_source/devices/intel_pt/type";
+
+const char *kPSBPeriodCapFile =
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";
+
+const char *kPSBPeriodValidValuesFile =
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";
+
+const char *kPSBPeriodBitOffsetFile =
+ "/sys/bus/event_source/devices/intel_pt/format/psb_period";
+
+const char *kTSCBitOffsetFile =
+ "/sys/bus/event_source/devices/intel_pt/format/tsc";
+
+enum IntelPTConfigFileType {
+ Hex = 0,
+ // 0 or 1
+ ZeroOne,
+ Decimal,
+ // a bit index file always starts with the prefix config: following by an int,
+ // which represents the offset of the perf_event_attr.config value where to
+ // store a given configuration.
+ BitOffset
+};
+
+static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
+ IntelPTConfigFileType type) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
+ MemoryBuffer::getFileAsStream(file);
+
+ if (!stream)
+ return createStringError(inconvertibleErrorCode(),
+ "Can't open the file '%s'", file);
+
+ uint32_t value = 0;
+ StringRef text_buffer = stream.get()->getBuffer();
+
+ if (type == BitOffset) {
+ const char *prefix = "config:";
+ if (!text_buffer.startswith(prefix))
+ return createStringError(inconvertibleErrorCode(),
+ "The file '%s' contents doesn't start with '%s'",
+ file, prefix);
+ text_buffer = text_buffer.substr(strlen(prefix));
+ }
+
+ auto getRadix = [&]() {
+ switch (type) {
+ case Hex:
+ return 16;
+ case ZeroOne:
+ case Decimal:
+ case BitOffset:
+ return 10;
+ }
+ llvm_unreachable("Fully covered switch above!");
+ };
+
+ auto createError = [&](const char *expected_value_message) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "The file '%s' has an invalid value. It should be %s.", file,
+ expected_value_message);
+ };
+
+ if (text_buffer.trim().consumeInteger(getRadix(), value) ||
+ (type == ZeroOne && value != 0 && value != 1)) {
+ switch (type) {
+ case Hex:
+ return createError("an unsigned hexadecimal int");
+ case ZeroOne:
+ return createError("0 or 1");
+ case Decimal:
+ case BitOffset:
+ return createError("an unsigned decimal int");
+ }
+ }
+ return value;
+}
+
+/// Return the Linux perf event type for Intel PT.
+Expected<uint32_t> process_linux::GetIntelPTOSEventType() {
+ return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
+ IntelPTConfigFileType::Decimal);
+}
+
+static Error CheckPsbPeriod(size_t psb_period) {
+ Expected<uint32_t> cap =
+ ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne);
+ if (!cap)
+ return cap.takeError();
+ if (*cap == 0)
+ return createStringError(inconvertibleErrorCode(),
+ "psb_period is unsupported in the system.");
+
+ Expected<uint32_t> valid_values = ReadIntelPTConfigFile(
+ kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex);
+ if (!valid_values)
+ return valid_values.takeError();
+
+ if (valid_values.get() & (1 << psb_period))
+ return Error::success();
+
+ std::ostringstream error;
+ // 0 is always a valid value
+ error << "Invalid psb_period. Valid values are: 0";
+ uint32_t mask = valid_values.get();
+ while (mask) {
+ int index = __builtin_ctz(mask);
+ if (index > 0)
+ error << ", " << index;
+ // clear the lowest bit
+ mask &= mask - 1;
+ }
+ error << ".";
+ return createStringError(inconvertibleErrorCode(), error.str().c_str());
+}
+
+static Expected<uint64_t>
+GeneratePerfEventConfigValue(bool enable_tsc, Optional<size_t> psb_period) {
+ uint64_t config = 0;
+ // tsc is always supported
+ if (enable_tsc) {
+ if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
+ kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))
+ config |= 1 << *offset;
+ else
+ return offset.takeError();
+ }
+ if (psb_period) {
+ if (Error error = CheckPsbPeriod(*psb_period))
+ return std::move(error);
+
+ if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
+ kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset))
+ config |= *psb_period << *offset;
+ else
+ return offset.takeError();
+ }
+ return config;
+}
+
+/// Create a \a perf_event_attr configured for
+/// an IntelPT event.
+///
+/// \return
+/// A \a perf_event_attr if successful,
+/// or an \a llvm::Error otherwise.
+static Expected<perf_event_attr>
+CreateIntelPTPerfEventConfiguration(bool enable_tsc,
+ llvm::Optional<size_t> psb_period) {
+#ifndef PERF_ATTR_SIZE_VER5
+ return llvm_unreachable("Intel PT Linux perf event not supported");
+#else
+ 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;
+
+ if (Expected<uint64_t> config_value =
+ GeneratePerfEventConfigValue(enable_tsc, psb_period))
+ attr.config = *config_value;
+ else
+ return config_value.takeError();
+
+ if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType())
+ attr.type = *intel_pt_type;
+ else
+ return intel_pt_type.takeError();
+
+ return attr;
+#endif
+}
+
+size_t IntelPTSingleBufferTrace::GetTraceBufferSize() const {
+ return m_perf_event.GetAuxBuffer().size();
+}
+
+Expected<std::vector<uint8_t>>
+IntelPTSingleBufferTrace::GetTraceBuffer(size_t offset, size_t size) const {
+ auto fd = m_perf_event.GetFd();
+ perf_event_mmap_page &mmap_metadata = m_perf_event.GetMetadataPage();
+ // Disable the perf event to force a flush out of the CPU's internal buffer.
+ // Besides, we can guarantee that the CPU won't override any data as we are
+ // reading the buffer.
+ //
+ // The Intel documentation says:
+ //
+ // Packets are first buffered internally and then written out asynchronously.
+ // To collect packet output for postprocessing, a collector needs first to
+ // ensure that all packet data has been flushed from internal buffers.
+ // Software can ensure this by stopping packet generation by clearing
+ // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
+ // Section 35.2.7.2).
+ //
+ // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
+ // in the man page of perf_event_open.
+ ioctl(fd, PERF_EVENT_IOC_DISABLE);
+
+ Log *log = GetLog(POSIXLog::Trace);
+ Status error;
+ uint64_t head = mmap_metadata.aux_head;
+
+ LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.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 ->|
+ *
+ * */
+
+ std::vector<uint8_t> data(size, 0);
+ MutableArrayRef<uint8_t> buffer(data);
+ ReadCyclicBuffer(buffer, m_perf_event.GetAuxBuffer(),
+ static_cast<size_t>(head), offset);
+
+ // Reenable tracing now we have read the buffer
+ ioctl(fd, PERF_EVENT_IOC_ENABLE);
+ return data;
+}
+
+Expected<IntelPTSingleBufferTraceUP>
+IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
+ lldb::tid_t tid) {
+ Log *log = GetLog(POSIXLog::Trace);
+
+ LLDB_LOG(log, "Will start tracing thread id {0}", tid);
+
+ if (__builtin_popcount(request.trace_buffer_size) != 1 ||
+ request.trace_buffer_size < 4096) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "The trace buffer size must be a power of 2 greater than or equal to "
+ "4096 (2^12) bytes. It was %" PRIu64 ".",
+ request.trace_buffer_size);
+ }
+ uint64_t page_size = getpagesize();
+ uint64_t buffer_numpages = static_cast<uint64_t>(llvm::PowerOf2Floor(
+ (request.trace_buffer_size + page_size - 1) / page_size));
+
+ Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration(
+ request.enable_tsc, request.psb_period.map([](int value) {
+ return static_cast<uint64_t>(value);
+ }));
+ if (!attr)
+ return attr.takeError();
+
+ LLDB_LOG(log, "Will create trace buffer of size {0}",
+ request.trace_buffer_size);
+
+ if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
+ if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
+ buffer_numpages)) {
+ return std::move(mmap_err);
+ }
+ return IntelPTSingleBufferTraceUP(
+ new IntelPTSingleBufferTrace(std::move(*perf_event), tid));
+ } else {
+ return perf_event.takeError();
+ }
+}
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h
new file mode 100644
index 0000000000000..ada8c3b72dea6
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h
@@ -0,0 +1,96 @@
+//===-- IntelPTSingleBufferTrace.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 liblldb_IntelPTSingleBufferTrace_H_
+#define liblldb_IntelPTSingleBufferTrace_H_
+
+#include "Perf.h"
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "lldb/lldb-types.h"
+
+#include "llvm/Support/Error.h"
+
+#include <memory>
+
+namespace lldb_private {
+namespace process_linux {
+
+llvm::Expected<uint32_t> GetIntelPTOSEventType();
+
+class IntelPTTrace;
+class IntelPTSingleBufferTrace;
+
+using IntelPTThreadTraceUP = std::unique_ptr<IntelPTTrace>;
+using IntelPTSingleBufferTraceUP = std::unique_ptr<IntelPTSingleBufferTrace>;
+
+/// This class wraps a single perf event collecting intel pt data in a single
+/// buffer.
+class IntelPTSingleBufferTrace {
+public:
+ /// Start tracing using a single Intel PT trace buffer.
+ ///
+ /// \param[in] request
+ /// Intel PT configuration parameters.
+ ///
+ /// \param[in] tid
+ /// The tid of the thread to be traced.
+ ///
+ /// \return
+ /// A \a IntelPTSingleBufferTrace instance if tracing was successful, or
+ /// an \a llvm::Error otherwise.
+ static llvm::Expected<IntelPTSingleBufferTraceUP>
+ Start(const TraceIntelPTStartRequest &request, lldb::tid_t tid);
+
+ /// \return
+ /// The bytes requested by a jLLDBTraceGetBinaryData packet that was routed
+ /// to this trace instace.
+ llvm::Expected<std::vector<uint8_t>>
+ GetBinaryData(const TraceGetBinaryDataRequest &request) const;
+
+ /// Read the trace buffer managed by this trace instance.
+ ///
+ /// \param[in] offset
+ /// Offset of the data to read.
+ ///
+ /// \param[in] size
+ /// Number of bytes to read.
+ ///
+ /// \return
+ /// A vector with the requested binary data. The vector will have the
+ /// size of the requested \a size. Non-available positions will be
+ /// filled with zeroes.
+ llvm::Expected<std::vector<uint8_t>> GetTraceBuffer(size_t offset,
+ size_t size) const;
+
+ /// \return
+ /// The total the size in bytes used by the trace buffer managed by this
+ /// trace instance.
+ size_t GetTraceBufferSize() const;
+
+private:
+ /// Construct new \a IntelPTSingleBufferThreadTrace. Users are supposed to
+ /// create instances of this class via the \a Start() method and not invoke
+ /// this one directly.
+ ///
+ /// \param[in] perf_event
+ /// perf event configured for IntelPT.
+ ///
+ /// \param[in] tid
+ /// The thread being traced.
+ IntelPTSingleBufferTrace(PerfEvent &&perf_event, lldb::tid_t tid)
+ : m_perf_event(std::move(perf_event)) {}
+
+ /// perf event configured for IntelPT.
+ PerfEvent m_perf_event;
+};
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // liblldb_IntelPTSingleBufferTrace_H_
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index dd91af54f4541..78d1b3a8e835f 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -312,7 +312,7 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd,
const ArchSpec &arch, MainLoop &mainloop,
llvm::ArrayRef<::pid_t> tids)
: NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
- m_main_loop(mainloop), m_intel_pt_collector(pid) {
+ m_main_loop(mainloop) {
if (m_terminal_fd != -1) {
Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
assert(status.Success());
diff --git a/lldb/source/Plugins/Process/Linux/Perf.cpp b/lldb/source/Plugins/Process/Linux/Perf.cpp
index d43cd121eb126..d1691a9d7d228 100644
--- a/lldb/source/Plugins/Process/Linux/Perf.cpp
+++ b/lldb/source/Plugins/Process/Linux/Perf.cpp
@@ -8,6 +8,7 @@
#include "Perf.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "lldb/Host/linux/Support.h"
#include "llvm/Support/FormatVariadic.h"
@@ -22,6 +23,54 @@ using namespace lldb_private;
using namespace process_linux;
using namespace llvm;
+void lldb_private::process_linux::ReadCyclicBuffer(
+ llvm::MutableArrayRef<uint8_t> &dst, llvm::ArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset) {
+
+ Log *log = GetLog(POSIXLog::Trace);
+
+ 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<ArrayRef<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);
+}
+
Expected<LinuxPerfZeroTscConversion>
lldb_private::process_linux::LoadPerfTscConversionParameters() {
lldb::pid_t pid = getpid();
diff --git a/lldb/source/Plugins/Process/Linux/Perf.h b/lldb/source/Plugins/Process/Linux/Perf.h
index d5cb1f3b7d886..d8fab93995c6e 100644
--- a/lldb/source/Plugins/Process/Linux/Perf.h
+++ b/lldb/source/Plugins/Process/Linux/Perf.h
@@ -74,6 +74,24 @@ using MmapUP = std::unique_ptr<void, resource_handle::MmapDeleter>;
} // namespace resource_handle
+/// Read data from a cyclic buffer
+///
+/// \param[in] [out] buf
+/// Destination buffer, the buffer will be truncated to written size.
+///
+/// \param[in] src
+/// Source buffer which must be a cyclic buffer.
+///
+/// \param[in] src_cyc_index
+/// The index pointer (start of the valid data in the cyclic
+/// buffer).
+///
+/// \param[in] offset
+/// The offset to begin reading the data in the cyclic buffer.
+void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
+ llvm::ArrayRef<uint8_t> src, size_t src_cyc_index,
+ size_t offset);
+
/// Thin wrapper of the perf_event_open API.
///
/// Exposes the metadata page and data and aux buffers of a perf event.
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index 9bd1e3c605a17..c9e77dde2282d 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -81,8 +81,7 @@ TraceIntelPT::TraceIntelPT(
for (const ThreadPostMortemTraceSP &thread : traced_threads) {
m_thread_decoders.emplace(thread->GetID(),
std::make_unique<ThreadDecoder>(thread, *this));
- SetPostMortemThreadDataFile(thread->GetID(),
- IntelPTDataKinds::kThreadTraceBuffer,
+ SetPostMortemThreadDataFile(thread->GetID(), IntelPTDataKinds::kTraceBuffer,
thread->GetTraceFile());
}
}
@@ -373,8 +372,7 @@ Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
OnBinaryDataReadCallback callback) {
- return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kThreadTraceBuffer,
- callback);
+ return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kTraceBuffer, callback);
}
TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; }
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
index 7ed4292280843..9d6d7e4f68ee8 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
@@ -49,7 +49,7 @@ llvm::Error TraceIntelPTSessionSaver::SaveToDisk(TraceIntelPT &trace_ipt,
llvm::Expected<JSONTraceSessionBase> json_session_description =
TraceSessionSaver::BuildProcessesSection(
- *live_process, IntelPTDataKinds::kThreadTraceBuffer, directory);
+ *live_process, IntelPTDataKinds::kTraceBuffer, directory);
if (!json_session_description)
return json_session_description.takeError();
diff --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
index 746e034e88de7..afc7cdf85509c 100644
--- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -14,7 +14,7 @@ using namespace llvm::json;
namespace lldb_private {
const char *IntelPTDataKinds::kProcFsCpuInfo = "procfsCpuInfo";
-const char *IntelPTDataKinds::kThreadTraceBuffer = "threadTraceBuffer";
+const char *IntelPTDataKinds::kTraceBuffer = "traceBuffer";
bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet,
Path path) {
diff --git a/lldb/unittests/Process/Linux/CMakeLists.txt b/lldb/unittests/Process/Linux/CMakeLists.txt
index d7ad90e66f611..9d91dd58cd705 100644
--- a/lldb/unittests/Process/Linux/CMakeLists.txt
+++ b/lldb/unittests/Process/Linux/CMakeLists.txt
@@ -1,5 +1,4 @@
add_lldb_unittest(ProcessLinuxTests
- IntelPTCollectorTests.cpp
PerfTests.cpp
ProcfsTests.cpp
diff --git a/lldb/unittests/Process/Linux/IntelPTCollectorTests.cpp b/lldb/unittests/Process/Linux/IntelPTCollectorTests.cpp
deleted file mode 100644
index 2754b958a9d49..0000000000000
--- a/lldb/unittests/Process/Linux/IntelPTCollectorTests.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-//===-- IntelPTCollectorTests.cpp -------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "gtest/gtest.h"
-
-#include "IntelPTCollector.h"
-#include "llvm/ADT/ArrayRef.h"
-
-
-using namespace lldb_private;
-using namespace process_linux;
-
-size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf,
- size_t cyc_buf_size, size_t cyc_start,
- size_t offset) {
- llvm::MutableArrayRef<uint8_t> dst(reinterpret_cast<uint8_t *>(buf),
- buf_size);
- llvm::ArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf),
- cyc_buf_size);
- IntelPTThreadTrace::ReadCyclicBuffer(dst, src, cyc_start, offset);
- return dst.size();
-}
-
-TEST(CyclicBuffer, EdgeCases) {
- size_t bytes_read;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char smaller_buffer[4] = {};
-
- // empty buffer to read into
- bytes_read = ReadCylicBufferWrapper(smaller_buffer, 0, cyclic_buffer,
- sizeof(cyclic_buffer), 3, 0);
- ASSERT_EQ(0u, bytes_read);
-
- // empty cyclic buffer
- bytes_read = ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, 0, 3, 0);
- ASSERT_EQ(0u, bytes_read);
-
- // bigger offset
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 6);
- ASSERT_EQ(0u, bytes_read);
-
- // wrong offset
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
- ASSERT_EQ(0u, bytes_read);
-
- // wrong start
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
- ASSERT_EQ(0u, bytes_read);
-}
-
-TEST(CyclicBuffer, EqualSizeBuffer) {
- size_t bytes_read = 0;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- char cyclic[] = "cyclic";
- for (size_t i = 0; i < sizeof(cyclic); i++) {
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char equal_size_buffer[7] = {};
- bytes_read =
- ReadCylicBufferWrapper(equal_size_buffer, sizeof(cyclic_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, i);
- ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
- ASSERT_STREQ(equal_size_buffer, (cyclic + i));
- }
-}
-
-TEST(CyclicBuffer, SmallerSizeBuffer) {
- size_t bytes_read;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char smaller_buffer[4] = {};
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 0);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "cyc");
-
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 1);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "ycl");
-
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 2);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "cli");
-
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 3);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "lic");
-
- {
- char smaller_buffer[4] = {};
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 4);
- ASSERT_EQ(2u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "ic");
- }
- {
- char smaller_buffer[4] = {};
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 5);
- ASSERT_EQ(1u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "c");
- }
-}
-
-TEST(CyclicBuffer, BiggerSizeBuffer) {
- size_t bytes_read = 0;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- char cyclic[] = "cyclic";
- for (size_t i = 0; i < sizeof(cyclic); i++) {
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char bigger_buffer[10] = {};
- bytes_read =
- ReadCylicBufferWrapper(bigger_buffer, (sizeof(bigger_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, i);
- ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
- ASSERT_STREQ(bigger_buffer, (cyclic + i));
- }
-}
diff --git a/lldb/unittests/Process/Linux/PerfTests.cpp b/lldb/unittests/Process/Linux/PerfTests.cpp
index fd9280b30e917..b5b66256b8eba 100644
--- a/lldb/unittests/Process/Linux/PerfTests.cpp
+++ b/lldb/unittests/Process/Linux/PerfTests.cpp
@@ -86,4 +86,135 @@ TEST(Perf, TscConversion) {
(SLEEP_NANOS + acceptable_overhead).count());
}
+size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf,
+ size_t cyc_buf_size, size_t cyc_start,
+ size_t offset) {
+ llvm::MutableArrayRef<uint8_t> dst(reinterpret_cast<uint8_t *>(buf),
+ buf_size);
+ llvm::ArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf),
+ cyc_buf_size);
+ ReadCyclicBuffer(dst, src, cyc_start, offset);
+ return dst.size();
+}
+
+TEST(CyclicBuffer, EdgeCases) {
+ size_t bytes_read;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ // We will always leave the last bytes untouched
+ // so that string comparisons work.
+ char smaller_buffer[4] = {};
+
+ // empty buffer to read into
+ bytes_read = ReadCylicBufferWrapper(smaller_buffer, 0, cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 0);
+ ASSERT_EQ(0u, bytes_read);
+
+ // empty cyclic buffer
+ bytes_read = ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
+ cyclic_buffer, 0, 3, 0);
+ ASSERT_EQ(0u, bytes_read);
+
+ // bigger offset
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 6);
+ ASSERT_EQ(0u, bytes_read);
+
+ // wrong offset
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
+ ASSERT_EQ(0u, bytes_read);
+
+ // wrong start
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
+ ASSERT_EQ(0u, bytes_read);
+}
+
+TEST(CyclicBuffer, EqualSizeBuffer) {
+ size_t bytes_read = 0;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ char cyclic[] = "cyclic";
+ for (size_t i = 0; i < sizeof(cyclic); i++) {
+ // We will always leave the last bytes untouched
+ // so that string comparisons work.
+ char equal_size_buffer[7] = {};
+ bytes_read =
+ ReadCylicBufferWrapper(equal_size_buffer, sizeof(cyclic_buffer),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, i);
+ ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
+ ASSERT_STREQ(equal_size_buffer, (cyclic + i));
+ }
+}
+
+TEST(CyclicBuffer, SmallerSizeBuffer) {
+ size_t bytes_read;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ // We will always leave the last bytes untouched
+ // so that string comparisons work.
+ char smaller_buffer[4] = {};
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 0);
+ ASSERT_EQ(3u, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "cyc");
+
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 1);
+ ASSERT_EQ(3u, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "ycl");
+
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 2);
+ ASSERT_EQ(3u, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "cli");
+
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 3);
+ ASSERT_EQ(3u, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "lic");
+
+ {
+ char smaller_buffer[4] = {};
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 4);
+ ASSERT_EQ(2u, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "ic");
+ }
+ {
+ char smaller_buffer[4] = {};
+ bytes_read =
+ ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, 5);
+ ASSERT_EQ(1u, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "c");
+ }
+}
+
+TEST(CyclicBuffer, BiggerSizeBuffer) {
+ size_t bytes_read = 0;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ char cyclic[] = "cyclic";
+ for (size_t i = 0; i < sizeof(cyclic); i++) {
+ // We will always leave the last bytes untouched
+ // so that string comparisons work.
+ char bigger_buffer[10] = {};
+ bytes_read =
+ ReadCylicBufferWrapper(bigger_buffer, (sizeof(bigger_buffer) - 1),
+ cyclic_buffer, sizeof(cyclic_buffer), 3, i);
+ ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
+ ASSERT_STREQ(bigger_buffer, (cyclic + i));
+ }
+}
+
#endif // __x86_64__
More information about the lldb-commits
mailing list