ravitheja updated this revision to Diff 102540.
ravitheja marked 29 inline comments as done.
ravitheja added a comment.
Changes suggested in previous round of feedback.
https://reviews.llvm.org/D33674
Files:
include/lldb/Host/common/NativeProcessProtocol.h
include/lldb/Host/linux/Support.h
source/Host/linux/Support.cpp
source/Plugins/Process/Linux/CMakeLists.txt
source/Plugins/Process/Linux/NativeProcessLinux.cpp
source/Plugins/Process/Linux/NativeProcessLinux.h
source/Plugins/Process/Linux/ProcessorTrace.cpp
source/Plugins/Process/Linux/ProcessorTrace.h
source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
unittests/CMakeLists.txt
unittests/Linux/
unittests/Linux/CMakeLists.txt
unittests/Linux/ProcessorTraceTest.cpp
unittests/Process/CMakeLists.txt
unittests/Process/Linux/
unittests/Process/Linux/CMakeLists.txt
unittests/Process/Linux/ProcessorTraceTest.cpp
Index: unittests/Process/Linux/ProcessorTraceTest.cpp
===================================================================
--- /dev/null
+++ unittests/Process/Linux/ProcessorTraceTest.cpp
@@ -0,0 +1,160 @@
+//===-- ProcessorTraceMonitorTest.cpp -------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "ProcessorTrace.h"
+// C Includes
+
+// C++ Includes
+
+using namespace lldb_private;
+
+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::MutableArrayRef<uint8_t> src(reinterpret_cast<uint8_t *> (cyc_buf), cyc_buf_size);
+ ProcessorTraceMonitor::ReadCyclicBuffer(dst, src, cyc_start, offset);
+ return dst.size();
+}
+
+TEST(CyclicBuffer, EdgeCases) {
+ size_t bytes_read = 0;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ // We will always leave the last bytes untouched
+ // so that string comparisions work.
+ char bigger_buffer[10] = {};
+ char equal_size_buffer[7] = {};
+ char smaller_buffer[4] = {};
+
+ // nullptr in buffer
+ bytes_read = ReadCylicBufferWrapper(
+ nullptr, sizeof(smaller_buffer), cyclic_buffer, sizeof(cyclic_buffer), 3,
+ 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // nullptr in cyclic buffer
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, sizeof(smaller_buffer), nullptr, sizeof(cyclic_buffer), 3,
+ 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // empty buffer to read into
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, 0, cyclic_buffer, sizeof(cyclic_buffer), 3, 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // empty cyclic buffer
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer, 0, 3, 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // bigger offset
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 6);
+ ASSERT_EQ(0, bytes_read);
+
+ // wrong offset
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 7);
+ ASSERT_EQ(0, bytes_read);
+
+ // wrong start
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 7);
+ ASSERT_EQ(0, 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 (int i = 0; i < sizeof(cyclic); i++) {
+ // We will always leave the last bytes untouched
+ // so that string comparisions 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 = 0;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ // We will always leave the last bytes untouched
+ // so that string comparisions work.
+ char smaller_buffer[4] = {};
+ bytes_read = ReadCylicBufferWrapper(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 0);
+ ASSERT_EQ(3, 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(3, 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(3, 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(3, 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(2, 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(1, 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 (int i = 0; i < sizeof(cyclic); i++) {
+ // We will always leave the last bytes untouched
+ // so that string comparisions 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: unittests/Process/Linux/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/Process/Linux/CMakeLists.txt
@@ -0,0 +1,8 @@
+include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Process/Linux)
+
+add_lldb_unittest(ProcessorTraceTest
+ ProcessorTraceTest.cpp
+
+ LINK_LIBS
+ lldbPluginProcessLinux
+ )
\ No newline at end of file
Index: unittests/Process/CMakeLists.txt
===================================================================
--- unittests/Process/CMakeLists.txt
+++ unittests/Process/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(gdb-remote)
+add_subdirectory(Linux)
add_subdirectory(minidump)
Index: unittests/Linux/ProcessorTraceTest.cpp
===================================================================
--- /dev/null
+++ unittests/Linux/ProcessorTraceTest.cpp
@@ -0,0 +1,151 @@
+//===-- ProcessorTraceTest.cpp -------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "NativeProcessLinux.h"
+// C Includes
+
+// C++ Includes
+
+using namespace lldb_private;
+using namespace process_linux;
+
+TEST(CyclicBuffer, EdgeCases) {
+ size_t bytes_read = 0;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ // We will always leave the last bytes untouched
+ // so that string comparisions work.
+ char bigger_buffer[10] = {};
+ char equal_size_buffer[7] = {};
+ char smaller_buffer[4] = {};
+
+ // nullptr in buffer
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ nullptr, sizeof(smaller_buffer), cyclic_buffer, sizeof(cyclic_buffer), 3,
+ 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // nullptr in cyclic buffer
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, sizeof(smaller_buffer), nullptr, sizeof(cyclic_buffer), 3,
+ 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // empty buffer to read into
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, 0, cyclic_buffer, sizeof(cyclic_buffer), 3, 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // empty cyclic buffer
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer, 0, 3, 0);
+ ASSERT_EQ(0, bytes_read);
+
+ // bigger offset
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 6);
+ ASSERT_EQ(0, bytes_read);
+
+ // wrong offset
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 7);
+ ASSERT_EQ(0, bytes_read);
+
+ // wrong start
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, sizeof(smaller_buffer), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 7);
+ ASSERT_EQ(0, 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 (int i = 0; i < sizeof(cyclic); i++) {
+ // We will always leave the last bytes untouched
+ // so that string comparisions work.
+ char equal_size_buffer[7] = {};
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ 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 = 0;
+ uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
+
+ // We will always leave the last bytes untouched
+ // so that string comparisions work.
+ char smaller_buffer[4] = {};
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 0);
+ ASSERT_EQ(3, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "cyc");
+
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 1);
+ ASSERT_EQ(3, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "ycl");
+
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 2);
+ ASSERT_EQ(3, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "cli");
+
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 3);
+ ASSERT_EQ(3, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "lic");
+ {
+ char smaller_buffer[4] = {};
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 4);
+ ASSERT_EQ(2, bytes_read);
+ ASSERT_STREQ(smaller_buffer, "ic");
+ }
+ {
+ char smaller_buffer[4] = {};
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ smaller_buffer, (sizeof(smaller_buffer) - 1), cyclic_buffer,
+ sizeof(cyclic_buffer), 3, 5);
+ ASSERT_EQ(1, 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 (int i = 0; i < sizeof(cyclic); i++) {
+ // We will always leave the last bytes untouched
+ // so that string comparisions work.
+ char bigger_buffer[10] = {};
+ bytes_read = NativeProcessLinux::ReadCyclicBuffer(
+ 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: unittests/Linux/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/Linux/CMakeLists.txt
@@ -0,0 +1,8 @@
+include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Process/Linux)
+
+add_lldb_unittest(ProcessorTraceTest
+ ProcessorTraceTest.cpp
+
+ LINK_LIBS
+ lldbPluginProcessLinux
+ )
\ No newline at end of file
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -73,4 +73,4 @@
if(LLDB_CAN_USE_DEBUGSERVER)
add_subdirectory(debugserver)
-endif()
\ No newline at end of file
+endif()
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -1280,9 +1280,9 @@
lldb::user_id_t uid = LLDB_INVALID_UID;
- size_t byte_count = std::numeric_limits<size_t>::max();
+ uint64_t byte_count = std::numeric_limits<uint64_t>::max();
lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
- size_t offset = std::numeric_limits<size_t>::max();
+ uint64_t offset = std::numeric_limits<uint64_t>::max();
auto json_object = StructuredData::ParseJSON(packet.Peek());
@@ -1316,8 +1316,8 @@
if (error.Fail())
return SendErrorResponse(error.GetError());
- for (size_t i = 0; i < buf.size(); ++i)
- response.PutHex8(buf[i]);
+ for (auto i : buf)
+ response.PutHex8(i);
StreamGDBRemote escaped_response;
escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
Index: source/Plugins/Process/Linux/ProcessorTrace.h
===================================================================
--- /dev/null
+++ source/Plugins/Process/Linux/ProcessorTrace.h
@@ -0,0 +1,156 @@
+//===-- ProcessorTrace.h -------------------------------------- -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessorTrace_H_
+#define liblldb_ProcessorTrace_H_
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+
+#include "lldb/Core/TraceOptions.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+enum PTErrorCode {
+ FileNotFound,
+ ThreadNotTraced,
+ TraceIDNotTraced,
+ ThreadNotSpecified,
+ ProcessNotBeingTraced,
+ ThreadAlreadyBeingTraced,
+ ProcessAlreadyBeingTraced,
+ InvalidFile,
+ PerfEventNotSupported,
+ PerfEventSyscallFailed,
+ MetaBufferAllocFailed,
+ TraceBufferAllocFailed,
+ TraceBufferDeAllocFailed,
+ MetaBufferDeAllocFailed,
+ PerfEventCloseFailed,
+ EmptyTraceBuffer,
+ EmptyMetaBuffer,
+ CPUInfoNotFound,
+ NullPointer,
+ AllocFailed,
+ InvalidTraceID,
+ InvalidOffset,
+ TraceNotSupported,
+ InvalidThread
+};
+
+// ---------------------------------------------------------------------
+// This class keeps track of one tracing instance of
+// Intel(R) Processor Trace on Linux OS. There is a map keeping track
+// of different tracing instances on each thread, which enables trace
+// gathering on a per thread level.
+//
+// The tracing instance is linked with a trace id. The trace id acts like
+// a key to the tracing instance and trace manipulations could be
+// performed using the trace id.
+//
+// The traace id could map to trace instances for a group of threads
+// (spanning to all the threads in the process) or a single thread.
+// The kernel interface for us is the perf_event_open.
+// ---------------------------------------------------------------------
+
+class ProcessorTraceMonitor;
+typedef std::shared_ptr<ProcessorTraceMonitor> ProcessorTraceMonitorSP;
+
+class ProcessorTraceMonitor {
+ int m_fd;
+ void *m_mmap_data;
+ void *m_mmap_aux;
+ void *m_mmap_base;
+ lldb::user_id_t m_traceid;
+ lldb::tid_t m_thread_id;
+
+ // Counter to track trace instances.
+ static lldb::user_id_t m_trace_num;
+
+ // Trace id of trace instance corresponding to the process.
+ static lldb::user_id_t m_pt_process_traceid;
+
+ // TraceOptions to be used for the complete process
+ // in case whole process is being traced.This config
+ // will also be applied to newly spawned threads.
+ static TraceOptions m_pt_process_config;
+
+ uint64_t GetAuxBufferSize(Status &error) const;
+
+ uint64_t GetDataBufferSize(Status &error) const;
+
+ void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; }
+
+ static Status GetCPUType(StructuredData::DictionarySP params_dict);
+
+ Status StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ const TraceOptions &config);
+
+ ProcessorTraceMonitor()
+ : m_fd(-1), m_mmap_data(nullptr), m_mmap_aux(nullptr),
+ m_mmap_base(nullptr), m_traceid(LLDB_INVALID_UID),
+ m_thread_id(LLDB_INVALID_THREAD_ID){};
+
+public:
+ static llvm::Expected<ProcessorTraceMonitorSP>
+ Create(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config,
+ bool useProcessSettings);
+
+ Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0);
+
+ Status ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0);
+
+ Status Destroy();
+
+ ~ProcessorTraceMonitor() { (void)Destroy(); }
+
+ void SetThreadID(lldb::tid_t tid) { m_thread_id = tid; }
+
+ lldb::tid_t GetThreadID() const { return m_thread_id; }
+
+ lldb::user_id_t GetTraceID() const { return m_traceid; }
+
+ Status GetTraceConfig(TraceOptions &config) const;
+
+ static TraceOptions GetProcessTraceConfig() { return m_pt_process_config; }
+
+ static lldb::user_id_t GetProcessTraceID() { return m_pt_process_traceid; }
+
+ static Status SetProcessTraceConfig(const TraceOptions &config);
+
+ static void SetProcessTraceID(lldb::user_id_t traceid) {
+ m_pt_process_traceid = traceid;
+ }
+
+ // ---------------------------------------------------------------------
+ /// 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::MutableArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset);
+};
+} // namespace lldb_private
+#endif
\ No newline at end of file
Index: source/Plugins/Process/Linux/ProcessorTrace.cpp
===================================================================
--- /dev/null
+++ source/Plugins/Process/Linux/ProcessorTrace.cpp
@@ -0,0 +1,606 @@
+//===-- ProcessorTrace.cpp ------------------------------------ -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <fstream>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "ProcessorTrace.h"
+#include "lldb/Host/linux/Support.h"
+
+#include <linux/perf_event.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+lldb::user_id_t ProcessorTraceMonitor::m_trace_num = 0;
+lldb::user_id_t ProcessorTraceMonitor::m_pt_process_traceid = LLDB_INVALID_UID;
+TraceOptions ProcessorTraceMonitor::m_pt_process_config;
+
+uint64_t ProcessorTraceMonitor::GetAuxBufferSize(Status &error) const {
+#ifndef PERF_ATTR_SIZE_VER5
+ error.SetError(PerfEventNotSupported, eErrorTypeGeneric);
+ return 0;
+#else
+ perf_event_mmap_page *base =
+ reinterpret_cast<perf_event_mmap_page *>(m_mmap_base);
+ if (base == nullptr) {
+ error.SetError(NullPointer, eErrorTypeGeneric);
+ return 0;
+ }
+ return base->aux_size;
+#endif
+}
+
+uint64_t ProcessorTraceMonitor::GetDataBufferSize(Status &error) const {
+#ifndef PERF_ATTR_SIZE_VER5
+ error.SetError(PerfEventNotSupported, eErrorTypeGeneric);
+ return 0;
+#else
+ perf_event_mmap_page *base =
+ reinterpret_cast<perf_event_mmap_page *>(m_mmap_base);
+ if (base == nullptr) {
+ error.SetError(NullPointer, eErrorTypeGeneric);
+ return 0;
+ }
+ return (base->data_size);
+#endif
+}
+
+Status ProcessorTraceMonitor::GetTraceConfig(TraceOptions &config) const {
+ Status error;
+
+ config.setType(lldb::TraceType::eTraceTypeProcessorTrace);
+ uint64_t data_size = GetDataBufferSize(error);
+ if (error.Fail())
+ return error;
+
+ config.setMetaDataBufferSize(data_size);
+
+ uint64_t aux_size = GetAuxBufferSize(error);
+ if (error.Fail())
+ return error;
+
+ config.setTraceBufferSize(aux_size);
+
+ auto custom_params_sp = std::make_shared<StructuredData::Dictionary>();
+ error = GetCPUType(custom_params_sp);
+ if (error.Fail())
+ return error;
+
+ llvm::StringRef intel_custom_params_key("intel-pt");
+
+ auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
+ intel_custom_params->AddItem(
+ intel_custom_params_key,
+ StructuredData::ObjectSP(std::move(custom_params_sp)));
+
+ config.setTraceParams(intel_custom_params);
+
+ return error;
+}
+
+Status
+ProcessorTraceMonitor::SetProcessTraceConfig(const TraceOptions &config) {
+ m_pt_process_config = config;
+
+ auto custom_params_sp = std::make_shared<StructuredData::Dictionary>();
+ Status error = ProcessorTraceMonitor::GetCPUType(custom_params_sp);
+ if (error.Fail())
+ return error;
+
+ llvm::StringRef intel_custom_params_key("intel-pt");
+ auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
+ intel_custom_params->AddItem(
+ intel_custom_params_key,
+ StructuredData::ObjectSP(std::move(custom_params_sp)));
+
+ m_pt_process_config.setTraceParams(intel_custom_params);
+
+ return error;
+}
+
+Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ const TraceOptions &config) {
+ Status error;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ LLDB_LOG(log, "ProcessorTrace {0} {1}", __FUNCTION__, config.getThreadID());
+
+#ifndef PERF_ATTR_SIZE_VER5
+ LLDB_LOG(log, "ProcessorTrace {0} Not supported", __FUNCTION__);
+ error.SetError(PerfEventNotSupported, eErrorTypePOSIX);
+#else
+
+ LLDB_LOG(log, "{0} called thread id {1}", __FUNCTION__, tid);
+ uint64_t page_size = getpagesize();
+ uint64_t bufsize = config.getTraceBufferSize();
+ uint64_t metabufsize = config.getMetaDataBufferSize();
+
+ uint64_t numpages = static_cast<uint64_t>(
+ llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
+ numpages = std::max(1ul, numpages);
+ bufsize = page_size * numpages;
+
+ numpages = static_cast<uint64_t>(
+ llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
+ numpages = std::max(0ul, numpages);
+ metabufsize = page_size * numpages;
+
+ perf_event_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.size = sizeof(attr);
+ attr.exclude_kernel = 1;
+ attr.sample_type = PERF_SAMPLE_TIME;
+ attr.sample_id_all = 1;
+ attr.exclude_hv = 1;
+ attr.exclude_idle = 1;
+ attr.mmap = 1;
+
+ int intel_pt_type = 0;
+
+ auto ret = llvm::MemoryBuffer::getFileAsStream(
+ "/sys/bus/event_source/devices/intel_pt/type");
+ if (!ret) {
+ LLDB_LOG(log, "ProcessorTrace failed to open Config file");
+ error.SetError(FileNotFound, eErrorTypePOSIX);
+ return error;
+ }
+
+ StringRef rest = ret.get()->getBuffer();
+ if (rest.empty() || rest.trim().getAsInteger(10, intel_pt_type)) {
+ LLDB_LOG(log, "ProcessorTrace failed to read Config file");
+ error.SetError(InvalidFile, eErrorTypePOSIX);
+ return error;
+ }
+
+ rest.trim().getAsInteger(10, intel_pt_type);
+ LLDB_LOG(log, "ProcessorTrace intel pt type {0}", intel_pt_type);
+ attr.type = intel_pt_type;
+
+ LLDB_LOG(log, "ProcessorTrace {0} meta buffer size {1}", __FUNCTION__,
+ metabufsize);
+ LLDB_LOG(log, "ProcessorTrace {0} buffer size {1} ", __FUNCTION__, bufsize);
+
+ if (error.Fail()) {
+ LLDB_LOG(log, "ProcessorTrace {0} Status in custom config {1}",
+ __FUNCTION__, error.AsCString());
+
+ return error;
+ }
+
+ errno = 0;
+ m_fd =
+ syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
+ if (m_fd == -1) {
+ LLDB_LOG(log, "ProcessorTrace {0} syscall error {1}", __FUNCTION__, errno);
+ error.SetError(PerfEventSyscallFailed, eErrorTypePOSIX);
+ return error;
+ }
+
+ perf_event_mmap_page *header;
+
+ errno = 0;
+ m_mmap_base =
+ mmap(NULL, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, m_fd, 0);
+ if (m_mmap_base == MAP_FAILED) {
+ m_mmap_base = nullptr;
+ LLDB_LOG(log, "ProcessorTrace {0} mmap base error {1}", __FUNCTION__,
+ errno);
+ error.SetError(MetaBufferAllocFailed, eErrorTypePOSIX);
+ (void)Destroy();
+ return error;
+ }
+
+ header = reinterpret_cast<perf_event_mmap_page *>(m_mmap_base);
+
+ m_mmap_data = reinterpret_cast<uint8_t *>(m_mmap_base) + header->data_offset;
+
+ header->aux_offset = header->data_offset + header->data_size;
+ header->aux_size = bufsize;
+
+ errno = 0;
+ m_mmap_aux = mmap(NULL, bufsize, PROT_READ, MAP_SHARED, m_fd,
+ static_cast<long int>(header->aux_offset));
+ if (m_mmap_aux == MAP_FAILED) {
+ m_mmap_aux = nullptr;
+ LLDB_LOG(log, "ProcessorTrace {0} second mmap done {1}", __FUNCTION__,
+ errno);
+ error.SetError(TraceBufferAllocFailed, eErrorTypePOSIX);
+ (void)Destroy();
+ return error;
+ }
+#endif
+ return error;
+}
+
+Status
+ProcessorTraceMonitor::GetCPUType(StructuredData::DictionarySP params_dict) {
+
+ Status error;
+ uint64_t cpu_family = -1;
+ uint64_t model = -1;
+ uint64_t stepping = -1;
+ std::string vendor_id;
+
+ if (!params_dict) {
+ error.SetError(NullPointer, eErrorTypeGeneric);
+ return error;
+ }
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ auto BufferOrError = getProcFile("cpuinfo");
+ if (!BufferOrError)
+ return BufferOrError.getError();
+
+ LLDB_LOG(log, "GetCPUType Function");
+
+ StringRef Rest = BufferOrError.get()->getBuffer();
+ while (!Rest.empty()) {
+ StringRef Line;
+ std::tie(Line, Rest) = Rest.split('\n');
+
+ SmallVector<StringRef, 2> columns;
+ Line.split(columns, StringRef(":"), -1, false);
+
+ if (columns.size() < 2)
+ continue; // continue searching
+
+ columns[1] = columns[1].trim(" ");
+ if (columns[0].contains("cpu family") &&
+ columns[1].getAsInteger(10, cpu_family))
+ continue;
+
+ else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
+ continue;
+
+ else if (columns[0].contains("stepping") &&
+ columns[1].getAsInteger(10, stepping))
+ continue;
+
+ else if (columns[0].contains("vendor_id")) {
+ vendor_id = columns[1].str();
+ if (!vendor_id.empty())
+ continue;
+ }
+ LLDB_LOG(log, "ProcessorTrace {0}:{1}:{2}:{3}", cpu_family, model, stepping,
+ vendor_id.c_str());
+
+ if ((cpu_family != static_cast<uint64_t>(-1)) &&
+ (model != static_cast<uint64_t>(-1)) &&
+ (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
+ params_dict->AddIntegerItem("cpu_family", cpu_family);
+ params_dict->AddIntegerItem("cpu_model", model);
+ params_dict->AddIntegerItem("cpu_stepping", stepping);
+ params_dict->AddStringItem("cpu_vendor", vendor_id);
+ return error; // we are done
+ }
+ }
+
+ error.SetError(CPUInfoNotFound, eErrorTypeGeneric);
+ return error;
+}
+
+llvm::Expected<ProcessorTraceMonitorSP>
+ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
+ const TraceOptions &config,
+ bool useProcessSettings) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ Status error;
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ error.SetError(ThreadNotSpecified, eErrorTypeGeneric);
+ return std::move(error.ToError());
+ }
+
+ if (useProcessSettings) {
+ if (m_pt_process_traceid == LLDB_INVALID_UID)
+ m_pt_process_traceid = ++m_trace_num;
+ LLDB_LOG(log, "NativeProcessLinux m_trace_num {0}", m_trace_num);
+ LLDB_LOG(log, "NativeProcessLinux m_pt_process_traceid {0}",
+ m_pt_process_traceid);
+ }
+
+ ProcessorTraceMonitorSP pt_monitor_sp(new ProcessorTraceMonitor);
+
+ if (!pt_monitor_sp) {
+ LLDB_LOG(log, "NativeProcessLinux {0}", "Alloc failed");
+ error.SetError(AllocFailed, eErrorTypeGeneric);
+ return std::move(error.ToError());
+ }
+
+ error = pt_monitor_sp->StartTrace(pid, tid, config);
+ if (error.Fail())
+ return std::move(error.ToError());
+
+ pt_monitor_sp->SetThreadID(tid);
+
+ if (useProcessSettings) {
+ pt_monitor_sp->SetTraceID(m_pt_process_traceid);
+
+ // The buffer sizes also need to be updated
+ // as the buffer sizes might have been rounded
+ // to page sizes.
+ uint64_t data_size = pt_monitor_sp->GetDataBufferSize(error);
+ if (error.Fail())
+ return std::move(error.ToError());
+
+ uint64_t aux_size = pt_monitor_sp->GetAuxBufferSize(error);
+ if (error.Fail())
+ return std::move(error.ToError());
+
+ m_pt_process_config.setTraceBufferSize(aux_size);
+ m_pt_process_config.setMetaDataBufferSize(data_size);
+ } else {
+ pt_monitor_sp->SetTraceID(++m_trace_num);
+ LLDB_LOG(log, "NativeProcessLinux Trace ID {0}", m_trace_num);
+ }
+ return pt_monitor_sp;
+}
+
+Status ProcessorTraceMonitor::Destroy() {
+ Status error;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ int ret;
+#ifndef PERF_ATTR_SIZE_VER5
+ LLDB_LOG(log, "ProcessorTrace {0} Not supported", __FUNCTION__);
+ error.SetError(PerfEventNotSupported, eErrorTypeGeneric);
+#else
+ if ((m_mmap_aux != nullptr) && (m_mmap_base != nullptr)) {
+ uint64_t aux_size =
+ reinterpret_cast<perf_event_mmap_page *>(m_mmap_base)->aux_size;
+ ret = munmap(m_mmap_aux, aux_size);
+ if (ret != 0) {
+ LLDB_LOG(log, "ProcessorTrace {0} munmap aux error {1}", __FUNCTION__,
+ errno);
+ error.SetError(TraceBufferDeAllocFailed, eErrorTypeGeneric);
+ } else
+ m_mmap_aux = nullptr;
+ }
+ // Since we allocate one page extra during
+ // initialization for the perf_event_mmap_page header
+ if (m_mmap_base != nullptr) {
+ uint64_t data_size =
+ reinterpret_cast<perf_event_mmap_page *>(m_mmap_base)->data_size;
+ int page_size = getpagesize();
+ data_size = data_size + page_size;
+ ret = munmap(m_mmap_base, data_size);
+ if (ret != 0) {
+ LLDB_LOG(log, "ProcessorTrace {0} munmap data error {1}", __FUNCTION__,
+ errno);
+ error.SetError(MetaBufferDeAllocFailed, eErrorTypeGeneric);
+ } else {
+ m_mmap_base = nullptr;
+ m_mmap_data = nullptr;
+ }
+ }
+ if (m_fd != -1) {
+ ret = close(m_fd);
+ if (ret != 0) {
+ LLDB_LOG(log, "ProcessorTrace {0} closing file descriptor error {1}",
+ __FUNCTION__, errno);
+ error.SetError(PerfEventCloseFailed, eErrorTypeGeneric);
+ } else
+ m_fd = -1;
+ }
+#endif
+ return error;
+}
+
+Status
+ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+
+#ifndef PERF_ATTR_SIZE_VER5
+ LLDB_LOG(log, "ProcessorTrace {0} Not supported ", __FUNCTION__);
+ error.SetError(PerfEventNotSupported, eErrorTypeGeneric);
+ buffer = buffer.slice(buffer.size());
+ return error;
+#else
+ if (buffer.empty()) {
+ LLDB_LOG(log, "ProcessorTrace {0} Empty buffer ", __FUNCTION__);
+ error.SetError(EmptyTraceBuffer, eErrorTypeGeneric);
+ return error;
+ }
+
+ perf_event_mmap_page *base =
+ reinterpret_cast<perf_event_mmap_page *>(m_mmap_base);
+ if (base == nullptr) {
+ LLDB_LOG(log, "ProcessorTrace {0} Null pointer ", __FUNCTION__);
+ error.SetError(NullPointer, eErrorTypeGeneric);
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+ uint64_t aux_size = base->aux_size;
+ uint64_t head = base->aux_head;
+
+ LLDB_LOG(log, "ProcessorTrace {0} Aux size -{1} , Head - {2}", __FUNCTION__,
+ 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,
+ llvm::MutableArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(m_mmap_aux),
+ static_cast<size_t>(aux_size)),
+ static_cast<size_t>(head), offset);
+ LLDB_LOG(log, "ReadCyclic BUffer Done");
+ return error;
+#endif
+}
+
+Status
+ProcessorTraceMonitor::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ uint64_t buf_size = static_cast<uint64_t>(buffer.size());
+ uint64_t bytes_remaining = buf_size;
+ Status error;
+#ifndef PERF_ATTR_SIZE_VER5
+ LLDB_LOG(log, "ProcessorTrace {0} Not supported ", __FUNCTION__);
+ error.SetError(PerfEventNotSupported, eErrorTypeGeneric);
+ buffer = buffer.slice(buffer.size());
+ return error;
+#else
+ if (buf_size == 0) {
+ LLDB_LOG(log, "ProcessorTrace {0} Empty buffer ", __FUNCTION__);
+ error.SetError(EmptyMetaBuffer, eErrorTypeGeneric);
+ return error;
+ }
+ perf_event_mmap_page *base =
+ reinterpret_cast<perf_event_mmap_page *>(m_mmap_base);
+ if (base == nullptr) {
+ LLDB_LOG(log, "ProcessorTrace {0} Null pointer ", __FUNCTION__);
+ error.SetError(NullPointer, eErrorTypeGeneric);
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+ uint64_t data_size = base->data_size;
+ uint64_t head = base->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
+ */
+
+ uint8_t *buf = reinterpret_cast<uint8_t *>(buffer.data());
+
+ LLDB_LOG(log, "ProcessorTrace {0} bytes_remaining - {1}", __FUNCTION__,
+ bytes_remaining);
+
+ if (head > data_size) {
+ head = head % data_size;
+ LLDB_LOG(log, "ProcessorTrace {0} Data size -{1} Head - {2}", __FUNCTION__,
+ data_size, head);
+
+ ReadCyclicBuffer(
+ buffer,
+ llvm::MutableArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(m_mmap_data),
+ static_cast<size_t>(data_size)),
+ static_cast<size_t>(head), offset);
+ size_t size_read = buffer.size();
+ bytes_remaining = bytes_remaining - size_read;
+ } else {
+ LLDB_LOG(log, "ProcessorTrace {0}, Head - {1}", __FUNCTION__, head);
+ if (offset >= head) {
+ LLDB_LOG(log, "ProcessorTrace {0} Invalid Offset ", __FUNCTION__);
+ error.SetError(InvalidOffset, eErrorTypeGeneric);
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+ uint64_t bytes_to_read = head - offset;
+ if (bytes_to_read < bytes_remaining) {
+ memcpy(buf, (reinterpret_cast<uint8_t *>(m_mmap_data) + offset),
+ bytes_to_read);
+ bytes_remaining = bytes_remaining - bytes_to_read;
+ } else {
+ memcpy(buf, (reinterpret_cast<uint8_t *>(m_mmap_data) + offset),
+ bytes_remaining);
+ bytes_remaining = 0;
+ }
+ }
+ buffer = llvm::MutableArrayRef<uint8_t>(buffer.data(),
+ (buf_size - bytes_remaining));
+ return error;
+#endif
+}
+
+void ProcessorTraceMonitor::ReadCyclicBuffer(
+ llvm::MutableArrayRef<uint8_t> &dst, llvm::MutableArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset) {
+
+ // size_t ProcessorTraceMonitor::ReadCyclicBuffer(void *buf, size_t buf_size,
+ // void *cyc_buf, size_t
+ // cyc_buf_size, size_t cyc_start,
+ // size_t offset) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (dst.empty() || src.empty()) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (dst.data() == nullptr || src.data() == nullptr) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (src_cyc_index > src.size()) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (offset >= src.size()) {
+ LLDB_LOG(log, "NativeProcessLinux {0} Too Big offset ", __FUNCTION__);
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ auto firstpart = src.slice(src_cyc_index);
+ auto secondpart = src.drop_back(src_cyc_index);
+ size_t size_to_read = src.size() - offset;
+
+ if (dst.size() >= size_to_read) {
+ if (offset >= firstpart.size()) {
+ secondpart = secondpart.slice(offset - firstpart.size());
+ std::copy(secondpart.begin(), secondpart.end(), dst.begin());
+ dst = dst.drop_back(dst.size() - secondpart.size());
+ } else {
+ firstpart = firstpart.slice(offset);
+ auto read_end =
+ std::copy(firstpart.begin(), firstpart.end(), dst.begin());
+ size_to_read -= firstpart.size();
+ secondpart = secondpart.drop_back(secondpart.size() - size_to_read);
+ std::copy(secondpart.begin(), secondpart.end(), read_end);
+ dst = dst.drop_back(dst.size() - firstpart.size() - secondpart.size());
+ }
+ } else {
+ if (offset >= firstpart.size()) {
+ secondpart = secondpart.slice(offset - firstpart.size());
+ std::copy(secondpart.begin(), secondpart.begin() + dst.size(),
+ dst.begin());
+ } else {
+ firstpart = firstpart.slice(offset);
+ if (dst.size() <= firstpart.size()) {
+ std::copy(firstpart.begin(), firstpart.begin() + dst.size(),
+ dst.begin());
+ } else {
+ auto read_end =
+ std::copy(firstpart.begin(), firstpart.end(), dst.begin());
+ secondpart = secondpart.drop_back(secondpart.size() - dst.size() +
+ firstpart.size());
+ std::copy(secondpart.begin(), secondpart.end(), read_end);
+ }
+ }
+ }
+}
\ No newline at end of file
Index: source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- source/Plugins/Process/Linux/NativeProcessLinux.h
+++ source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -23,6 +23,7 @@
#include "lldb/lldb-types.h"
#include "NativeThreadLinux.h"
+#include "ProcessorTrace.h"
#include "lldb/Host/common/NativeProcessProtocol.h"
namespace lldb_private {
@@ -105,6 +106,22 @@
return getProcFile(GetID(), "auxv");
}
+ lldb::user_id_t StartTrace(const TraceOptions &config,
+ Status &error) override;
+
+ Status StopTrace(lldb::user_id_t traceid,
+ lldb::tid_t thread = LLDB_INVALID_THREAD_ID) override;
+
+ Status GetData(lldb::user_id_t traceid, lldb::tid_t thread,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) override;
+
+ Status GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) override;
+
+ Status GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) override;
+
// ---------------------------------------------------------------------
// Interface used by NativeRegisterContext-derived classes.
// ---------------------------------------------------------------------
@@ -228,6 +245,41 @@
void SigchldHandler();
Status PopulateMemoryRegionCache();
+
+ lldb::user_id_t StartTracingAllThreads(const TraceOptions &config,
+ Status &error);
+
+ // This function is intended to be used to stop tracing
+ // on a thread that exited.
+ Status StopTracingForThread(lldb::tid_t thread);
+
+ // The below function as the name suggests, looks up a ProcessorTrace
+ // instance from the m_processor_trace_monitor map. In the case of
+ // process tracing where the traceid passed would map to the complete
+ // process, it is mandatory to provide a threadid to obtain a trace
+ // instance (since ProcessorTrace is tied to a thread). In the other
+ // scenario that an individual thread is being traced, just the traceid
+ // is sufficient to obtain the actual ProcessorTrace instance.
+ ProcessorTraceMonitorSP LookupProcessorTraceInstance(lldb::user_id_t traceid,
+ lldb::tid_t thread,
+ Status &error);
+
+ // Stops tracing on individual threads being traced. Not intended
+ // to be used to stop tracing on complete process.
+ Status StopProcessorTracingOnThread(lldb::user_id_t traceid,
+ lldb::tid_t thread);
+
+ // Intended to stop tracing on complete process.
+ // Should not be used for stopping trace on
+ // individual threads.
+ Status StopProcessorTracingOnProcess();
+
+ llvm::DenseMap<lldb::tid_t, ProcessorTraceMonitorSP>
+ m_processor_trace_monitor;
+
+ // Set for tracking threads being traced under
+ // same process user id.
+ llvm::DenseSet<lldb::tid_t> m_pt_traced_thread_group;
};
} // namespace process_linux
Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -607,6 +607,21 @@
info.si_pid);
auto thread_sp = AddThread(pid);
+
+ if (ProcessorTraceMonitor::GetProcessTraceID() != LLDB_INVALID_UID) {
+ auto traceMonitor = ProcessorTraceMonitor::Create(
+ GetID(), pid, ProcessorTraceMonitor::GetProcessTraceConfig(), true);
+ if (traceMonitor) {
+ m_pt_traced_thread_group.insert(pid);
+ m_processor_trace_monitor.insert(std::make_pair(pid, *traceMonitor));
+ } else {
+ LLDB_LOG(log, "failed to start trace on thread {0}", pid);
+ Status error2(traceMonitor.takeError());
+ LLDB_LOG(log, "{0} error in create m_code {1} m_string {2}",
+ __FUNCTION__, error2.GetError(), error2.AsCString());
+ }
+ }
+
// Resume the newly created thread.
ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
ThreadWasCreated(*thread_sp);
@@ -719,6 +734,19 @@
LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid);
new_thread_sp = AddThread(tid);
+ if (ProcessorTraceMonitor::GetProcessTraceID() != LLDB_INVALID_UID) {
+ auto traceMonitor = ProcessorTraceMonitor::Create(
+ GetID(), tid, ProcessorTraceMonitor::GetProcessTraceConfig(), true);
+ if (traceMonitor) {
+ m_pt_traced_thread_group.insert(tid);
+ m_processor_trace_monitor.insert(std::make_pair(tid, *traceMonitor));
+ } else {
+ LLDB_LOG(log, "failed to start trace on thread {0}", tid);
+ Status error2(traceMonitor.takeError());
+ LLDB_LOG(log, "{0} error in create m_code {1} m_string {2}", __FUNCTION__,
+ error2.GetError(), error2.AsCString());
+ }
+ }
ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
ThreadWasCreated(*new_thread_sp);
}
@@ -1331,6 +1359,9 @@
e; // Save the error, but still attempt to detach from other threads.
}
+ m_processor_trace_monitor.clear();
+ ProcessorTraceMonitor::SetProcessTraceID(LLDB_INVALID_UID);
+
return error;
}
@@ -2119,6 +2150,8 @@
}
}
+ if (found)
+ StopTracingForThread(thread_id);
SignalIfAllThreadsStopped();
return found;
}
@@ -2451,3 +2484,258 @@
return error;
}
+
+ProcessorTraceMonitorSP NativeProcessLinux::LookupProcessorTraceInstance(
+ lldb::user_id_t traceid, lldb::tid_t thread, Status &error) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ if (thread == LLDB_INVALID_THREAD_ID &&
+ traceid == ProcessorTraceMonitor::GetProcessTraceID()) {
+ LLDB_LOG(log, "NativeProcessLinux {0}_thread not specified: {1}",
+ __FUNCTION__, traceid);
+ error.SetError(ThreadNotSpecified, eErrorTypeGeneric);
+ return nullptr;
+ }
+
+ for (auto iter : m_processor_trace_monitor) {
+ if (traceid == iter.second->GetTraceID() &&
+ (thread == iter.first || thread == LLDB_INVALID_THREAD_ID))
+ return iter.second;
+ }
+
+ LLDB_LOG(log, "NativeProcessLinux {0}_traceid not being traced: {1}",
+ __FUNCTION__, traceid);
+ error.SetError(TraceIDNotTraced, eErrorTypeGeneric);
+ return nullptr;
+}
+
+Status NativeProcessLinux::GetMetaData(lldb::user_id_t traceid,
+ lldb::tid_t thread,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ TraceOptions trace_options;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+
+ LLDB_LOG(log, "NativeProcessLinux {0}_traceid {1}", __FUNCTION__, traceid);
+
+ auto perf_monitor = LookupProcessorTraceInstance(traceid, thread, error);
+ if (!perf_monitor) {
+ LLDB_LOG(log, "NativeProcessLinux {0}_traceid not being traced: {1}",
+ __FUNCTION__, traceid);
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+ return perf_monitor->ReadPerfTraceData(buffer, offset);
+}
+
+Status NativeProcessLinux::GetData(lldb::user_id_t traceid, lldb::tid_t thread,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ TraceOptions trace_options;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+
+ LLDB_LOG(log, "NativeProcessLinux {0}_traceid {1}", __FUNCTION__, traceid);
+
+ auto perf_monitor = LookupProcessorTraceInstance(traceid, thread, error);
+ if (!perf_monitor) {
+ LLDB_LOG(log, "NativeProcessLinux {0}_traceid not being traced: {1}",
+ __FUNCTION__, traceid);
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+ return perf_monitor->ReadPerfTraceAux(buffer, offset);
+}
+
+Status NativeProcessLinux::GetTraceConfig(lldb::user_id_t traceid,
+ TraceOptions &config) {
+ Status error;
+
+ if (config.getThreadID() == LLDB_INVALID_THREAD_ID &&
+ traceid == ProcessorTraceMonitor::GetProcessTraceID()) {
+ config = ProcessorTraceMonitor::GetProcessTraceConfig();
+ } else {
+ auto perf_monitor =
+ LookupProcessorTraceInstance(traceid, config.getThreadID(), error);
+ if (!perf_monitor)
+ return error;
+ error = perf_monitor->GetTraceConfig(config);
+ }
+ return error;
+}
+
+lldb::user_id_t
+NativeProcessLinux::StartTracingAllThreads(const TraceOptions &config,
+ Status &error) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ if (config.getType() != TraceType::eTraceTypeProcessorTrace)
+ return LLDB_INVALID_UID;
+
+ if (ProcessorTraceMonitor::GetProcessTraceID() != LLDB_INVALID_UID) {
+ error.SetError(ProcessAlreadyBeingTraced, eErrorTypeGeneric);
+ return ProcessorTraceMonitor::GetProcessTraceID();
+ }
+ LLDB_LOG(log, "NativeProcessLinux {0}", __FUNCTION__);
+ ProcessorTraceMonitor::SetProcessTraceConfig(config);
+
+ for (const auto &thread_sp : m_threads) {
+ if (auto traceInstance = ProcessorTraceMonitor::Create(
+ GetID(), thread_sp->GetID(),
+ ProcessorTraceMonitor::GetProcessTraceConfig(), true)) {
+ m_pt_traced_thread_group.insert(thread_sp->GetID());
+ m_processor_trace_monitor.insert(
+ std::pair<lldb::tid_t, ProcessorTraceMonitorSP>(thread_sp->GetID(),
+ *traceInstance));
+ }
+ }
+
+ LLDB_LOG(log, "NativeProcessLinux {0} {1}",
+ ProcessorTraceMonitor::GetProcessTraceID());
+ return ProcessorTraceMonitor::GetProcessTraceID();
+}
+
+lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config,
+ Status &error) {
+ if (config.getType() != TraceType::eTraceTypeProcessorTrace)
+ return NativeProcessProtocol::StartTrace(config, error);
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ LLDB_LOG(log, "NativeProcessLinux {0}", __FUNCTION__);
+ lldb::tid_t threadid = config.getThreadID();
+
+ if (threadid == LLDB_INVALID_THREAD_ID)
+ return StartTracingAllThreads(config, error);
+
+ auto thread_sp = GetThreadByID(threadid);
+ if (!thread_sp) {
+ // Thread not tracked by lldb so don't trace.
+ error.SetError(InvalidThread, eErrorTypeGeneric);
+ return LLDB_INVALID_UID;
+ }
+
+ auto iter = m_processor_trace_monitor.find(threadid);
+ if (iter != m_processor_trace_monitor.end()) {
+ LLDB_LOG(log, "NativeProcessLinux {0}", "Thread already being traced");
+ error.SetError(ThreadAlreadyBeingTraced, eErrorTypeGeneric);
+ return LLDB_INVALID_UID;
+ }
+
+ auto traceMonitor =
+ ProcessorTraceMonitor::Create(GetID(), threadid, config, false);
+ if (!traceMonitor) {
+ Status error2(traceMonitor.takeError());
+ LLDB_LOG(log, "{0} error in create m_code {1} m_string {2}", __FUNCTION__,
+ error2.GetError(), error2.AsCString());
+ error = error2;
+ return LLDB_INVALID_UID;
+ }
+ m_processor_trace_monitor.insert(std::make_pair(threadid, *traceMonitor));
+ return (*traceMonitor)->GetTraceID();
+}
+
+Status NativeProcessLinux::StopTracingForThread(lldb::tid_t thread) {
+ Status error;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ LLDB_LOG(log, "NativeProcessLinux {0} Thread {1}", __FUNCTION__, thread);
+
+ auto iter = m_processor_trace_monitor.find(thread);
+ if (iter != m_processor_trace_monitor.end())
+ error = StopTrace(iter->second->GetTraceID(), thread);
+
+ return error;
+}
+
+Status NativeProcessLinux::StopTrace(lldb::user_id_t traceid,
+ lldb::tid_t thread) {
+ Status error;
+
+ TraceOptions trace_options;
+ trace_options.setThreadID(thread);
+ error = NativeProcessLinux::GetTraceConfig(traceid, trace_options);
+
+ if (error.Fail())
+ return error;
+
+ switch (trace_options.getType()) {
+ case lldb::TraceType::eTraceTypeProcessorTrace:
+ if (traceid == ProcessorTraceMonitor::GetProcessTraceID() &&
+ thread == LLDB_INVALID_THREAD_ID)
+ error = StopProcessorTracingOnProcess();
+ else
+ error = StopProcessorTracingOnThread(traceid, thread);
+ break;
+ default:
+ error.SetError(TraceNotSupported, eErrorTypeGeneric);
+ break;
+ }
+
+ return error;
+}
+
+Status NativeProcessLinux::StopProcessorTracingOnProcess() {
+ Status error;
+ if (ProcessorTraceMonitor::GetProcessTraceID() == LLDB_INVALID_UID) {
+ error.SetError(ProcessNotBeingTraced, eErrorTypeGeneric);
+ return error;
+ }
+
+ for (auto thread_id_iter : m_pt_traced_thread_group)
+ m_processor_trace_monitor.erase(thread_id_iter);
+ m_pt_traced_thread_group.clear();
+ ProcessorTraceMonitor::SetProcessTraceID(LLDB_INVALID_UID);
+ return error;
+}
+
+Status NativeProcessLinux::StopProcessorTracingOnThread(lldb::user_id_t traceid,
+ lldb::tid_t thread) {
+ Status error;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (thread == LLDB_INVALID_THREAD_ID) {
+ for (auto iter : m_processor_trace_monitor) {
+ if (iter.second->GetTraceID() == traceid) {
+ // Stopping a trace instance for an individual thread
+ // hence there will only be one traceid that can match.
+ m_processor_trace_monitor.erase(iter.first);
+ return error;
+ }
+ LLDB_LOG(log, "NativeProcessLinux Trace ID {0}",
+ iter.second->GetTraceID());
+ }
+
+ LLDB_LOG(log, "NativeProcessLinux {0} Invalid TraceID", __FUNCTION__);
+ error.SetError(InvalidTraceID, eErrorTypeGeneric);
+ return error;
+ }
+
+ // thread is specified so we can use find function on the map.
+ auto iter = m_processor_trace_monitor.find(thread);
+ if (iter == m_processor_trace_monitor.end()) {
+ // thread not found in our map.
+ LLDB_LOG(log, "NativeProcessLinux {0} thread not being traced",
+ __FUNCTION__);
+ error.SetError(ThreadNotTraced, eErrorTypeGeneric);
+ return error;
+ }
+ if (iter->second->GetTraceID() != traceid) {
+ // traceid did not match so it has to be invalid.
+ LLDB_LOG(log, "NativeProcessLinux {0} Invalid TraceID", __FUNCTION__);
+ error.SetError(InvalidTraceID, eErrorTypeGeneric);
+ return error;
+ }
+
+ LLDB_LOG(log, "NativeProcessLinux {0} UID - {1} , Thread -{2}", __FUNCTION__,
+ traceid, thread);
+
+ if (traceid == ProcessorTraceMonitor::GetProcessTraceID()) {
+ // traceid maps to the whole process so we have to erase it from the
+ // thread group.
+ LLDB_LOG(log, "NativeProcessLinux {0} traceid maps to process",
+ __FUNCTION__);
+ m_pt_traced_thread_group.erase(thread);
+ }
+ m_processor_trace_monitor.erase(iter);
+
+ return error;
+}
Index: source/Plugins/Process/Linux/CMakeLists.txt
===================================================================
--- source/Plugins/Process/Linux/CMakeLists.txt
+++ source/Plugins/Process/Linux/CMakeLists.txt
@@ -11,6 +11,7 @@
NativeRegisterContextLinux_mips64.cpp
NativeRegisterContextLinux_s390x.cpp
NativeThreadLinux.cpp
+ ProcessorTrace.cpp
SingleStepCheck.cpp
LINK_LIBS
Index: source/Host/linux/Support.cpp
===================================================================
--- source/Host/linux/Support.cpp
+++ source/Host/linux/Support.cpp
@@ -32,3 +32,13 @@
LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message());
return Ret;
}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+lldb_private::getProcFile(const llvm::Twine &file) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ std::string File = ("/proc/" + file).str();
+ auto Ret = llvm::MemoryBuffer::getFileAsStream(File);
+ if (!Ret)
+ LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message());
+ return Ret;
+}
Index: include/lldb/Host/linux/Support.h
===================================================================
--- include/lldb/Host/linux/Support.h
+++ include/lldb/Host/linux/Support.h
@@ -22,6 +22,9 @@
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getProcFile(::pid_t pid, const llvm::Twine &file);
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+getProcFile(const llvm::Twine &file);
+
} // namespace lldb_private
#endif // #ifndef LLDB_HOST_LINUX_SUPPORT_H
Index: include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- include/lldb/Host/common/NativeProcessProtocol.h
+++ include/lldb/Host/common/NativeProcessProtocol.h
@@ -335,7 +335,7 @@
//------------------------------------------------------------------
/// StopTracing API as the name suggests stops a tracing instance.
///
- /// @param[in] uid
+ /// @param[in] traceid
/// The user id of the trace intended to be stopped. Now a
/// user_id may map to multiple threads in which case this API
/// could be used to stop the tracing for a specific thread by
@@ -348,17 +348,17 @@
/// @return
/// Status indicating what went wrong.
//------------------------------------------------------------------
- virtual Status StopTrace(lldb::user_id_t uid,
+ virtual Status StopTrace(lldb::user_id_t traceid,
lldb::tid_t thread = LLDB_INVALID_THREAD_ID) {
return Status("Not implemented");
}
//------------------------------------------------------------------
/// This API provides the trace data collected in the form of raw
/// data.
///
- /// @param[in] uid thread
- /// The uid and thread provide the context for the trace
+ /// @param[in] traceid thread
+ /// The traceid and thread provide the context for the trace
/// instance.
///
/// @param[in] buffer
@@ -374,7 +374,7 @@
/// @return
/// The size of the data actually read.
//------------------------------------------------------------------
- virtual Status GetData(lldb::user_id_t uid, lldb::tid_t thread,
+ virtual Status GetData(lldb::user_id_t traceid, lldb::tid_t thread,
llvm::MutableArrayRef<uint8_t> &buffer,
size_t offset = 0) {
return Status("Not implemented");
@@ -384,16 +384,16 @@
/// Similar API as above except it aims to provide any extra data
/// useful for decoding the actual trace data.
//------------------------------------------------------------------
- virtual Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread,
+ virtual Status GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread,
llvm::MutableArrayRef<uint8_t> &buffer,
size_t offset = 0) {
return Status("Not implemented");
}
//------------------------------------------------------------------
/// API to query the TraceOptions for a given user id
///
- /// @param[in] uid
+ /// @param[in] traceid
/// The user id of the tracing instance.
///
/// @param[in] config
@@ -407,7 +407,7 @@
/// @param[out] config
/// The actual configuration being used for tracing.
//------------------------------------------------------------------
- virtual Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &config) {
+ virtual Status GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) {
return Status("Not implemented");
}
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits