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
llvm-svn: 306516
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index e39a178..8e37880 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -289,7 +289,8 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ if (found)
+ StopTracingForThread(thread_id);
SignalIfAllThreadsStopped();
return found;
}
@@ -2106,6 +2114,21 @@
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 @@
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;
+}