This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG7b73de9ec2b1: [trace][intelpt] Support system-wide tracing
[3] - Refactor IntelPTThreadTrace (authored by Walter Erquinigo
<[email protected]>).
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D124648/new/
https://reviews.llvm.org/D124648
Files:
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/IntelPTSingleBufferTrace.cpp
lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.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/IntelPTCollectorTests.cpp
lldb/unittests/Process/Linux/PerfTests.cpp
Index: lldb/unittests/Process/Linux/PerfTests.cpp
===================================================================
--- lldb/unittests/Process/Linux/PerfTests.cpp
+++ lldb/unittests/Process/Linux/PerfTests.cpp
@@ -86,4 +86,135 @@
(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__
Index: lldb/unittests/Process/Linux/IntelPTCollectorTests.cpp
===================================================================
--- 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));
- }
-}
Index: lldb/unittests/Process/Linux/CMakeLists.txt
===================================================================
--- lldb/unittests/Process/Linux/CMakeLists.txt
+++ lldb/unittests/Process/Linux/CMakeLists.txt
@@ -1,5 +1,4 @@
add_lldb_unittest(ProcessLinuxTests
- IntelPTCollectorTests.cpp
PerfTests.cpp
ProcfsTests.cpp
Index: lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
===================================================================
--- lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -14,7 +14,7 @@
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) {
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
@@ -49,7 +49,7 @@
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();
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -81,8 +81,7 @@
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::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; }
Index: lldb/source/Plugins/Process/Linux/Perf.h
===================================================================
--- lldb/source/Plugins/Process/Linux/Perf.h
+++ lldb/source/Plugins/Process/Linux/Perf.h
@@ -74,6 +74,24 @@
} // 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.
Index: lldb/source/Plugins/Process/Linux/Perf.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/Perf.cpp
+++ 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 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();
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -312,7 +312,7 @@
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());
Index: lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h
===================================================================
--- /dev/null
+++ 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_
Index: lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
===================================================================
--- /dev/null
+++ 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();
+ }
+}
Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.h
===================================================================
--- lldb/source/Plugins/Process/Linux/IntelPTCollector.h
+++ 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 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 @@
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 @@
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 @@
/// 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 @@
/// 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 @@
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"
Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
+++ lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
@@ -32,394 +32,6 @@
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 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 = 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 @@
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 @@
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 @@
/// 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 @@
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 @@
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 @@
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 @@
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 {
Index: lldb/source/Plugins/Process/Linux/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Process/Linux/CMakeLists.txt
+++ 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
Index: lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
===================================================================
--- lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
+++ lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
@@ -21,7 +21,7 @@
// 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
Index: lldb/docs/lldb-gdb-remote.txt
===================================================================
--- lldb/docs/lldb-gdb-remote.txt
+++ lldb/docs/lldb-gdb-remote.txt
@@ -495,7 +495,7 @@
// 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 @@
// 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.
//----------------------------------------------------------------------
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits