diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.h b/lldb/source/Plugins/Process/Linux/IntelPTCollector.h --- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.h @@ -69,26 +69,14 @@ llvm::Error TraceStart(lldb::tid_t tid, const TraceIntelPTStartRequest &request); - llvm::Expected GetTracedThread(lldb::tid_t tid); - - bool IsProcessTracingEnabled() const; - - void ClearProcessTracing(); - + /// The target process. NativeProcessProtocol &m_process; /// Threads traced due to "thread tracing" IntelPTThreadTraceCollection m_thread_traces; - /// Only one of the following "process tracing" handlers can be active at a - /// given time. - /// - /// \{ - /// Threads traced due to per-thread "process tracing". This might be \b - /// nullptr. - IntelPTPerThreadProcessTraceUP m_per_thread_process_trace_up; - /// Cores traced due to per-core "process tracing". This might be \b nullptr. - IntelPTMultiCoreTraceUP m_per_core_process_trace_up; - /// \} + /// Only one instance of "process trace" can be active at a given time. + /// It might be \b nullptr. + IntelPTProcessTraceUP m_process_trace_up; /// TSC to wall time conversion. TraceTscConversionUP m_tsc_conversion; diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp --- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp +++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp @@ -44,13 +44,8 @@ } Error IntelPTCollector::TraceStop(lldb::tid_t tid) { - if (m_per_thread_process_trace_up && - m_per_thread_process_trace_up->TracesThread(tid)) - return m_per_thread_process_trace_up->TraceStop(tid); - else if (m_per_core_process_trace_up) - return createStringError(inconvertibleErrorCode(), - "Can't stop tracing an individual thread when " - "per-core process tracing is enabled."); + if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) + return m_process_trace_up->TraceStop(tid); return m_thread_traces.TraceStop(tid); } @@ -67,23 +62,9 @@ } } -Expected -IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request, - ArrayRef current_tids) { - IntelPTPerThreadProcessTraceUP trace( - new IntelPTPerThreadProcessTrace(request)); - - Error error = Error::success(); - for (lldb::tid_t tid : current_tids) - error = joinErrors(std::move(error), trace->TraceStart(tid)); - if (error) - return std::move(error); - return std::move(trace); -} - Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { if (request.IsProcessTracing()) { - if (IsProcessTracingEnabled()) { + if (m_process_trace_up) { return createStringError( inconvertibleErrorCode(), "Process currently traced. Stop process tracing first"); @@ -93,9 +74,9 @@ return createStringError( inconvertibleErrorCode(), "Threads currently traced. Stop tracing them first."); - if (Expected trace = - IntelPTMultiCoreTrace::StartOnAllCores(request)) { - m_per_core_process_trace_up = std::move(*trace); + if (Expected trace = + IntelPTMultiCoreTrace::StartOnAllCores(request, m_process)) { + m_process_trace_up = std::move(*trace); return Error::success(); } else { return trace.takeError(); @@ -106,9 +87,9 @@ process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID()); // per-thread process tracing - if (Expected trace = + if (Expected trace = IntelPTPerThreadProcessTrace::Start(request, process_threads)) { - m_per_thread_process_trace_up = std::move(trace.get()); + m_process_trace_up = std::move(trace.get()); return Error::success(); } else { return trace.takeError(); @@ -116,35 +97,40 @@ } } else { // individual thread tracing - if (m_per_core_process_trace_up) - return createStringError(inconvertibleErrorCode(), - "Process currently traced with per-core " - "tracing. Stop process tracing first"); - Error error = Error::success(); - for (int64_t tid : *request.tids) - error = joinErrors(std::move(error), - m_thread_traces.TraceStart(tid, request)); + for (int64_t tid : *request.tids) { + if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) + error = joinErrors( + std::move(error), + createStringError(inconvertibleErrorCode(), + formatv("Thread with tid {0} is currently " + "traced. Stop tracing it first.", + tid) + .str() + .c_str())); + else + error = joinErrors(std::move(error), + m_thread_traces.TraceStart(tid, request)); + } return error; } } void IntelPTCollector::OnProcessStateChanged(lldb::StateType state) { - if (m_per_core_process_trace_up) - m_per_core_process_trace_up->OnProcessStateChanged(state); + if (m_process_trace_up) + m_process_trace_up->OnProcessStateChanged(state); } Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { - if (m_per_thread_process_trace_up) - return m_per_thread_process_trace_up->TraceStart(tid); + if (m_process_trace_up) + return m_process_trace_up->TraceStart(tid); return Error::success(); } Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { - if (m_per_thread_process_trace_up && - m_per_thread_process_trace_up->TracesThread(tid)) - return m_per_thread_process_trace_up->TraceStop(tid); + if (m_process_trace_up && m_process_trace_up->TracesThread(tid)) + return m_process_trace_up->TraceStop(tid); else if (m_thread_traces.TracesThread(tid)) return m_thread_traces.TraceStop(tid); return Error::success(); @@ -156,6 +142,9 @@ return cpu_info.takeError(); TraceGetStateResponse state; + if (m_process_trace_up) + state = m_process_trace_up->GetState(); + state.process_binary_data.push_back( {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); @@ -165,48 +154,22 @@ {{IntelPTDataKinds::kTraceBuffer, thread_trace.GetTraceBufferSize()}}}); }); - - if (m_per_thread_process_trace_up) { - m_per_thread_process_trace_up->GetThreadTraces().ForEachThread( - [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { - state.traced_threads.push_back( - {tid, - {{IntelPTDataKinds::kTraceBuffer, - thread_trace.GetTraceBufferSize()}}}); - }); - } - - if (m_per_core_process_trace_up) { - for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) - state.traced_threads.push_back( - TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}}); - - state.cores.emplace(); - m_per_core_process_trace_up->ForEachCore( - [&](lldb::core_id_t core_id, - const IntelPTSingleBufferTrace &core_trace) { - state.cores->push_back({core_id, - {{IntelPTDataKinds::kTraceBuffer, - core_trace.GetTraceBufferSize()}}}); - }); - } return toJSON(state); } -Expected -IntelPTCollector::GetTracedThread(lldb::tid_t tid) { - if (m_per_thread_process_trace_up && - m_per_thread_process_trace_up->TracesThread(tid)) - return m_per_thread_process_trace_up->GetThreadTraces().GetTracedThread( - tid); - return m_thread_traces.GetTracedThread(tid); -} - Expected> IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) { if (request.kind == IntelPTDataKinds::kTraceBuffer) { + if (!request.tid) + return createStringError( + inconvertibleErrorCode(), + "Getting a trace buffer without a tid is currently unsupported"); + + if (m_process_trace_up && m_process_trace_up->TracesThread(*request.tid)) + return m_process_trace_up->GetBinaryData(request); + if (Expected trace = - GetTracedThread(*request.tid)) + m_thread_traces.GetTracedThread(*request.tid)) return trace->GetTraceBuffer(request.offset, request.size); else return trace.takeError(); @@ -218,11 +181,6 @@ request.kind.c_str()); } -void IntelPTCollector::ClearProcessTracing() { - m_per_thread_process_trace_up.reset(); - m_per_core_process_trace_up.reset(); -} - bool IntelPTCollector::IsSupported() { if (Expected intel_pt_type = GetIntelPTOSEventType()) { return true; @@ -232,12 +190,7 @@ } } -bool IntelPTCollector::IsProcessTracingEnabled() const { - return (bool)m_per_thread_process_trace_up || - (bool)m_per_core_process_trace_up; -} - void IntelPTCollector::Clear() { - ClearProcessTracing(); + m_process_trace_up.reset(); m_thread_traces.Clear(); } diff --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h --- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h @@ -9,8 +9,10 @@ #ifndef liblldb_IntelPTMultiCoreTrace_H_ #define liblldb_IntelPTMultiCoreTrace_H_ +#include "IntelPTProcessTrace.h" #include "IntelPTSingleBufferTrace.h" +#include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "lldb/lldb-types.h" @@ -21,21 +23,22 @@ namespace lldb_private { namespace process_linux { -class IntelPTMultiCoreTrace; -using IntelPTMultiCoreTraceUP = std::unique_ptr; - -class IntelPTMultiCoreTrace { +class IntelPTMultiCoreTrace : public IntelPTProcessTrace { public: /// Start tracing all CPU cores. /// /// \param[in] request /// Intel PT configuration parameters. /// + /// \param[in] process + /// The process being debugged. + /// /// \return /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or /// an \a llvm::Error otherwise. - static llvm::Expected - StartOnAllCores(const TraceIntelPTStartRequest &request); + static llvm::Expected + StartOnAllCores(const TraceIntelPTStartRequest &request, + NativeProcessProtocol &process); /// Execute the provided callback on each core that is being traced. /// @@ -48,27 +51,34 @@ IntelPTSingleBufferTrace &core_trace)> callback); - /// This method should be invoked as early as possible whenever the process - /// resumes or stops so that intel-pt collection is not enabled when - /// the process is not running. This is done to prevent polluting the core - /// traces with executions of unrelated processes, which increases the data - /// loss of the target process, given that core traces don't filter by - /// process. - /// A possible way to avoid this is to use CR3 filtering, which is equivalent - /// to process filtering, but the perf_event API doesn't support it. - void OnProcessStateChanged(lldb::StateType state); + void OnProcessStateChanged(lldb::StateType state) override; + + TraceGetStateResponse GetState() override; + + bool TracesThread(lldb::tid_t tid) const override; + + llvm::Error TraceStart(lldb::tid_t tid) override; + + llvm::Error TraceStop(lldb::tid_t tid) override; + + llvm::Expected> + GetBinaryData(const TraceGetBinaryDataRequest &request) override; private: IntelPTMultiCoreTrace( llvm::DenseMap - &&traces_per_core) - : m_traces_per_core(std::move(traces_per_core)) {} + &&traces_per_core, + NativeProcessProtocol &process) + : m_traces_per_core(std::move(traces_per_core)), m_process(process) {} llvm::DenseMap m_traces_per_core; /// The initial state is stopped because tracing can only start when the /// process is paused. lldb::StateType m_process_state = lldb::StateType::eStateStopped; + + /// The target process. + NativeProcessProtocol &m_process; }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp --- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp +++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp @@ -33,8 +33,9 @@ toString(std::move(error)).c_str()); } -Expected IntelPTMultiCoreTrace::StartOnAllCores( - const TraceIntelPTStartRequest &request) { +Expected +IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request, + NativeProcessProtocol &process) { Expected> core_ids = GetAvailableLogicalCoreIDs(); if (!core_ids) return core_ids.takeError(); @@ -55,7 +56,8 @@ return IncludePerfEventParanoidMessageInError(core_trace.takeError()); } - return IntelPTMultiCoreTraceUP(new IntelPTMultiCoreTrace(std::move(buffers))); + return IntelPTProcessTraceUP( + new IntelPTMultiCoreTrace(std::move(buffers), process)); } void IntelPTMultiCoreTrace::ForEachCore( @@ -94,3 +96,42 @@ break; } } + +TraceGetStateResponse IntelPTMultiCoreTrace::GetState() { + TraceGetStateResponse state; + + for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) + state.traced_threads.push_back( + TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}}); + + state.cores.emplace(); + ForEachCore([&](lldb::core_id_t core_id, + const IntelPTSingleBufferTrace &core_trace) { + state.cores->push_back( + {core_id, + {{IntelPTDataKinds::kTraceBuffer, core_trace.GetTraceBufferSize()}}}); + }); + + return state; +} + +bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const { + // All the process' threads are being traced automatically. + return (bool)m_process.GetThreadByID(tid); +} + +llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) { + // This instance is already tracing all threads automatically. + return llvm::Error::success(); +} + +Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) { + return createStringError(inconvertibleErrorCode(), + "Can't stop tracing an individual thread when " + "per-core process tracing is enabled."); +} + +Expected> +IntelPTMultiCoreTrace::GetBinaryData(const TraceGetBinaryDataRequest &request) { + return createStringError(inconvertibleErrorCode(), "Unimplemented"); +} diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h --- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h @@ -9,18 +9,15 @@ #ifndef liblldb_IntelPTPerThreadProcessTrace_H_ #define liblldb_IntelPTPerThreadProcessTrace_H_ +#include "IntelPTProcessTrace.h" #include "IntelPTSingleBufferTrace.h" #include "IntelPTThreadTraceCollection.h" namespace lldb_private { namespace process_linux { -class IntelPTPerThreadProcessTrace; -using IntelPTPerThreadProcessTraceUP = - std::unique_ptr; - /// Manages a "process trace" instance by tracing each thread individually. -class IntelPTPerThreadProcessTrace { +class IntelPTPerThreadProcessTrace : public IntelPTProcessTrace { public: /// Start tracing the current process by tracing each of its tids /// individually. @@ -35,19 +32,20 @@ /// \return /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or /// an \a llvm::Error otherwise. - static llvm::Expected + static llvm::Expected Start(const TraceIntelPTStartRequest &request, llvm::ArrayRef current_tids); - bool TracesThread(lldb::tid_t tid) const; + bool TracesThread(lldb::tid_t tid) const override; + + llvm::Error TraceStart(lldb::tid_t tid) override; - IntelPTThreadTraceCollection &GetThreadTraces(); + llvm::Error TraceStop(lldb::tid_t tid) override; - /// \copydoc IntelPTThreadTraceCollection::TraceStart() - llvm::Error TraceStart(lldb::tid_t tid); + TraceGetStateResponse GetState() override; - /// \copydoc IntelPTThreadTraceCollection::TraceStop() - llvm::Error TraceStop(lldb::tid_t tid); + llvm::Expected> + GetBinaryData(const TraceGetBinaryDataRequest &request) override; private: IntelPTPerThreadProcessTrace(const TraceIntelPTStartRequest &request) diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp --- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp +++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp @@ -35,6 +35,35 @@ return m_thread_traces.TraceStart(tid, m_tracing_params); } -IntelPTThreadTraceCollection &IntelPTPerThreadProcessTrace::GetThreadTraces() { - return m_thread_traces; +TraceGetStateResponse IntelPTPerThreadProcessTrace::GetState() { + TraceGetStateResponse state; + m_thread_traces.ForEachThread( + [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { + state.traced_threads.push_back({tid, + {{IntelPTDataKinds::kTraceBuffer, + thread_trace.GetTraceBufferSize()}}}); + }); + return state; +} + +Expected> IntelPTPerThreadProcessTrace::GetBinaryData( + const TraceGetBinaryDataRequest &request) { + if (Expected trace = + m_thread_traces.GetTracedThread(*request.tid)) + return trace->GetTraceBuffer(request.offset, request.size); + else + return trace.takeError(); +} + +Expected +IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request, + ArrayRef current_tids) { + IntelPTProcessTraceUP trace(new IntelPTPerThreadProcessTrace(request)); + + Error error = Error::success(); + for (lldb::tid_t tid : current_tids) + error = joinErrors(std::move(error), trace->TraceStart(tid)); + if (error) + return std::move(error); + return trace; } diff --git a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h @@ -0,0 +1,59 @@ +//===-- IntelPTProcessTrace.h --------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IntelPTProcessTrace_H_ +#define liblldb_IntelPTProcessTrace_H_ + +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" + +#include + +namespace lldb_private { +namespace process_linux { + +// Abstract class to be inherited by all the process tracing strategies. +class IntelPTProcessTrace { +public: + virtual ~IntelPTProcessTrace() = default; + + /// This method should be invoked as early as possible whenever the process + /// resumes or stops so that intel-pt collection is not enabled when + /// the process is not running. A case in which this is useful in when + /// tracing is done per-core. In this case we want to prevent polluting the + /// core traces with executions of unrelated processes, which increases the + /// data loss of the target process, given that core traces don't filter by + /// process. + /// A possible way to avoid this is to use CR3 filtering, which is equivalent + /// to process filtering, but the perf_event API doesn't support it. + /// + /// \param[in] state + /// The new state of the target process. + virtual void OnProcessStateChanged(lldb::StateType state){}; + + /// Construct a minimal jLLDBTraceGetState response for this process trace. + virtual TraceGetStateResponse GetState() = 0; + + virtual bool TracesThread(lldb::tid_t tid) const = 0; + + /// \copydoc IntelPTThreadTraceCollection::TraceStart() + virtual llvm::Error TraceStart(lldb::tid_t tid) = 0; + + /// \copydoc IntelPTThreadTraceCollection::TraceStop() + virtual llvm::Error TraceStop(lldb::tid_t tid) = 0; + + /// Get binary data owned by this instance. + virtual llvm::Expected> + GetBinaryData(const TraceGetBinaryDataRequest &request) = 0; +}; + +using IntelPTProcessTraceUP = std::unique_ptr; + +} // namespace process_linux +} // namespace lldb_private + +#endif // liblldb_IntelPTProcessTrace_H_ diff --git a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py --- a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py +++ b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py @@ -189,7 +189,7 @@ # We can't support tracing per thread is per core is enabled. self.traceStartThread( error="True", - substrs=["Process currently traced with per-core tracing. Stop process tracing first"]) + substrs=["Thread with tid ", "is currently traced"]) # We can't stop individual thread when per core is enabled. self.traceStopThread(error="True",