wallace updated this revision to Diff 428687. wallace added a comment. address comments
In a newer diff I'll create a base class for all "process tracing" strategies, so that we don't need to use two unique pointers for both process tracing classes in the collector. I'm not doing it here becaues rebasing would be a pain. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D124858/new/ https://reviews.llvm.org/D124858 Files: lldb/docs/lldb-gdb-remote.txt lldb/include/lldb/Utility/TraceGDBRemotePackets.h lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h lldb/include/lldb/lldb-types.h lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py lldb/source/Plugins/Process/Linux/CMakeLists.txt lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp lldb/source/Plugins/Process/Linux/IntelPTCollector.h lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp lldb/source/Plugins/Process/Linux/Perf.cpp lldb/source/Plugins/Process/Linux/Perf.h lldb/source/Plugins/Process/Linux/Procfs.cpp lldb/source/Plugins/Process/Linux/Procfs.h lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td lldb/source/Target/Trace.cpp lldb/source/Utility/TraceGDBRemotePackets.cpp lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py lldb/unittests/Process/Linux/ProcfsTests.cpp
Index: lldb/unittests/Process/Linux/ProcfsTests.cpp =================================================================== --- lldb/unittests/Process/Linux/ProcfsTests.cpp +++ lldb/unittests/Process/Linux/ProcfsTests.cpp @@ -18,7 +18,7 @@ using namespace llvm; TEST(Perf, HardcodedLogicalCoreIDs) { - Expected<std::vector<int>> core_ids = + Expected<std::vector<lldb::core_id_t>> core_ids = GetAvailableLogicalCoreIDs(R"(processor : 13 vendor_id : GenuineIntel cpu family : 6 @@ -98,7 +98,7 @@ GTEST_SKIP() << toString(buffer_or_error.takeError()); // At this point we shouldn't fail parsing the core ids - Expected<ArrayRef<int>> core_ids = GetAvailableLogicalCoreIDs(); + Expected<ArrayRef<lldb::core_id_t>> core_ids = GetAvailableLogicalCoreIDs(); ASSERT_TRUE((bool)core_ids); ASSERT_GT((int)core_ids->size(), 0) << "We must see at least one core"; } Index: lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py =================================================================== --- lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py +++ lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py @@ -30,6 +30,8 @@ self.expect("continue") self.expect("thread trace dump instructions", substrs=['main.cpp:4']) + self.traceStopProcess() + @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @testSBAPIAndCommands def testStartMultipleLiveThreadsWithStops(self): @@ -65,6 +67,8 @@ self.expect("thread trace dump instructions 2", substrs=['not traced']) + self.traceStopProcess() + @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @testSBAPIAndCommands def testStartMultipleLiveThreadsWithStops(self): @@ -100,6 +104,8 @@ self.expect("thread trace dump instructions 1", substrs=['not traced']) self.expect("thread trace dump instructions 2", substrs=['not traced']) + self.traceStopProcess() + @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) def testStartMultipleLiveThreadsWithThreadStartAll(self): self.build() @@ -156,6 +162,8 @@ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @testSBAPIAndCommands def testStartPerCoreSession(self): + self.skipIfPerCoreTracingIsNotSupported() + self.build() exe = self.getBuildArtifact("a.out") self.dbg.CreateTarget(exe) @@ -163,6 +171,32 @@ self.expect("b main") self.expect("r") - self.traceStartProcess( - error=True, perCoreTracing=True, - substrs=["Per-core tracing is not supported"]) + # We should fail if we hit the total buffer limit. Useful if the number + # of cores is huge. + self.traceStartProcess(error="True", processBufferSizeLimit=100, + perCoreTracing=True, + substrs=["The process can't be traced because the process trace size " + "limit has been reached. Consider retracing with a higher limit."]) + + self.traceStartProcess(perCoreTracing=True) + self.traceStopProcess() + + self.traceStartProcess(perCoreTracing=True) + # We can't support multiple per-core tracing sessions. + self.traceStartProcess(error=True, perCoreTracing=True, + substrs=["Process currently traced. Stop process tracing first"]) + + # 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"]) + + # We can't stop individual thread when per core is enabled. + self.traceStopThread(error="True", + substrs=["Can't stop tracing an individual thread when per-core process tracing is enabled"]) + + # The GetState packet should return trace buffers per core and at least one traced thread + self.expect("""process plugin packet send 'jLLDBTraceGetState:{"type":"intel-pt"}]'""", + substrs=['''[{"kind":"traceBuffer","size":4096}],"coreId":''', '"tid":']) + + self.traceStopProcess() Index: lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp =================================================================== --- lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp +++ lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -16,6 +16,10 @@ const char *IntelPTDataKinds::kProcFsCpuInfo = "procfsCpuInfo"; const char *IntelPTDataKinds::kTraceBuffer = "traceBuffer"; +bool TraceIntelPTStartRequest::IsPerCoreTracing() const { + return per_core_tracing.getValueOr(false); +} + bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet, Path path) { ObjectMapper o(value, path); Index: lldb/source/Utility/TraceGDBRemotePackets.cpp =================================================================== --- lldb/source/Utility/TraceGDBRemotePackets.cpp +++ lldb/source/Utility/TraceGDBRemotePackets.cpp @@ -87,24 +87,42 @@ bool fromJSON(const json::Value &value, TraceThreadState &packet, Path path) { ObjectMapper o(value, path); return o && o.map("tid", packet.tid) && - o.map("binaryData", packet.binaryData); + o.map("binaryData", packet.binary_data); } json::Value toJSON(const TraceThreadState &packet) { return json::Value( - Object{{"tid", packet.tid}, {"binaryData", packet.binaryData}}); + Object{{"tid", packet.tid}, {"binaryData", packet.binary_data}}); } bool fromJSON(const json::Value &value, TraceGetStateResponse &packet, Path path) { ObjectMapper o(value, path); - return o && o.map("tracedThreads", packet.tracedThreads) && - o.map("processBinaryData", packet.processBinaryData); + return o && o.map("tracedThreads", packet.traced_threads) && + o.map("processBinaryData", packet.process_binary_data) && + o.map("cores", packet.cores); } json::Value toJSON(const TraceGetStateResponse &packet) { - return json::Value(Object{{"tracedThreads", packet.tracedThreads}, - {"processBinaryData", packet.processBinaryData}}); + return json::Value(Object{{"tracedThreads", packet.traced_threads}, + {"processBinaryData", packet.process_binary_data}, + {"cores", packet.cores}}); +} + +bool fromJSON(const json::Value &value, TraceCoreState &packet, + json::Path path) { + ObjectMapper o(value, path); + int64_t core_id; + if (!o || !o.map("coreId", core_id) || + !o.map("binaryData", packet.binary_data)) + return false; + packet.core_id = static_cast<lldb::core_id_t>(core_id); + return true; +} + +json::Value toJSON(const TraceCoreState &packet) { + return json::Value( + Object{{"coreId", packet.core_id}, {"binaryData", packet.binary_data}}); } /// \} Index: lldb/source/Target/Trace.cpp =================================================================== --- lldb/source/Target/Trace.cpp +++ lldb/source/Target/Trace.cpp @@ -199,12 +199,12 @@ } for (const TraceThreadState &thread_state : - live_process_state->tracedThreads) { - for (const TraceBinaryData &item : thread_state.binaryData) + live_process_state->traced_threads) { + for (const TraceBinaryData &item : thread_state.binary_data) m_live_thread_data[thread_state.tid][item.kind] = item.size; } - for (const TraceBinaryData &item : live_process_state->processBinaryData) + for (const TraceBinaryData &item : live_process_state->process_binary_data) m_live_process_data[item.kind] = item.size; DoRefreshLiveProcessState(std::move(live_process_state)); Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td @@ -57,8 +57,8 @@ Group<1>, Arg<"Value">, Desc<"Maximum total trace size per process in bytes. This limit applies to " - "the sum of the sizes of all thread traces of this process, excluding " - "the ones created with the \"thread trace start\" command. " + "the sum of the sizes of all thread and core traces of this process, " + "excluding the ones created with the \"thread trace start\" command. " "Whenever a thread is attempted to be traced due to this command and " "the limit would be reached, the process is stopped with a " "\"processor trace\" reason, so that the user can retrace the process " Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -252,7 +252,7 @@ return; } - for (const TraceThreadState &thread_state : state->tracedThreads) { + for (const TraceThreadState &thread_state : state->traced_threads) { ThreadSP thread_sp = m_live_process->GetThreadList().FindThreadByID(thread_state.tid); m_thread_decoders.emplace( @@ -354,9 +354,9 @@ Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, StructuredData::ObjectSP configuration) { - size_t trace_buffer_size = kDefaultTraceBufferSize; + uint64_t trace_buffer_size = kDefaultTraceBufferSize; bool enable_tsc = kDefaultEnableTscValue; - Optional<size_t> psb_period = kDefaultPsbPeriod; + Optional<uint64_t> psb_period = kDefaultPsbPeriod; if (configuration) { if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { Index: lldb/source/Plugins/Process/Linux/Procfs.h =================================================================== --- lldb/source/Plugins/Process/Linux/Procfs.h +++ lldb/source/Plugins/Process/Linux/Procfs.h @@ -11,6 +11,8 @@ #include <sys/ptrace.h> +#include "lldb/lldb-types.h" + #include "llvm/Support/Error.h" #include <vector> @@ -43,13 +45,13 @@ /// \return /// A list of available logical core ids given the contents of /// /proc/cpuinfo. -llvm::Expected<std::vector<int>> +llvm::Expected<std::vector<lldb::core_id_t>> GetAvailableLogicalCoreIDs(llvm::StringRef cpuinfo); /// \return /// A list with all the logical cores available in the system and cache it /// if errors didn't happen. -llvm::Expected<llvm::ArrayRef<int>> GetAvailableLogicalCoreIDs(); +llvm::Expected<llvm::ArrayRef<lldb::core_id_t>> GetAvailableLogicalCoreIDs(); } // namespace process_linux } // namespace lldb_private Index: lldb/source/Plugins/Process/Linux/Procfs.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/Procfs.cpp +++ lldb/source/Plugins/Process/Linux/Procfs.cpp @@ -11,6 +11,7 @@ #include "lldb/Host/linux/Support.h" #include "llvm/Support/MemoryBuffer.h" +using namespace lldb; using namespace lldb_private; using namespace process_linux; using namespace llvm; @@ -29,18 +30,18 @@ return *cpu_info; } -Expected<std::vector<int>> +Expected<std::vector<core_id_t>> lldb_private::process_linux::GetAvailableLogicalCoreIDs(StringRef cpuinfo) { SmallVector<StringRef, 8> lines; cpuinfo.split(lines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); - std::vector<int> logical_cores; + std::vector<core_id_t> logical_cores; for (StringRef line : lines) { std::pair<StringRef, StringRef> key_value = line.split(':'); auto key = key_value.first.trim(); auto val = key_value.second.trim(); if (key == "processor") { - int processor; + core_id_t processor; if (val.getAsInteger(10, processor)) return createStringError( inconvertibleErrorCode(), @@ -51,17 +52,17 @@ return logical_cores; } -llvm::Expected<llvm::ArrayRef<int>> +llvm::Expected<llvm::ArrayRef<core_id_t>> lldb_private::process_linux::GetAvailableLogicalCoreIDs() { - static Optional<std::vector<int>> logical_cores_ids; + static Optional<std::vector<core_id_t>> logical_cores_ids; if (!logical_cores_ids) { // We find the actual list of core ids by parsing /proc/cpuinfo Expected<ArrayRef<uint8_t>> cpuinfo = GetProcfsCpuInfo(); if (!cpuinfo) return cpuinfo.takeError(); - Expected<std::vector<int>> core_ids = GetAvailableLogicalCoreIDs(StringRef( - reinterpret_cast<const char *>(cpuinfo->data()), cpuinfo->size())); + Expected<std::vector<core_id_t>> core_ids = GetAvailableLogicalCoreIDs( + StringRef(reinterpret_cast<const char *>(cpuinfo->data()))); if (!core_ids) return core_ids.takeError(); Index: lldb/source/Plugins/Process/Linux/Perf.h =================================================================== --- lldb/source/Plugins/Process/Linux/Perf.h +++ lldb/source/Plugins/Process/Linux/Perf.h @@ -109,13 +109,16 @@ /// Configuration information for the event. /// /// \param[in] pid - /// The process to be monitored by the event. + /// The process or thread to be monitored by the event. If \b None, then + /// all processes and threads are monitored. /// /// \param[in] cpu - /// The cpu to be monitored by the event. + /// The cpu to be monitored by the event. If \b None, then all cpus are + /// monitored. /// /// \param[in] group_fd - /// File descriptor of the group leader. + /// File descriptor of the group leader. If \b None, then this perf_event + /// doesn't belong to a preexisting group. /// /// \param[in] flags /// Bitmask of additional configuration flags. @@ -123,8 +126,10 @@ /// \return /// If the perf_event_open syscall was successful, a minimal \a PerfEvent /// instance, or an \a llvm::Error otherwise. - static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, lldb::pid_t pid, - int cpu, int group_fd, + static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, + llvm::Optional<lldb::pid_t> pid, + llvm::Optional<lldb::core_id_t> cpu, + llvm::Optional<int> group_fd, unsigned long flags); /// Create a new performance monitoring event via the perf_event_open syscall @@ -137,8 +142,11 @@ /// Configuration information for the event. /// /// \param[in] pid - /// The process to be monitored by the event. - static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, lldb::pid_t pid); + /// The process or thread to be monitored by the event. If \b None, then + /// all threads and processes are monitored. + static llvm::Expected<PerfEvent> + Init(perf_event_attr &attr, llvm::Optional<lldb::pid_t> pid, + llvm::Optional<lldb::core_id_t> core = llvm::None); /// Mmap the metadata page and the data and aux buffers of the perf event and /// expose them through \a PerfEvent::GetMetadataPage() , \a Index: lldb/source/Plugins/Process/Linux/Perf.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/Perf.cpp +++ lldb/source/Plugins/Process/Linux/Perf.cpp @@ -117,10 +117,13 @@ } llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr, - lldb::pid_t pid, int cpu, - int group_fd, unsigned long flags) { + Optional<lldb::pid_t> pid, + Optional<lldb::core_id_t> cpu, + Optional<int> group_fd, + unsigned long flags) { errno = 0; - long fd = syscall(SYS_perf_event_open, &attr, pid, cpu, group_fd, flags); + long fd = syscall(SYS_perf_event_open, &attr, pid.getValueOr(-1), + cpu.getValueOr(-1), group_fd.getValueOr(-1), flags); if (fd == -1) { std::string err_msg = llvm::formatv("perf event syscall failed: {0}", std::strerror(errno)); @@ -130,8 +133,9 @@ } llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr, - lldb::pid_t pid) { - return Init(attr, pid, -1, -1, 0); + Optional<lldb::pid_t> pid, + Optional<lldb::core_id_t> cpu) { + return Init(attr, pid, cpu, -1, 0); } llvm::Expected<resource_handle::MmapUP> Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -312,7 +312,7 @@ const ArchSpec &arch, MainLoop &mainloop, llvm::ArrayRef<::pid_t> tids) : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch), - m_main_loop(mainloop) { + m_main_loop(mainloop), m_intel_pt_collector(*this) { if (m_terminal_fd != -1) { Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); assert(status.Success()); @@ -1967,10 +1967,7 @@ if (Expected<TraceIntelPTStartRequest> request = json::parse<TraceIntelPTStartRequest>(json_request, "TraceIntelPTStartRequest")) { - std::vector<lldb::tid_t> process_threads; - for (auto &thread : m_threads) - process_threads.push_back(thread->GetID()); - return m_intel_pt_collector.TraceStart(*request, process_threads); + return m_intel_pt_collector.TraceStart(*request); } else return request.takeError(); } Index: lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h +++ lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h @@ -23,10 +23,8 @@ llvm::Expected<uint32_t> GetIntelPTOSEventType(); -class IntelPTTrace; class IntelPTSingleBufferTrace; -using IntelPTThreadTraceUP = std::unique_ptr<IntelPTTrace>; using IntelPTSingleBufferTraceUP = std::unique_ptr<IntelPTSingleBufferTrace>; /// This class wraps a single perf event collecting intel pt data in a single @@ -39,13 +37,19 @@ /// Intel PT configuration parameters. /// /// \param[in] tid - /// The tid of the thread to be traced. + /// The tid of the thread to be traced. If \b None, then this traces all + /// threads of all processes. + /// + /// \param[in] core_id + /// The CPU core id where to trace. If \b None, then this traces all CPUs. /// /// \return /// A \a IntelPTSingleBufferTrace instance if tracing was successful, or /// an \a llvm::Error otherwise. static llvm::Expected<IntelPTSingleBufferTraceUP> - Start(const TraceIntelPTStartRequest &request, lldb::tid_t tid); + Start(const TraceIntelPTStartRequest &request, + llvm::Optional<lldb::tid_t> tid, + llvm::Optional<lldb::core_id_t> core_id = llvm::None); /// \return /// The bytes requested by a jLLDBTraceGetBinaryData packet that was routed @@ -80,10 +84,7 @@ /// /// \param[in] perf_event /// perf event configured for IntelPT. - /// - /// \param[in] tid - /// The thread being traced. - IntelPTSingleBufferTrace(PerfEvent &&perf_event, lldb::tid_t tid) + IntelPTSingleBufferTrace(PerfEvent &&perf_event) : m_perf_event(std::move(perf_event)) {} /// perf event configured for IntelPT. Index: lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp +++ lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp @@ -260,14 +260,16 @@ Expected<IntelPTSingleBufferTraceUP> IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request, - lldb::tid_t tid) { + Optional<lldb::tid_t> tid, + Optional<core_id_t> core_id) { #ifndef PERF_ATTR_SIZE_VER5 return createStringError(inconvertibleErrorCode(), "Intel PT Linux perf event not supported"); #else Log *log = GetLog(POSIXLog::Trace); - LLDB_LOG(log, "Will start tracing thread id {0}", tid); + LLDB_LOG(log, "Will start tracing thread id {0} and cpu id {1}", tid, + core_id); if (__builtin_popcount(request.trace_buffer_size) != 1 || request.trace_buffer_size < 4096) { @@ -291,13 +293,13 @@ LLDB_LOG(log, "Will create trace buffer of size {0}", request.trace_buffer_size); - if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) { + if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid, core_id)) { if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages, buffer_numpages)) { return std::move(mmap_err); } return IntelPTSingleBufferTraceUP( - new IntelPTSingleBufferTrace(std::move(*perf_event), tid)); + new IntelPTSingleBufferTrace(std::move(*perf_event))); } else { return perf_event.takeError(); } Index: lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h @@ -0,0 +1,64 @@ +//===-- IntelPTMultiCoreTrace.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_IntelPTMultiCoreTrace_H_ +#define liblldb_IntelPTMultiCoreTrace_H_ + +#include "IntelPTSingleBufferTrace.h" + +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" +#include "lldb/lldb-types.h" + +#include "llvm/Support/Error.h" + +#include <memory> + +namespace lldb_private { +namespace process_linux { + +class IntelPTMultiCoreTrace; +using IntelPTMultiCoreTraceUP = std::unique_ptr<IntelPTMultiCoreTrace>; + +class IntelPTMultiCoreTrace { +public: + /// Start tracing all CPU cores. + /// + /// \param[in] request + /// Intel PT configuration parameters. + /// + /// \return + /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or + /// an \a llvm::Error otherwise. + static llvm::Expected<IntelPTMultiCoreTraceUP> + StartOnAllCores(const TraceIntelPTStartRequest &request); + + /// Execute the provided callback on each core that is being traced. + /// + /// \param[in] callback.core_id + /// The core id that is being traced. + /// + /// \param[in] callback.core_trace + /// The single-buffer trace instance for the given core. + void + ForEachCore(std::function<void(lldb::core_id_t core_id, + const IntelPTSingleBufferTrace &core_trace)> + callback); + +private: + IntelPTMultiCoreTrace( + llvm::DenseMap<lldb::core_id_t, IntelPTSingleBufferTraceUP> + &&traces_per_core) + : m_traces_per_core(std::move(traces_per_core)) {} + + llvm::DenseMap<lldb::core_id_t, IntelPTSingleBufferTraceUP> m_traces_per_core; +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // liblldb_IntelPTMultiCoreTrace_H_ Index: lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp @@ -0,0 +1,64 @@ +//===-- IntelPTMultiCoreTrace.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 "IntelPTMultiCoreTrace.h" + +#include "Procfs.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_linux; +using namespace llvm; + +static bool IsTotalBufferLimitReached(ArrayRef<core_id_t> cores, + const TraceIntelPTStartRequest &request) { + uint64_t required = cores.size() * request.trace_buffer_size; + uint64_t limit = request.process_buffer_size_limit.getValueOr( + std::numeric_limits<uint64_t>::max()); + return required > limit; +} + +static Error IncludePerfEventParanoidMessageInError(Error &&error) { + return createStringError( + inconvertibleErrorCode(), + "%s\nYou might need to rerun as sudo or to set " + "/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1.", + toString(std::move(error)).c_str()); +} + +Expected<IntelPTMultiCoreTraceUP> IntelPTMultiCoreTrace::StartOnAllCores( + const TraceIntelPTStartRequest &request) { + Expected<ArrayRef<core_id_t>> core_ids = GetAvailableLogicalCoreIDs(); + if (!core_ids) + return core_ids.takeError(); + + if (IsTotalBufferLimitReached(*core_ids, request)) + return createStringError( + inconvertibleErrorCode(), + "The process can't be traced because the process trace size limit " + "has been reached. Consider retracing with a higher limit."); + + llvm::DenseMap<core_id_t, IntelPTSingleBufferTraceUP> buffers; + for (core_id_t core_id : *core_ids) { + if (Expected<IntelPTSingleBufferTraceUP> core_trace = + IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id)) + buffers.try_emplace(core_id, std::move(*core_trace)); + else + return IncludePerfEventParanoidMessageInError(core_trace.takeError()); + } + + return IntelPTMultiCoreTraceUP(new IntelPTMultiCoreTrace(std::move(buffers))); +} + +void IntelPTMultiCoreTrace::ForEachCore( + std::function<void(core_id_t core_id, + const IntelPTSingleBufferTrace &core_trace)> + callback) { + for (auto &it : m_traces_per_core) + callback(it.first, *it.second); +} Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.h =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTCollector.h +++ lldb/source/Plugins/Process/Linux/IntelPTCollector.h @@ -1,4 +1,4 @@ -//===-- IntelPTCollector.h -------------------------------------- -*- C++ -*-===// +//===-- IntelPTCollector.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. @@ -11,8 +11,10 @@ #include "Perf.h" +#include "IntelPTMultiCoreTrace.h" #include "IntelPTSingleBufferTrace.h" +#include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "lldb/lldb-types.h" @@ -47,17 +49,37 @@ llvm::Error TraceStop(lldb::tid_t tid); + size_t GetTracedThreadsCount() const; + private: llvm::DenseMap<lldb::tid_t, IntelPTSingleBufferTraceUP> m_thread_traces; /// Total actual thread buffer size in bytes size_t m_total_buffer_size = 0; }; -/// Manages a "process trace" instance. -class IntelPTProcessTrace { +class IntelPTPerThreadProcessTrace; +using IntelPTPerThreadProcessTraceUP = + std::unique_ptr<IntelPTPerThreadProcessTrace>; + +/// Manages a "process trace" instance by tracing each thread individually. +class IntelPTPerThreadProcessTrace { public: - IntelPTProcessTrace(const TraceIntelPTStartRequest &request) - : m_tracing_params(request) {} + /// 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<IntelPTPerThreadProcessTraceUP> + Start(const TraceIntelPTStartRequest &request, + llvm::ArrayRef<lldb::tid_t> current_tids); bool TracesThread(lldb::tid_t tid) const; @@ -68,6 +90,9 @@ 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; @@ -76,7 +101,9 @@ /// Main class that manages intel-pt process and thread tracing. class IntelPTCollector { public: - IntelPTCollector(); + /// \param[in] process + /// Process to be traced. + IntelPTCollector(NativeProcessProtocol &process); static bool IsSupported(); @@ -90,11 +117,7 @@ llvm::Error TraceStop(const TraceStopRequest &request); /// Implementation of the jLLDBTraceStart packet - /// - /// \param[in] process_threads - /// A list of all threads owned by the process. - llvm::Error TraceStart(const TraceIntelPTStartRequest &request, - const std::vector<lldb::tid_t> &process_threads); + llvm::Error TraceStart(const TraceIntelPTStartRequest &request); /// Implementation of the jLLDBTraceGetState packet llvm::Expected<llvm::json::Value> GetState() const; @@ -120,11 +143,21 @@ void ClearProcessTracing(); + NativeProcessProtocol &m_process; /// Threads traced due to "thread tracing" IntelPTThreadTraceCollection m_thread_traces; - /// Threads traced due to "process tracing". Only one active "process tracing" - /// instance is assumed for a single process. - llvm::Optional<IntelPTProcessTrace> m_process_trace; + + /// 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; + /// \} + /// TSC to wall time conversion. TraceTscConversionUP m_tsc_conversion; }; Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp +++ lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp @@ -92,17 +92,21 @@ m_total_buffer_size = 0; } -/// IntelPTProcessTrace +size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const { + return m_thread_traces.size(); +} + +/// IntelPTPerThreadProcessTrace -bool IntelPTProcessTrace::TracesThread(lldb::tid_t tid) const { +bool IntelPTPerThreadProcessTrace::TracesThread(lldb::tid_t tid) const { return m_thread_traces.TracesThread(tid); } -Error IntelPTProcessTrace::TraceStop(lldb::tid_t tid) { +Error IntelPTPerThreadProcessTrace::TraceStop(lldb::tid_t tid) { return m_thread_traces.TraceStop(tid); } -Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) { +Error IntelPTPerThreadProcessTrace::TraceStart(lldb::tid_t tid) { if (m_thread_traces.GetTotalBufferSize() + m_tracing_params.trace_buffer_size > static_cast<size_t>(*m_tracing_params.process_buffer_size_limit)) @@ -117,13 +121,14 @@ } const IntelPTThreadTraceCollection & -IntelPTProcessTrace::GetThreadTraces() const { +IntelPTPerThreadProcessTrace::GetThreadTraces() const { return m_thread_traces; } /// IntelPTCollector -IntelPTCollector::IntelPTCollector() { +IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process) + : m_process(process) { if (Expected<LinuxPerfZeroTscConversion> tsc_conversion = LoadPerfTscConversionParameters()) m_tsc_conversion = @@ -134,8 +139,13 @@ } Error IntelPTCollector::TraceStop(lldb::tid_t tid) { - if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) - return m_process_trace->TraceStop(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."); return m_thread_traces.TraceStop(tid); } @@ -152,26 +162,60 @@ } } -Error IntelPTCollector::TraceStart( - const TraceIntelPTStartRequest &request, - const std::vector<lldb::tid_t> &process_threads) { +Expected<IntelPTPerThreadProcessTraceUP> +IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request, + ArrayRef<lldb::tid_t> 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 trace; +} + +Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) { if (request.IsProcessTracing()) { if (IsProcessTracingEnabled()) { return createStringError( inconvertibleErrorCode(), "Process currently traced. Stop process tracing first"); } - if (request.per_core_tracing.getValueOr(false)) { - return createStringError(inconvertibleErrorCode(), - "Per-core tracing is not supported."); + if (request.IsPerCoreTracing()) { + if (m_thread_traces.GetTracedThreadsCount() > 0) + return createStringError( + inconvertibleErrorCode(), + "Threads currently traced. Stop tracing them first."); + if (Expected<IntelPTMultiCoreTraceUP> trace = + IntelPTMultiCoreTrace::StartOnAllCores(request)) { + m_per_core_process_trace_up = std::move(*trace); + return Error::success(); + } else { + return trace.takeError(); + } + } else { + std::vector<lldb::tid_t> process_threads; + for (size_t i = 0; m_process.GetThreadAtIndex(i); i++) + process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID()); + + // per-thread process tracing + if (Expected<IntelPTPerThreadProcessTraceUP> trace = + IntelPTPerThreadProcessTrace::Start(request, process_threads)) { + m_per_thread_process_trace_up = std::move(trace.get()); + return Error::success(); + } else { + return trace.takeError(); + } } - m_process_trace = IntelPTProcessTrace(request); - - Error error = Error::success(); - for (lldb::tid_t tid : process_threads) - error = joinErrors(std::move(error), m_process_trace->TraceStart(tid)); - return error; } 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), @@ -181,14 +225,16 @@ } Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { - if (!IsProcessTracingEnabled()) - return Error::success(); - return m_process_trace->TraceStart(tid); + if (m_per_thread_process_trace_up) + return m_per_thread_process_trace_up->TraceStart(tid); + + return Error::success(); } Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { - if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) - return m_process_trace->TraceStop(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_thread_traces.TracesThread(tid)) return m_thread_traces.TraceStop(tid); return Error::success(); @@ -200,26 +246,42 @@ return cpu_info.takeError(); TraceGetStateResponse state; - state.processBinaryData.push_back( + state.process_binary_data.push_back( {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()}); std::vector<TraceThreadState> thread_states = m_thread_traces.GetThreadStates(); - state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), - thread_states.end()); - - if (IsProcessTracingEnabled()) { - thread_states = m_process_trace->GetThreadTraces().GetThreadStates(); - state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), - thread_states.end()); + state.traced_threads.insert(state.traced_threads.end(), thread_states.begin(), + thread_states.end()); + + 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()); + } else 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<const IntelPTSingleBufferTrace &> IntelPTCollector::GetTracedThread(lldb::tid_t tid) const { - if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) - return m_process_trace->GetThreadTraces().GetTracedThread(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); } @@ -239,7 +301,10 @@ request.kind.c_str()); } -void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; } +void IntelPTCollector::ClearProcessTracing() { + m_per_thread_process_trace_up.reset(); + m_per_core_process_trace_up.reset(); +} bool IntelPTCollector::IsSupported() { if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) { @@ -251,7 +316,8 @@ } bool IntelPTCollector::IsProcessTracingEnabled() const { - return (bool)m_process_trace; + return (bool)m_per_thread_process_trace_up || + (bool)m_per_core_process_trace_up; } void IntelPTCollector::Clear() { Index: lldb/source/Plugins/Process/Linux/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/Linux/CMakeLists.txt +++ lldb/source/Plugins/Process/Linux/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_library(lldbPluginProcessLinux IntelPTCollector.cpp IntelPTSingleBufferTrace.cpp + IntelPTMultiCoreTrace.cpp NativeProcessLinux.cpp NativeRegisterContextLinux.cpp NativeRegisterContextLinux_arm.cpp Index: lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py =================================================================== --- lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py +++ lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py @@ -33,6 +33,19 @@ if 'intel-pt' not in configuration.enabled_plugins: self.skipTest("The intel-pt test plugin is not enabled") + def skipIfPerCoreTracingIsNotSupported(self): + def is_supported(): + try: + with open("/proc/sys/kernel/perf_event_paranoid", "r") as permissions: + value = int(permissions.readlines()[0]) + if value <= 0: + return True + except: + return False + if not is_supported(): + self.skipTest("Per core tracing is not supported. You need " + "/proc/sys/kernel/perf_event_paranoid to be 0 or -1.") + def getTraceOrCreate(self): if not self.target().GetTrace().IsValid(): error = lldb.SBError() @@ -110,7 +123,7 @@ else: self.expect("process trace stop") - def traceStopThread(self, thread=None, error=False): + def traceStopThread(self, thread=None, error=False, substrs=None): if self.USE_SB_API: thread = thread if thread is not None else self.thread() self.assertSBError(self.target().GetTrace().Stop(thread), error) @@ -119,4 +132,4 @@ command = "thread trace stop" if thread is not None: command += " " + str(thread.GetIndexID()) - self.expect(command, error=error) + self.expect(command, error=error, substrs=substrs) Index: lldb/include/lldb/lldb-types.h =================================================================== --- lldb/include/lldb/lldb-types.h +++ lldb/include/lldb/lldb-types.h @@ -89,6 +89,7 @@ typedef int32_t watch_id_t; typedef void *opaque_compiler_type_t; typedef uint64_t queue_id_t; +typedef uint32_t core_id_t; // CPU core id } // namespace lldb #endif // LLDB_LLDB_TYPES_H Index: lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h =================================================================== --- lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h +++ lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h @@ -48,6 +48,8 @@ /// Whether to have a trace buffer per thread or per cpu core. llvm::Optional<bool> per_core_tracing; + + bool IsPerCoreTracing() const; }; bool fromJSON(const llvm::json::Value &value, TraceIntelPTStartRequest &packet, Index: lldb/include/lldb/Utility/TraceGDBRemotePackets.h =================================================================== --- lldb/include/lldb/Utility/TraceGDBRemotePackets.h +++ lldb/include/lldb/Utility/TraceGDBRemotePackets.h @@ -109,7 +109,7 @@ struct TraceThreadState { lldb::tid_t tid; /// List of binary data objects for this thread. - std::vector<TraceBinaryData> binaryData; + std::vector<TraceBinaryData> binary_data; }; bool fromJSON(const llvm::json::Value &value, TraceThreadState &packet, @@ -117,6 +117,17 @@ llvm::json::Value toJSON(const TraceThreadState &packet); +struct TraceCoreState { + lldb::core_id_t core_id; + /// List of binary data objects for this core. + std::vector<TraceBinaryData> binary_data; +}; + +bool fromJSON(const llvm::json::Value &value, TraceCoreState &packet, + llvm::json::Path path); + +llvm::json::Value toJSON(const TraceCoreState &packet); + /// Interface for different algorithms used to convert trace /// counters into different units. template <typename ToType> class TraceCounterConversion { @@ -143,8 +154,9 @@ std::unique_ptr<TraceCounterConversion<std::chrono::nanoseconds>>; struct TraceGetStateResponse { - std::vector<TraceThreadState> tracedThreads; - std::vector<TraceBinaryData> processBinaryData; + std::vector<TraceThreadState> traced_threads; + std::vector<TraceBinaryData> process_binary_data; + llvm::Optional<std::vector<TraceCoreState>> cores; }; bool fromJSON(const llvm::json::Value &value, TraceGetStateResponse &packet, Index: lldb/docs/lldb-gdb-remote.txt =================================================================== --- lldb/docs/lldb-gdb-remote.txt +++ lldb/docs/lldb-gdb-remote.txt @@ -245,7 +245,7 @@ // OUTPUT SCHEMA // { // "name": <string>, -// Tracing technology name, e.g. intel-pt, arm-coresight. +// Tracing technology name, e.g. intel-pt, arm-etm. // "description": <string>, // Description for this technology. // } @@ -280,7 +280,7 @@ // INPUT SCHEMA // { // "type": <string>, -// Tracing technology name, e.g. intel-pt, arm-coresight. +// Tracing technology name, e.g. intel-pt, arm-etm. // // /* thread tracing only */ // "tids"?: [<decimal integer>], @@ -369,8 +369,9 @@ // /* process tracing only */ // "processBufferSizeLimit": <decimal integer>, // Maximum total buffer size per process in bytes. -// This limit applies to the sum of the sizes of all trace buffers for -// the current process, excluding the ones started with "thread tracing". +// This limit applies to the sum of the sizes of all thread or core +// buffers for the current process, excluding the ones started with +// "thread tracing". // // If "perCoreTracing" is false, whenever a thread is attempted to be // traced due to "process tracing" and the limit would be reached, the @@ -421,7 +422,7 @@ // // { // "type": <string> -// Tracing technology name, e.g. intel-pt, arm-coresight. +// Tracing technology name, e.g. intel-pt, arm-etm. // // /* thread trace stopping only */ // "tids": [<decimal integer>] @@ -455,7 +456,7 @@ // INPUT SCHEMA // { // "type": <string> -// Tracing technology name, e.g. intel-pt, arm-coresight. +// Tracing technology name, e.g. intel-pt, arm-etm. // } // // OUTPUT SCHEMA @@ -481,10 +482,23 @@ // Size in bytes of this thread data. // }, // ], +// "cores"?: [ +// "id": <decimal integer>, +// Identifier for this CPU logical core. +// "binaryData": [ +// { +// "kind": <string>, +// Identifier for some binary data related to this thread to +// fetch with the jLLDBTraceGetBinaryData packet. +// "size": <decimal integer>, +// Size in bytes of this cpu core data. +// }, +// ] +// ], // "counters"?: { // "info_kind": {...parameters specific to the provided counter info kind}, -// Each entry includes information related to counters associated with the trace. -// They are described below. +// Each entry includes information related to counters associated with +// the trace. They are described below. // } // } // @@ -494,6 +508,12 @@ // // INTEL PT // +// If per-core process tracing is enabled, "tracedThreads" will contain all +// the threads of the process without any trace buffers. Besides that, the +// "cores" field will also be returned with per core trace buffers. +// A side effect of per-core tracing is that all the threads of unrelated +// processes will also be traced, thus polluting the tracing data. +// // Binary data kinds: // - traceBuffer: trace buffer for a thread or a core. // - procfsCpuInfo: contents of the /proc/cpuinfo file. @@ -536,9 +556,11 @@ // // { // "type": <string>, -// Tracing technology name, e.g. intel-pt, arm-coresight. +// Tracing technology name, e.g. intel-pt, arm-etm. // "kind": <string>, // Identifier for the data. +// "coreId": <Optional decimal>, +// Core id in decimal if the data belongs to a CPU core. // "tid"?: <Optional decimal>, // Tid in decimal if the data belongs to a thread. // "offset": <decimal>,
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits