[Lldb-commits] [lldb] r306516 - Implementation of Intel(R) Processor Trace support for Linux
Ravitheja Addepally via lldb-commits
lldb-commits at lists.llvm.org
Wed Jun 28 00:58:31 PDT 2017
Author: ravitheja
Date: Wed Jun 28 00:58:31 2017
New Revision: 306516
URL: http://llvm.org/viewvc/llvm-project?rev=306516&view=rev
Log:
Implementation of Intel(R) Processor Trace support for Linux
Summary:
This patch implements support for Intel(R) Processor Trace
in lldb server. The changes have support for
starting/stopping and reading the trace data. The code
is only available on Linux versions where the perf
attributes for aux buffers are available.
The patch also consists of Unit tests for testing the
core buffer reading function.
Reviewers: lldb-commits, labath, clayborg, zturner, tberghammer
Reviewed By: labath, clayborg
Subscribers: mgorny
Differential Revision: https://reviews.llvm.org/D33674
Added:
lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.cpp
lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.h
lldb/trunk/unittests/Process/Linux/
lldb/trunk/unittests/Process/Linux/CMakeLists.txt
lldb/trunk/unittests/Process/Linux/ProcessorTraceTest.cpp
Modified:
lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h
lldb/trunk/include/lldb/Host/linux/Support.h
lldb/trunk/source/Host/linux/Support.cpp
lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt
lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h
lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
lldb/trunk/unittests/Process/CMakeLists.txt
Modified: lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h (original)
+++ lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h Wed Jun 28 00:58:31 2017
@@ -333,7 +333,7 @@ public:
//------------------------------------------------------------------
/// 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
@@ -346,7 +346,7 @@ public:
/// @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");
}
@@ -355,8 +355,8 @@ public:
/// 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
@@ -372,7 +372,7 @@ public:
/// @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");
@@ -382,7 +382,7 @@ public:
/// 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");
@@ -391,7 +391,7 @@ public:
//------------------------------------------------------------------
/// 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
@@ -405,7 +405,7 @@ public:
/// @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");
}
Modified: lldb/trunk/include/lldb/Host/linux/Support.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Host/linux/Support.h?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Host/linux/Support.h (original)
+++ lldb/trunk/include/lldb/Host/linux/Support.h Wed Jun 28 00:58:31 2017
@@ -22,6 +22,9 @@ getProcFile(::pid_t pid, ::pid_t tid, co
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
Modified: lldb/trunk/source/Host/linux/Support.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/linux/Support.cpp?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/source/Host/linux/Support.cpp (original)
+++ lldb/trunk/source/Host/linux/Support.cpp Wed Jun 28 00:58:31 2017
@@ -32,3 +32,13 @@ lldb_private::getProcFile(::pid_t pid, c
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;
+}
Modified: lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt (original)
+++ lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt Wed Jun 28 00:58:31 2017
@@ -11,6 +11,7 @@ add_lldb_library(lldbPluginProcessLinux
NativeRegisterContextLinux_mips64.cpp
NativeRegisterContextLinux_s390x.cpp
NativeThreadLinux.cpp
+ ProcessorTrace.cpp
SingleStepCheck.cpp
LINK_LIBS
Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp Wed Jun 28 00:58:31 2017
@@ -289,7 +289,8 @@ Status NativeProcessProtocol::Attach(
NativeProcessLinux::NativeProcessLinux()
: NativeProcessProtocol(LLDB_INVALID_PROCESS_ID), m_arch(),
m_supports_mem_region(eLazyBoolCalculate), m_mem_region_cache(),
- m_pending_notification_tid(LLDB_INVALID_THREAD_ID) {}
+ m_pending_notification_tid(LLDB_INVALID_THREAD_ID),
+ m_pt_proces_trace_id(LLDB_INVALID_UID) {}
void NativeProcessLinux::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid,
Status &error) {
@@ -580,6 +581,7 @@ void NativeProcessLinux::MonitorCallback
info.si_pid);
auto thread_sp = AddThread(pid);
+
// Resume the newly created thread.
ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
ThreadWasCreated(*thread_sp);
@@ -691,6 +693,7 @@ void NativeProcessLinux::WaitForNewThrea
LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid);
new_thread_sp = AddThread(tid);
+
ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
ThreadWasCreated(*new_thread_sp);
}
@@ -1301,6 +1304,9 @@ Status NativeProcessLinux::Detach() {
e; // Save the error, but still attempt to detach from other threads.
}
+ m_processor_trace_monitor.clear();
+ m_pt_proces_trace_id = LLDB_INVALID_UID;
+
return error;
}
@@ -2089,6 +2095,8 @@ bool NativeProcessLinux::StopTrackingThr
}
}
+ if (found)
+ StopTracingForThread(thread_id);
SignalIfAllThreadsStopped();
return found;
}
@@ -2106,6 +2114,21 @@ NativeThreadLinuxSP NativeProcessLinux::
auto thread_sp = std::make_shared<NativeThreadLinux>(this, thread_id);
m_threads.push_back(thread_sp);
+
+ if (m_pt_proces_trace_id != LLDB_INVALID_UID) {
+ auto traceMonitor = ProcessorTraceMonitor::Create(
+ GetID(), thread_id, m_pt_process_trace_config, true);
+ if (traceMonitor) {
+ m_pt_traced_thread_group.insert(thread_id);
+ m_processor_trace_monitor.insert(
+ std::make_pair(thread_id, std::move(*traceMonitor)));
+ } else {
+ LLDB_LOG(log, "failed to start trace on thread {0}", thread_id);
+ Status error(traceMonitor.takeError());
+ LLDB_LOG(log, "error {0}", error);
+ }
+ }
+
return thread_sp;
}
@@ -2405,3 +2428,258 @@ Status NativeProcessLinux::PtraceWrapper
return error;
}
+
+llvm::Expected<ProcessorTraceMonitor &>
+NativeProcessLinux::LookupProcessorTraceInstance(lldb::user_id_t traceid,
+ lldb::tid_t thread) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ if (thread == LLDB_INVALID_THREAD_ID && traceid == m_pt_proces_trace_id) {
+ LLDB_LOG(log, "thread not specified: {0}", traceid);
+ return Status("tracing not active thread not specified").ToError();
+ }
+
+ 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, "traceid not being traced: {0}", traceid);
+ return Status("tracing not active for this thread").ToError();
+}
+
+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, "traceid {0}", traceid);
+
+ auto perf_monitor = LookupProcessorTraceInstance(traceid, thread);
+ if (!perf_monitor) {
+ LLDB_LOG(log, "traceid not being traced: {0}", traceid);
+ buffer = buffer.slice(buffer.size());
+ error = perf_monitor.takeError();
+ 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) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+
+ LLDB_LOG(log, "traceid {0}", traceid);
+
+ auto perf_monitor = LookupProcessorTraceInstance(traceid, thread);
+ if (!perf_monitor) {
+ LLDB_LOG(log, "traceid not being traced: {0}", traceid);
+ buffer = buffer.slice(buffer.size());
+ error = perf_monitor.takeError();
+ 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 &&
+ m_pt_proces_trace_id == traceid) {
+ if (m_pt_proces_trace_id == LLDB_INVALID_UID) {
+ error.SetErrorString("tracing not active for this process");
+ return error;
+ }
+ config = m_pt_process_trace_config;
+ } else {
+ auto perf_monitor =
+ LookupProcessorTraceInstance(traceid, config.getThreadID());
+ if (!perf_monitor) {
+ error = perf_monitor.takeError();
+ return error;
+ }
+ error = (*perf_monitor).GetTraceConfig(config);
+ }
+ return error;
+}
+
+lldb::user_id_t
+NativeProcessLinux::StartTraceGroup(const TraceOptions &config,
+ Status &error) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ if (config.getType() != TraceType::eTraceTypeProcessorTrace)
+ return LLDB_INVALID_UID;
+
+ if (m_pt_proces_trace_id != LLDB_INVALID_UID) {
+ error.SetErrorString("tracing already active on this process");
+ return m_pt_proces_trace_id;
+ }
+
+ for (const auto &thread_sp : m_threads) {
+ if (auto traceInstance = ProcessorTraceMonitor::Create(
+ GetID(), thread_sp->GetID(), config, true)) {
+ m_pt_traced_thread_group.insert(thread_sp->GetID());
+ m_processor_trace_monitor.insert(
+ std::make_pair(thread_sp->GetID(), std::move(*traceInstance)));
+ }
+ }
+
+ m_pt_process_trace_config = config;
+ error = ProcessorTraceMonitor::GetCPUType(m_pt_process_trace_config);
+
+ // Trace on Complete process will have traceid of 0
+ m_pt_proces_trace_id = 0;
+
+ LLDB_LOG(log, "Process Trace ID {0}", m_pt_proces_trace_id);
+ return m_pt_proces_trace_id;
+}
+
+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::tid_t threadid = config.getThreadID();
+
+ if (threadid == LLDB_INVALID_THREAD_ID)
+ return StartTraceGroup(config, error);
+
+ auto thread_sp = GetThreadByID(threadid);
+ if (!thread_sp) {
+ // Thread not tracked by lldb so don't trace.
+ error.SetErrorString("invalid thread id");
+ return LLDB_INVALID_UID;
+ }
+
+ const auto &iter = m_processor_trace_monitor.find(threadid);
+ if (iter != m_processor_trace_monitor.end()) {
+ LLDB_LOG(log, "Thread already being traced");
+ error.SetErrorString("tracing already active on this thread");
+ return LLDB_INVALID_UID;
+ }
+
+ auto traceMonitor =
+ ProcessorTraceMonitor::Create(GetID(), threadid, config, false);
+ if (!traceMonitor) {
+ error = traceMonitor.takeError();
+ LLDB_LOG(log, "error {0}", error);
+ return LLDB_INVALID_UID;
+ }
+ lldb::user_id_t ret_trace_id = (*traceMonitor)->GetTraceID();
+ m_processor_trace_monitor.insert(
+ std::make_pair(threadid, std::move(*traceMonitor)));
+ return ret_trace_id;
+}
+
+Status NativeProcessLinux::StopTracingForThread(lldb::tid_t thread) {
+ Status error;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ LLDB_LOG(log, "Thread {0}", thread);
+
+ const auto& iter = m_processor_trace_monitor.find(thread);
+ if (iter == m_processor_trace_monitor.end()) {
+ error.SetErrorString("tracing not active for this thread");
+ return error;
+ }
+
+ if (iter->second->GetTraceID() == m_pt_proces_trace_id) {
+ // traceid maps to the whole process so we have to erase it from the
+ // thread group.
+ LLDB_LOG(log, "traceid maps to process");
+ m_pt_traced_thread_group.erase(thread);
+ }
+ m_processor_trace_monitor.erase(iter);
+
+ 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 == m_pt_proces_trace_id &&
+ thread == LLDB_INVALID_THREAD_ID)
+ StopProcessorTracingOnProcess();
+ else
+ error = StopProcessorTracingOnThread(traceid, thread);
+ break;
+ default:
+ error.SetErrorString("trace not supported");
+ break;
+ }
+
+ return error;
+}
+
+void NativeProcessLinux::StopProcessorTracingOnProcess() {
+ for (auto thread_id_iter : m_pt_traced_thread_group)
+ m_processor_trace_monitor.erase(thread_id_iter);
+ m_pt_traced_thread_group.clear();
+ m_pt_proces_trace_id = LLDB_INVALID_UID;
+}
+
+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, "Trace ID {0}", iter.second->GetTraceID());
+ }
+
+ LLDB_LOG(log, "Invalid TraceID");
+ error.SetErrorString("invalid trace id");
+ return error;
+ }
+
+ // thread is specified so we can use find function on the map.
+ const auto& iter = m_processor_trace_monitor.find(thread);
+ if (iter == m_processor_trace_monitor.end()) {
+ // thread not found in our map.
+ LLDB_LOG(log, "thread not being traced");
+ error.SetErrorString("tracing not active for this thread");
+ return error;
+ }
+ if (iter->second->GetTraceID() != traceid) {
+ // traceid did not match so it has to be invalid.
+ LLDB_LOG(log, "Invalid TraceID");
+ error.SetErrorString("invalid trace id");
+ return error;
+ }
+
+ LLDB_LOG(log, "UID - {0} , Thread -{1}", traceid, thread);
+
+ if (traceid == m_pt_proces_trace_id) {
+ // traceid maps to the whole process so we have to erase it from the
+ // thread group.
+ LLDB_LOG(log, "traceid maps to process");
+ m_pt_traced_thread_group.erase(thread);
+ }
+ m_processor_trace_monitor.erase(iter);
+
+ return error;
+}
Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h Wed Jun 28 00:58:31 2017
@@ -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 @@ public:
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) 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,43 @@ private:
void SigchldHandler();
Status PopulateMemoryRegionCache();
+
+ lldb::user_id_t StartTraceGroup(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.
+ llvm::Expected<ProcessorTraceMonitor &>
+ LookupProcessorTraceInstance(lldb::user_id_t traceid, lldb::tid_t thread);
+
+ // 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.
+ void StopProcessorTracingOnProcess();
+
+ llvm::DenseMap<lldb::tid_t, ProcessorTraceMonitorUP>
+ 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;
+
+ lldb::user_id_t m_pt_proces_trace_id;
+ TraceOptions m_pt_process_trace_config;
};
} // namespace process_linux
Added: lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.cpp?rev=306516&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.cpp (added)
+++ lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.cpp Wed Jun 28 00:58:31 2017
@@ -0,0 +1,400 @@
+//===-- ProcessorTrace.cpp ------------------------------------ -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <fstream>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "ProcessorTrace.h"
+#include "lldb/Host/linux/Support.h"
+
+#include <sys/syscall.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace process_linux;
+using namespace llvm;
+
+lldb::user_id_t ProcessorTraceMonitor::m_trace_num = 1;
+
+Status ProcessorTraceMonitor::GetTraceConfig(TraceOptions &config) const {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+ Status error;
+
+ config.setType(lldb::TraceType::eTraceTypeProcessorTrace);
+ config.setMetaDataBufferSize(m_mmap_meta->data_size);
+
+ config.setTraceBufferSize(m_mmap_meta->aux_size);
+
+ error = GetCPUType(config);
+
+ return error;
+#endif
+}
+
+Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ const TraceOptions &config) {
+ Status error;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ LLDB_LOG(log, "{0}", config.getThreadID());
+
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+
+ LLDB_LOG(log, "called thread id {0}", tid);
+ uint64_t page_size = getpagesize();
+ uint64_t bufsize = config.getTraceBufferSize();
+ uint64_t metabufsize = config.getMetaDataBufferSize();
+
+ uint64_t numpages = static_cast<uint64_t>(
+ llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
+ numpages = std::max(1ul, numpages);
+ bufsize = page_size * numpages;
+
+ numpages = static_cast<uint64_t>(
+ llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
+ numpages = std::max(0ul, numpages);
+ metabufsize = page_size * numpages;
+
+ perf_event_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.size = sizeof(attr);
+ attr.exclude_kernel = 1;
+ attr.sample_type = PERF_SAMPLE_TIME;
+ attr.sample_id_all = 1;
+ attr.exclude_hv = 1;
+ attr.exclude_idle = 1;
+ attr.mmap = 1;
+
+ int intel_pt_type = 0;
+
+ auto ret = llvm::MemoryBuffer::getFileAsStream(
+ "/sys/bus/event_source/devices/intel_pt/type");
+ if (!ret) {
+ LLDB_LOG(log, "failed to open Config file");
+ return ret.getError();
+ }
+
+ StringRef rest = ret.get()->getBuffer();
+ if (rest.empty() || rest.trim().getAsInteger(10, intel_pt_type)) {
+ LLDB_LOG(log, "failed to read Config file");
+ error.SetErrorString("invalid file");
+ return error;
+ }
+
+ rest.trim().getAsInteger(10, intel_pt_type);
+ LLDB_LOG(log, "intel pt type {0}", intel_pt_type);
+ attr.type = intel_pt_type;
+
+ LLDB_LOG(log, "meta buffer size {0}", metabufsize);
+ LLDB_LOG(log, "buffer size {0} ", bufsize);
+
+ if (error.Fail()) {
+ LLDB_LOG(log, "Status in custom config");
+
+ return error;
+ }
+
+ errno = 0;
+ auto fd =
+ syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
+ if (fd == -1) {
+ LLDB_LOG(log, "syscall error {0}", errno);
+ error.SetErrorString("perf event syscall Failed");
+ return error;
+ }
+
+ m_fd = std::move(std::unique_ptr<int, file_close>(new int(fd), file_close()));
+
+ errno = 0;
+ auto base =
+ mmap(NULL, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (base == MAP_FAILED) {
+ LLDB_LOG(log, "mmap base error {0}", errno);
+ error.SetErrorString("Meta buffer allocation failed");
+ return error;
+ }
+
+ m_mmap_meta = std::move(std::unique_ptr<perf_event_mmap_page, munmap_delete>(
+ reinterpret_cast<perf_event_mmap_page *>(base),
+ munmap_delete(metabufsize + page_size)));
+
+ m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
+ m_mmap_meta->aux_size = bufsize;
+
+ errno = 0;
+ auto mmap_aux = mmap(NULL, bufsize, PROT_READ, MAP_SHARED, fd,
+ static_cast<long int>(m_mmap_meta->aux_offset));
+
+ if (mmap_aux == MAP_FAILED) {
+ LLDB_LOG(log, "second mmap done {0}", errno);
+ error.SetErrorString("Trace buffer allocation failed");
+ return error;
+ }
+ m_mmap_aux = std::move(std::unique_ptr<uint8_t, munmap_delete>(
+ reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize)));
+#endif
+ return error;
+}
+
+llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetDataBuffer() {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+ return MutableArrayRef<uint8_t>(
+ (reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
+ m_mmap_meta->data_offset),
+ m_mmap_meta->data_size);
+#endif
+}
+
+llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetAuxBuffer() {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+ return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
+#endif
+}
+
+Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
+
+ Status error;
+ uint64_t cpu_family = -1;
+ uint64_t model = -1;
+ uint64_t stepping = -1;
+ std::string vendor_id;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ auto BufferOrError = getProcFile("cpuinfo");
+ if (!BufferOrError)
+ return BufferOrError.getError();
+
+ LLDB_LOG(log, "GetCPUType Function");
+
+ StringRef Rest = BufferOrError.get()->getBuffer();
+ while (!Rest.empty()) {
+ StringRef Line;
+ std::tie(Line, Rest) = Rest.split('\n');
+
+ SmallVector<StringRef, 2> columns;
+ Line.split(columns, StringRef(":"), -1, false);
+
+ if (columns.size() < 2)
+ continue; // continue searching
+
+ columns[1] = columns[1].trim(" ");
+ if (columns[0].contains("cpu family") &&
+ columns[1].getAsInteger(10, cpu_family))
+ continue;
+
+ else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
+ continue;
+
+ else if (columns[0].contains("stepping") &&
+ columns[1].getAsInteger(10, stepping))
+ continue;
+
+ else if (columns[0].contains("vendor_id")) {
+ vendor_id = columns[1].str();
+ if (!vendor_id.empty())
+ continue;
+ }
+ LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id);
+
+ if ((cpu_family != static_cast<uint64_t>(-1)) &&
+ (model != static_cast<uint64_t>(-1)) &&
+ (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
+ auto params_dict = std::make_shared<StructuredData::Dictionary>();
+ params_dict->AddIntegerItem("cpu_family", cpu_family);
+ params_dict->AddIntegerItem("cpu_model", model);
+ params_dict->AddIntegerItem("cpu_stepping", stepping);
+ params_dict->AddStringItem("cpu_vendor", vendor_id);
+
+ llvm::StringRef intel_custom_params_key("intel-pt");
+
+ auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
+ intel_custom_params->AddItem(
+ intel_custom_params_key,
+ StructuredData::ObjectSP(std::move(params_dict)));
+
+ config.setTraceParams(intel_custom_params);
+ return error; // we are done
+ }
+ }
+
+ error.SetErrorString("cpu info not found");
+ return error;
+}
+
+llvm::Expected<ProcessorTraceMonitorUP>
+ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
+ const TraceOptions &config,
+ bool useProcessSettings) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ Status error;
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ error.SetErrorString("thread not specified");
+ return std::move(error.ToError());
+ }
+
+ ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
+
+ error = pt_monitor_up->StartTrace(pid, tid, config);
+ if (error.Fail())
+ return std::move(error.ToError());
+
+ pt_monitor_up->SetThreadID(tid);
+
+ if (useProcessSettings) {
+ pt_monitor_up->SetTraceID(0);
+ } else {
+ pt_monitor_up->SetTraceID(m_trace_num++);
+ LLDB_LOG(log, "Trace ID {0}", m_trace_num);
+ }
+ return std::move(pt_monitor_up);
+}
+
+Status
+ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+ uint64_t head = m_mmap_meta->aux_head;
+
+ LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
+
+ /**
+ * When configured as ring buffer, the aux buffer keeps wrapping around
+ * the buffer and its not possible to detect how many times the buffer
+ * wrapped. Initially the buffer is filled with zeros,as shown below
+ * so in order to get complete buffer we first copy firstpartsize, followed
+ * by any left over part from beginning to aux_head
+ *
+ * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
+ * aux_head->||<- firstpartsize ->|
+ *
+ * */
+
+ ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
+ LLDB_LOG(log, "ReadCyclic BUffer Done");
+ return error;
+#endif
+}
+
+Status
+ProcessorTraceMonitor::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ uint64_t bytes_remaining = buffer.size();
+ Status error;
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+
+ uint64_t head = m_mmap_meta->data_head;
+
+ /*
+ * The data buffer and aux buffer have different implementations
+ * with respect to their definition of head pointer. In the case
+ * of Aux data buffer the head always wraps around the aux buffer
+ * and we don't need to care about it, whereas the data_head keeps
+ * increasing and needs to be wrapped by modulus operator
+ */
+
+ LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
+
+ auto data_buffer = GetDataBuffer();
+
+ if (head > data_buffer.size()) {
+ head = head % data_buffer.size();
+ LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
+
+ ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
+ bytes_remaining -= buffer.size();
+ } else {
+ LLDB_LOG(log, "Head - {0}", head);
+ if (offset >= head) {
+ LLDB_LOG(log, "Invalid Offset ");
+ error.SetErrorString("invalid offset");
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+
+ auto data = data_buffer.slice(offset, (head - offset));
+ auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
+ bytes_remaining -= (remaining - buffer.begin());
+ }
+ buffer = buffer.drop_back(bytes_remaining);
+ return error;
+#endif
+}
+
+void ProcessorTraceMonitor::ReadCyclicBuffer(
+ llvm::MutableArrayRef<uint8_t> &dst, llvm::MutableArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (dst.empty() || src.empty()) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (dst.data() == nullptr || src.data() == nullptr) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (src_cyc_index > src.size()) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (offset >= src.size()) {
+ LLDB_LOG(log, "Too Big offset ");
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
+ src.slice(src_cyc_index), src.take_front(src_cyc_index)};
+
+ if (offset > parts[0].size()) {
+ parts[1] = parts[1].slice(offset - parts[0].size());
+ parts[0] = parts[0].drop_back(parts[0].size());
+ } else if (offset == parts[0].size()) {
+ parts[0] = parts[0].drop_back(parts[0].size());
+ } else {
+ parts[0] = parts[0].slice(offset);
+ }
+ auto next = dst.begin();
+ auto bytes_left = dst.size();
+ for (auto part : parts) {
+ size_t chunk_size = std::min(part.size(), bytes_left);
+ next = std::copy_n(part.begin(), chunk_size, next);
+ bytes_left -= chunk_size;
+ }
+ dst = dst.drop_back(bytes_left);
+}
Added: lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.h?rev=306516&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.h (added)
+++ lldb/trunk/source/Plugins/Process/Linux/ProcessorTrace.h Wed Jun 28 00:58:31 2017
@@ -0,0 +1,141 @@
+//===-- 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"
+
+#include <linux/perf_event.h>
+#include <sys/mman.h>
+
+namespace lldb_private {
+
+namespace process_linux {
+
+// ---------------------------------------------------------------------
+// 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::unique_ptr<ProcessorTraceMonitor> ProcessorTraceMonitorUP;
+
+class ProcessorTraceMonitor {
+
+ class munmap_delete {
+ size_t m_length;
+
+ public:
+ munmap_delete(size_t length) : m_length(length) {}
+ void operator()(void *ptr) {
+ if (m_length)
+ munmap(ptr, m_length);
+ }
+ };
+
+ class file_close {
+
+ public:
+ file_close() = default;
+ void operator()(int *ptr) {
+ if (ptr == nullptr)
+ return;
+ if (*ptr == -1)
+ return;
+ close(*ptr);
+ std::default_delete<int>()(ptr);
+ }
+ };
+
+ std::unique_ptr<perf_event_mmap_page, munmap_delete> m_mmap_meta;
+ std::unique_ptr<uint8_t, munmap_delete> m_mmap_aux;
+ std::unique_ptr<int, file_close> m_fd;
+
+ // perf_event_mmap_page *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;
+
+ void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; }
+
+ Status StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ const TraceOptions &config);
+
+ llvm::MutableArrayRef<uint8_t> GetAuxBuffer();
+ llvm::MutableArrayRef<uint8_t> GetDataBuffer();
+
+ ProcessorTraceMonitor()
+ : m_mmap_meta(nullptr, munmap_delete(0)),
+ m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()),
+ m_traceid(LLDB_INVALID_UID), m_thread_id(LLDB_INVALID_THREAD_ID){};
+
+ void SetThreadID(lldb::tid_t tid) { m_thread_id = tid; }
+
+public:
+ static Status GetCPUType(TraceOptions &config);
+
+ static llvm::Expected<ProcessorTraceMonitorUP>
+ 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);
+
+ ~ProcessorTraceMonitor() = default;
+
+ lldb::tid_t GetThreadID() const { return m_thread_id; }
+
+ lldb::user_id_t GetTraceID() const { return m_traceid; }
+
+ Status GetTraceConfig(TraceOptions &config) 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::MutableArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset);
+};
+} // namespace process_linux
+} // namespace lldb_private
+#endif
\ No newline at end of file
Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp Wed Jun 28 00:58:31 2017
@@ -1250,9 +1250,9 @@ GDBRemoteCommunicationServerLLGS::Handle
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());
@@ -1286,8 +1286,8 @@ GDBRemoteCommunicationServerLLGS::Handle
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());
Modified: lldb/trunk/unittests/Process/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/CMakeLists.txt?rev=306516&r1=306515&r2=306516&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/CMakeLists.txt (original)
+++ lldb/trunk/unittests/Process/CMakeLists.txt Wed Jun 28 00:58:31 2017
@@ -1,2 +1,3 @@
add_subdirectory(gdb-remote)
+add_subdirectory(Linux)
add_subdirectory(minidump)
Added: lldb/trunk/unittests/Process/Linux/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/Linux/CMakeLists.txt?rev=306516&view=auto
==============================================================================
--- lldb/trunk/unittests/Process/Linux/CMakeLists.txt (added)
+++ lldb/trunk/unittests/Process/Linux/CMakeLists.txt Wed Jun 28 00:58:31 2017
@@ -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
Added: lldb/trunk/unittests/Process/Linux/ProcessorTraceTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/Linux/ProcessorTraceTest.cpp?rev=306516&view=auto
==============================================================================
--- lldb/trunk/unittests/Process/Linux/ProcessorTraceTest.cpp (added)
+++ lldb/trunk/unittests/Process/Linux/ProcessorTraceTest.cpp Wed Jun 28 00:58:31 2017
@@ -0,0 +1,152 @@
+//===-- 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 "ProcessorTrace.h"
+#include "llvm/ADT/ArrayRef.h"
+// C Includes
+
+// C++ Includes
+
+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::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] = {};
+
+ // 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));
+ }
+}
More information about the lldb-commits
mailing list