diff --git a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h --- a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h +++ b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h @@ -10,6 +10,7 @@ #define LLDB_UTILITY_TRACEGDBREMOTEPACKETS_H #include "llvm/Support/JSON.h" + #include #include "lldb/lldb-defines.h" @@ -98,7 +99,7 @@ /// Identifier of data to fetch with jLLDBTraceGetBinaryData. std::string kind; /// Size in bytes for this data. - int64_t size; + size_t size; }; bool fromJSON(const llvm::json::Value &value, TraceBinaryData &packet, diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt --- a/lldb/source/Plugins/Process/Linux/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Linux/CMakeLists.txt @@ -2,6 +2,8 @@ IntelPTCollector.cpp IntelPTSingleBufferTrace.cpp IntelPTMultiCoreTrace.cpp + IntelPTPerThreadProcessTrace.cpp + IntelPTThreadTraceCollection.cpp NativeProcessLinux.cpp NativeRegisterContextLinux.cpp NativeRegisterContextLinux_arm.cpp 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 @@ -12,6 +12,7 @@ #include "Perf.h" #include "IntelPTMultiCoreTrace.h" +#include "IntelPTPerThreadProcessTrace.h" #include "IntelPTSingleBufferTrace.h" #include "lldb/Utility/Status.h" @@ -26,76 +27,6 @@ namespace process_linux { -/// Manages a list of thread traces. -class IntelPTThreadTraceCollection { -public: - IntelPTThreadTraceCollection() {} - - /// Dispose of all traces - void Clear(); - - bool TracesThread(lldb::tid_t tid) const; - - size_t GetTotalBufferSize() const; - - std::vector GetThreadStates() const; - - llvm::Expected GetTracedThread(lldb::tid_t tid); - - llvm::Error TraceStart(lldb::tid_t tid, - const TraceIntelPTStartRequest &request); - - llvm::Error TraceStop(lldb::tid_t tid); - - size_t GetTracedThreadsCount() const; - -private: - llvm::DenseMap m_thread_traces; - /// Total actual thread buffer size in bytes - size_t m_total_buffer_size = 0; -}; - -class IntelPTPerThreadProcessTrace; -using IntelPTPerThreadProcessTraceUP = - std::unique_ptr; - -/// Manages a "process trace" instance by tracing each thread individually. -class IntelPTPerThreadProcessTrace { -public: - /// Start tracing the current process by tracing each of its tids - /// individually. - /// - /// \param[in] request - /// Intel PT configuration parameters. - /// - /// \param[in] current_tids - /// List of tids currently alive. In the future, whenever a new thread is - /// spawned, they should be traced by calling the \a TraceStart(tid) method. - /// - /// \return - /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or - /// an \a llvm::Error otherwise. - static llvm::Expected - Start(const TraceIntelPTStartRequest &request, - llvm::ArrayRef current_tids); - - bool TracesThread(lldb::tid_t tid) const; - - IntelPTThreadTraceCollection &GetThreadTraces(); - - llvm::Error TraceStart(lldb::tid_t tid); - - llvm::Error TraceStop(lldb::tid_t tid); - -private: - IntelPTPerThreadProcessTrace(const TraceIntelPTStartRequest &request) - : m_tracing_params(request) {} - - IntelPTThreadTraceCollection m_thread_traces; - /// Params used to trace threads when the user started "process tracing". - TraceIntelPTStartRequest m_tracing_params; -}; - /// Main class that manages intel-pt process and thread tracing. class IntelPTCollector { public: @@ -123,7 +54,7 @@ const std::vector &process_threads); /// Implementation of the jLLDBTraceGetState packet - llvm::Expected GetState() const; + llvm::Expected GetState(); /// Implementation of the jLLDBTraceGetBinaryData packet llvm::Expected> 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 @@ -32,102 +32,6 @@ using namespace process_linux; using namespace llvm; -/// IntelPTThreadTraceCollection - -bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const { - return m_thread_traces.count(tid); -} - -Error IntelPTThreadTraceCollection::TraceStop(lldb::tid_t tid) { - auto it = m_thread_traces.find(tid); - if (it == m_thread_traces.end()) - return createStringError(inconvertibleErrorCode(), - "Thread %" PRIu64 " not currently traced", tid); - m_total_buffer_size -= it->second->GetTraceBufferSize(); - m_thread_traces.erase(tid); - return Error::success(); -} - -Error IntelPTThreadTraceCollection::TraceStart( - lldb::tid_t tid, const TraceIntelPTStartRequest &request) { - if (TracesThread(tid)) - return createStringError(inconvertibleErrorCode(), - "Thread %" PRIu64 " already traced", tid); - - Expected trace_up = - IntelPTSingleBufferTrace::Start(request, tid, /*core_id=*/None, - TraceCollectionState::Running); - if (!trace_up) - return trace_up.takeError(); - - m_total_buffer_size += (*trace_up)->GetTraceBufferSize(); - m_thread_traces.try_emplace(tid, std::move(*trace_up)); - return Error::success(); -} - -size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const { - return m_total_buffer_size; -} - -std::vector -IntelPTThreadTraceCollection::GetThreadStates() const { - std::vector states; - for (const auto &it : m_thread_traces) - states.push_back({static_cast(it.first), - {TraceBinaryData{IntelPTDataKinds::kTraceBuffer, - static_cast( - it.second->GetTraceBufferSize())}}}); - return states; -} - -Expected -IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) { - auto it = m_thread_traces.find(tid); - if (it == m_thread_traces.end()) - return createStringError(inconvertibleErrorCode(), - "Thread %" PRIu64 " not currently traced", tid); - return *it->second.get(); -} - -void IntelPTThreadTraceCollection::Clear() { - m_thread_traces.clear(); - m_total_buffer_size = 0; -} - -size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const { - return m_thread_traces.size(); -} - -/// IntelPTPerThreadProcessTrace - -bool IntelPTPerThreadProcessTrace::TracesThread(lldb::tid_t tid) const { - return m_thread_traces.TracesThread(tid); -} - -Error IntelPTPerThreadProcessTrace::TraceStop(lldb::tid_t tid) { - return m_thread_traces.TraceStop(tid); -} - -Error IntelPTPerThreadProcessTrace::TraceStart(lldb::tid_t tid) { - if (m_thread_traces.GetTotalBufferSize() + - m_tracing_params.trace_buffer_size > - static_cast(*m_tracing_params.process_buffer_size_limit)) - return createStringError( - inconvertibleErrorCode(), - "Thread %" PRIu64 " can't be traced as the process trace size limit " - "has been reached. Consider retracing with a higher " - "limit.", - tid); - - return m_thread_traces.TraceStart(tid, m_tracing_params); -} - -IntelPTThreadTraceCollection &IntelPTPerThreadProcessTrace::GetThreadTraces() { - return m_thread_traces; -} - -/// IntelPTCollector - IntelPTCollector::IntelPTCollector() { if (Expected tsc_conversion = LoadPerfTscConversionParameters()) @@ -242,35 +146,40 @@ return Error::success(); } -Expected IntelPTCollector::GetState() const { +Expected IntelPTCollector::GetState() { Expected> cpu_info = GetProcfsCpuInfo(); if (!cpu_info) return cpu_info.takeError(); TraceGetStateResponse state; - state.process_binary_data.push_back({IntelPTDataKinds::kProcFsCpuInfo, - static_cast(cpu_info->size())}); + state.process_binary_data.push_back( + {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); - std::vector thread_states = - m_thread_traces.GetThreadStates(); - state.traced_threads.insert(state.traced_threads.end(), thread_states.begin(), - thread_states.end()); + m_thread_traces.ForEachThread( + [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { + state.traced_threads.push_back({static_cast(tid), + {{IntelPTDataKinds::kTraceBuffer, + thread_trace.GetTraceBufferSize()}}}); + }); if (m_per_thread_process_trace_up) { - thread_states = - m_per_thread_process_trace_up->GetThreadTraces().GetThreadStates(); - state.traced_threads.insert(state.traced_threads.end(), - thread_states.begin(), thread_states.end()); + m_per_thread_process_trace_up->GetThreadTraces().ForEachThread( + [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) { + state.traced_threads.push_back( + {static_cast(tid), + {{IntelPTDataKinds::kTraceBuffer, + thread_trace.GetTraceBufferSize()}}}); + }); } + if (m_per_core_process_trace_up) { 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, - static_cast(core_trace.GetTraceBufferSize())}}}); + state.cores->push_back({core_id, + {{IntelPTDataKinds::kTraceBuffer, + core_trace.GetTraceBufferSize()}}}); }); } return toJSON(state); diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h @@ -0,0 +1,64 @@ +//===-- IntelPTPerThreadProcessTrace.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_IntelPTPerThreadProcessTrace_H_ +#define liblldb_IntelPTPerThreadProcessTrace_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 { +public: + /// Start tracing the current process by tracing each of its tids + /// individually. + /// + /// \param[in] request + /// Intel PT configuration parameters. + /// + /// \param[in] current_tids + /// List of tids currently alive. In the future, whenever a new thread is + /// spawned, they should be traced by calling the \a TraceStart(tid) method. + /// + /// \return + /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or + /// an \a llvm::Error otherwise. + static llvm::Expected + Start(const TraceIntelPTStartRequest &request, + llvm::ArrayRef current_tids); + + bool TracesThread(lldb::tid_t tid) const; + + IntelPTThreadTraceCollection &GetThreadTraces(); + + /// \copydoc IntelPTThreadTraceCollection::TraceStart() + llvm::Error TraceStart(lldb::tid_t tid); + + /// \copydoc IntelPTThreadTraceCollection::TraceStop() + llvm::Error TraceStop(lldb::tid_t tid); + +private: + IntelPTPerThreadProcessTrace(const TraceIntelPTStartRequest &request) + : m_tracing_params(request) {} + + IntelPTThreadTraceCollection m_thread_traces; + /// Params used to trace threads when the user started "process tracing". + TraceIntelPTStartRequest m_tracing_params; +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // liblldb_IntelPTPerThreadProcessTrace_H_ diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp @@ -0,0 +1,40 @@ +//===-- IntelPTPerThreadProcessTrace.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IntelPTPerThreadProcessTrace.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_linux; +using namespace llvm; + +bool IntelPTPerThreadProcessTrace::TracesThread(lldb::tid_t tid) const { + return m_thread_traces.TracesThread(tid); +} + +Error IntelPTPerThreadProcessTrace::TraceStop(lldb::tid_t tid) { + return m_thread_traces.TraceStop(tid); +} + +Error IntelPTPerThreadProcessTrace::TraceStart(lldb::tid_t tid) { + if (m_thread_traces.GetTotalBufferSize() + + m_tracing_params.trace_buffer_size > + static_cast(*m_tracing_params.process_buffer_size_limit)) + return createStringError( + inconvertibleErrorCode(), + "Thread %" PRIu64 " can't be traced as the process trace size limit " + "has been reached. Consider retracing with a higher " + "limit.", + tid); + + return m_thread_traces.TraceStart(tid, m_tracing_params); +} + +IntelPTThreadTraceCollection &IntelPTPerThreadProcessTrace::GetThreadTraces() { + return m_thread_traces; +} diff --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h @@ -0,0 +1,71 @@ +//===-- IntelPTThreadTraceCollection.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_IntelPTPerThreadTraceCollection_H_ +#define liblldb_IntelPTPerThreadTraceCollection_H_ + +#include "IntelPTSingleBufferTrace.h" + +namespace lldb_private { +namespace process_linux { + +/// Manages a list of thread traces. +class IntelPTThreadTraceCollection { +public: + IntelPTThreadTraceCollection() {} + + /// Dispose of all traces + void Clear(); + + /// \return + /// \b true if and only if this instance of tracing the provided \p tid. + bool TracesThread(lldb::tid_t tid) const; + + /// \return + /// The total sum of the trace buffer sizes used by this collection. + size_t GetTotalBufferSize() const; + + /// Execute the provided callback on each thread that is being traced. + /// + /// \param[in] callback.tid + /// The id of the thread that is being traced. + /// + /// \param[in] callback.core_trace + /// The single-buffer trace instance for the given core. + void ForEachThread(std::function + callback); + + llvm::Expected GetTracedThread(lldb::tid_t tid); + + /// Start tracing the thread given by its \p tid. + /// + /// \return + /// An error if the operation failed. + llvm::Error TraceStart(lldb::tid_t tid, + const TraceIntelPTStartRequest &request); + + /// Stop tracing the thread given by its \p tid. + /// + /// \return + /// An error if the given thread is not being traced or tracing couldn't be + /// stopped. + llvm::Error TraceStop(lldb::tid_t tid); + + size_t GetTracedThreadsCount() const; + +private: + llvm::DenseMap m_thread_traces; + /// Total actual thread buffer size in bytes + size_t m_total_buffer_size = 0; +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // liblldb_IntelPTPerThreadTraceCollection_H_ diff --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp @@ -0,0 +1,74 @@ +//===-- IntelPTThreadTraceCollection.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IntelPTThreadTraceCollection.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_linux; +using namespace llvm; + +bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const { + return m_thread_traces.count(tid); +} + +Error IntelPTThreadTraceCollection::TraceStop(lldb::tid_t tid) { + auto it = m_thread_traces.find(tid); + if (it == m_thread_traces.end()) + return createStringError(inconvertibleErrorCode(), + "Thread %" PRIu64 " not currently traced", tid); + m_total_buffer_size -= it->second->GetTraceBufferSize(); + m_thread_traces.erase(tid); + return Error::success(); +} + +Error IntelPTThreadTraceCollection::TraceStart( + lldb::tid_t tid, const TraceIntelPTStartRequest &request) { + if (TracesThread(tid)) + return createStringError(inconvertibleErrorCode(), + "Thread %" PRIu64 " already traced", tid); + + Expected trace_up = + IntelPTSingleBufferTrace::Start(request, tid, /*core_id=*/None, + TraceCollectionState::Running); + if (!trace_up) + return trace_up.takeError(); + + m_total_buffer_size += (*trace_up)->GetTraceBufferSize(); + m_thread_traces.try_emplace(tid, std::move(*trace_up)); + return Error::success(); +} + +size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const { + return m_total_buffer_size; +} + +void IntelPTThreadTraceCollection::ForEachThread( + std::function + callback) { + for (auto &it : m_thread_traces) + callback(it.first, *it.second); +} + +Expected +IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) { + auto it = m_thread_traces.find(tid); + if (it == m_thread_traces.end()) + return createStringError(inconvertibleErrorCode(), + "Thread %" PRIu64 " not currently traced", tid); + return *it->second.get(); +} + +void IntelPTThreadTraceCollection::Clear() { + m_thread_traces.clear(); + m_total_buffer_size = 0; +} + +size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const { + return m_thread_traces.size(); +} diff --git a/lldb/source/Utility/TraceGDBRemotePackets.cpp b/lldb/source/Utility/TraceGDBRemotePackets.cpp --- a/lldb/source/Utility/TraceGDBRemotePackets.cpp +++ b/lldb/source/Utility/TraceGDBRemotePackets.cpp @@ -133,15 +133,20 @@ {"kind", packet.kind}, {"offset", packet.offset}, {"tid", packet.tid}, - {"size", packet.size}}); + {"size", static_cast(packet.size)}}); } bool fromJSON(const json::Value &value, TraceGetBinaryDataRequest &packet, Path path) { ObjectMapper o(value, path); - return o && o.map("type", packet.type) && o.map("kind", packet.kind) && - o.map("tid", packet.tid) && o.map("offset", packet.offset) && - o.map("size", packet.size); + int64_t size; + if (!o || !o.map("type", packet.type) || !o.map("kind", packet.kind) || + !o.map("tid", packet.tid) || !o.map("offset", packet.offset) || + !o.map("size", size)) { + return false; + } + packet.size = static_cast(size); + return true; } /// \}