diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt --- a/lldb/docs/lldb-gdb-remote.txt +++ b/lldb/docs/lldb-gdb-remote.txt @@ -467,7 +467,7 @@ // // Binary data kinds: // - threadTraceBuffer: trace buffer for a thread. -// - cpuInfo: contents of the /proc/cpuinfo file. +// - procFsCpuInfo: contents of the /proc/cpuinfo file. // // Counter info kinds: // tsc-perf-zero-conversion: @@ -522,7 +522,7 @@ // // Binary data kinds: // - threadTraceBuffer: trace buffer for a thread. -// - cpuInfo: contents of the /proc/cpuinfo file. +// - procFsCpuInfo: contents of the /proc/cpuinfo file. //---------------------------------------------------------------------- send packet: jLLDBTraceGetBinaryData:{"type":,"kind":,"tid":,"offset":,"size":}] diff --git a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h --- a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h +++ b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h @@ -18,6 +18,12 @@ /// See docs/lldb-gdb-remote.txt for more information. namespace lldb_private { +// List of data kinds used by jLLDBGetState and jLLDBGetBinaryData. +struct IntelPTDataKinds { + static const char *kProcFsCpuInfo; + static const char *kThreadTraceBuffer; +}; + /// jLLDBTraceStart gdb-remote packet /// \{ struct TraceIntelPTStartRequest : TraceStartRequest { 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 @@ -9,6 +9,7 @@ NativeRegisterContextLinux_x86_64.cpp NativeThreadLinux.cpp Perf.cpp + Procfs.cpp SingleStepCheck.cpp LINK_LIBS 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 @@ -9,6 +9,7 @@ #include "IntelPTCollector.h" #include "Perf.h" +#include "Procfs.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Host/linux/Support.h" @@ -57,21 +58,6 @@ BitOffset }; -/// Get the content of /proc/cpuinfo that can be later used to decode traces. -static Expected> GetCPUInfo() { - static llvm::Optional> cpu_info; - if (!cpu_info) { - auto buffer_or_error = errorOrToExpected(getProcFile("cpuinfo")); - if (!buffer_or_error) - return buffer_or_error.takeError(); - MemoryBuffer &buffer = **buffer_or_error; - cpu_info = std::vector( - reinterpret_cast(buffer.getBufferStart()), - reinterpret_cast(buffer.getBufferEnd())); - } - return *cpu_info; -} - static Expected ReadIntelPTConfigFile(const char *file, IntelPTConfigFileType type) { ErrorOr> stream = @@ -430,7 +416,7 @@ TraceThreadState IntelPTThreadTrace::GetState() const { return {static_cast(m_tid), - {TraceBinaryData{"threadTraceBuffer", + {TraceBinaryData{IntelPTDataKinds::kThreadTraceBuffer, static_cast(GetTraceBufferSize())}}}; } @@ -592,13 +578,13 @@ } Expected IntelPTCollector::GetState() const { - Expected> cpu_info = GetCPUInfo(); + Expected> cpu_info = GetProcfsCpuInfo(); if (!cpu_info) return cpu_info.takeError(); TraceGetStateResponse state; - state.processBinaryData.push_back( - {"cpuInfo", static_cast(cpu_info->size())}); + state.processBinaryData.push_back({IntelPTDataKinds::kProcFsCpuInfo, + static_cast(cpu_info->size())}); std::vector thread_states = m_thread_traces.GetThreadStates(); @@ -622,14 +608,14 @@ Expected> IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const { - if (request.kind == "threadTraceBuffer") { + if (request.kind == IntelPTDataKinds::kThreadTraceBuffer) { if (Expected trace = GetTracedThread(*request.tid)) return trace->GetIntelPTBuffer(request.offset, request.size); else return trace.takeError(); - } else if (request.kind == "cpuInfo") { - return GetCPUInfo(); + } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) { + return GetProcfsCpuInfo(); } return createStringError(inconvertibleErrorCode(), "Unsuported trace binary data kind: %s", diff --git a/lldb/source/Plugins/Process/Linux/Perf.cpp b/lldb/source/Plugins/Process/Linux/Perf.cpp --- a/lldb/source/Plugins/Process/Linux/Perf.cpp +++ b/lldb/source/Plugins/Process/Linux/Perf.cpp @@ -8,8 +8,11 @@ #include "Perf.h" +#include "lldb/Host/linux/Support.h" + #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" #include #include diff --git a/lldb/source/Plugins/Process/Linux/Procfs.h b/lldb/source/Plugins/Process/Linux/Procfs.h --- a/lldb/source/Plugins/Process/Linux/Procfs.h +++ b/lldb/source/Plugins/Process/Linux/Procfs.h @@ -11,6 +11,10 @@ #include +#include "llvm/Support/Error.h" + +#include + #ifdef __ANDROID__ #if defined(__arm64__) || defined(__aarch64__) typedef unsigned long elf_greg_t; @@ -28,3 +32,24 @@ #else // __ANDROID__ #include #endif // __ANDROID__ + +namespace lldb_private { +namespace process_linux { + +/// \return +/// The content of /proc/cpuinfo and cache it if errors didn't happen. +llvm::Expected> GetProcfsCpuInfo(); + +/// \return +/// A list of available logical core ids given the contents of +/// /proc/cpuinfo. +llvm::Expected> +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> GetAvailableLogicalCoreIDs(); + +} // namespace process_linux +} // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Linux/Procfs.cpp b/lldb/source/Plugins/Process/Linux/Procfs.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/Procfs.cpp @@ -0,0 +1,71 @@ +//===-- Procfs.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 "Procfs.h" + +#include "lldb/Host/linux/Support.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace lldb_private; +using namespace process_linux; +using namespace llvm; + +Expected> lldb_private::process_linux::GetProcfsCpuInfo() { + static Optional> cpu_info; + if (!cpu_info) { + auto buffer_or_error = errorOrToExpected(getProcFile("cpuinfo")); + if (!buffer_or_error) + return buffer_or_error.takeError(); + MemoryBuffer &buffer = **buffer_or_error; + cpu_info = std::vector( + reinterpret_cast(buffer.getBufferStart()), + reinterpret_cast(buffer.getBufferEnd())); + } + return *cpu_info; +} + +Expected> +lldb_private::process_linux::GetAvailableLogicalCoreIDs(StringRef cpuinfo) { + SmallVector lines; + cpuinfo.split(lines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + std::vector logical_cores; + + for (StringRef line : lines) { + std::pair key_value = line.split(':'); + auto key = key_value.first.trim(); + auto val = key_value.second.trim(); + if (key == "processor") { + int processor; + if (val.getAsInteger(10, processor)) + return createStringError( + inconvertibleErrorCode(), + "Failed parsing the /proc/cpuinfo line entry: %s", line.data()); + logical_cores.push_back(processor); + } + } + return logical_cores; +} + +llvm::Expected> +lldb_private::process_linux::GetAvailableLogicalCoreIDs() { + static Optional> logical_cores_ids; + if (!logical_cores_ids) { + // We find the actual list of core ids by parsing /proc/cpuinfo + Expected> cpuinfo = GetProcfsCpuInfo(); + if (!cpuinfo) + return cpuinfo.takeError(); + + Expected> core_ids = GetAvailableLogicalCoreIDs( + StringRef(reinterpret_cast(cpuinfo->data()))); + if (!core_ids) + return core_ids.takeError(); + + logical_cores_ids.emplace(std::move(*core_ids)); + } + return *logical_cores_ids; +} diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -81,7 +81,8 @@ for (const ThreadPostMortemTraceSP &thread : traced_threads) { m_thread_decoders.emplace(thread->GetID(), std::make_unique(thread, *this)); - SetPostMortemThreadDataFile(thread->GetID(), "threadTraceBuffer", + SetPostMortemThreadDataFile(thread->GetID(), + IntelPTDataKinds::kThreadTraceBuffer, thread->GetTraceFile()); } } @@ -179,7 +180,8 @@ } Expected TraceIntelPT::GetCPUInfoForLiveProcess() { - Expected> cpu_info = GetLiveProcessBinaryData("cpuInfo"); + Expected> cpu_info = + GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo); if (!cpu_info) return cpu_info.takeError(); @@ -393,7 +395,8 @@ Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, OnBinaryDataReadCallback callback) { - return OnThreadBinaryDataRead(tid, "threadTraceBuffer", callback); + return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kThreadTraceBuffer, + callback); } TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp @@ -48,8 +48,8 @@ return json_intel_pt_trace.takeError(); llvm::Expected json_session_description = - TraceSessionSaver::BuildProcessesSection(*live_process, - "threadTraceBuffer", directory); + TraceSessionSaver::BuildProcessesSection( + *live_process, IntelPTDataKinds::kThreadTraceBuffer, directory); if (!json_session_description) return json_session_description.takeError(); diff --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp --- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp +++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -13,6 +13,9 @@ namespace lldb_private { +const char *IntelPTDataKinds::kProcFsCpuInfo = "procFsCpuInfo"; +const char *IntelPTDataKinds::kThreadTraceBuffer = "threadTraceBuffer"; + bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet, Path path) { ObjectMapper o(value, path); diff --git a/lldb/unittests/Process/Linux/CMakeLists.txt b/lldb/unittests/Process/Linux/CMakeLists.txt --- a/lldb/unittests/Process/Linux/CMakeLists.txt +++ b/lldb/unittests/Process/Linux/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_unittest(ProcessLinuxTests IntelPTCollectorTests.cpp PerfTests.cpp + ProcfsTests.cpp LINK_LIBS lldbPluginProcessLinux diff --git a/lldb/unittests/Process/Linux/PerfTests.cpp b/lldb/unittests/Process/Linux/PerfTests.cpp --- a/lldb/unittests/Process/Linux/PerfTests.cpp +++ b/lldb/unittests/Process/Linux/PerfTests.cpp @@ -10,9 +10,13 @@ #include "Perf.h" +#include "lldb/Host/linux/Support.h" + #include "llvm/Support/Error.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" + #include #include diff --git a/lldb/unittests/Process/Linux/ProcfsTests.cpp b/lldb/unittests/Process/Linux/ProcfsTests.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Process/Linux/ProcfsTests.cpp @@ -0,0 +1,104 @@ +//===-- ProcfsTests.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 "Procfs.h" + +#include "lldb/Host/linux/Support.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace process_linux; +using namespace llvm; + +TEST(Perf, HardcodedLogicalCoreIDs) { + Expected> core_ids = + GetAvailableLogicalCoreIDs(R"(processor : 13 +vendor_id : GenuineIntel +cpu family : 6 +model : 85 +model name : Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz +stepping : 4 +microcode : 0x2000065 +cpu MHz : 2886.370 +cache size : 28160 KB +physical id : 1 +siblings : 40 +core id : 19 +cpu cores : 20 +apicid : 103 +initial apicid : 103 +fpu : yes +fpu_exception : yes +cpuid level : 22 +power management: + +processor : 24 +vendor_id : GenuineIntel +cpu family : 6 +model : 85 +model name : Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz +stepping : 4 +microcode : 0x2000065 +cpu MHz : 2768.494 +cache size : 28160 KB +physical id : 1 +siblings : 40 +core id : 20 +cpu cores : 20 +apicid : 105 +power management: + +processor : 35 +vendor_id : GenuineIntel +cpu family : 6 +model : 85 +model name : Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz +stepping : 4 +microcode : 0x2000065 +cpu MHz : 2884.703 +cache size : 28160 KB +physical id : 1 +siblings : 40 +core id : 24 +cpu cores : 20 +apicid : 113 + +processor : 79 +vendor_id : GenuineIntel +cpu family : 6 +model : 85 +model name : Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz +stepping : 4 +microcode : 0x2000065 +cpu MHz : 3073.955 +cache size : 28160 KB +physical id : 1 +siblings : 40 +core id : 28 +cpu cores : 20 +apicid : 121 +power management: +)"); + + ASSERT_TRUE((bool)core_ids); + ASSERT_THAT(*core_ids, ::testing::ElementsAre(13, 24, 35, 79)); +} + +TEST(Perf, RealLogicalCoreIDs) { + // We first check we can read /proc/cpuinfo + auto buffer_or_error = errorOrToExpected(getProcFile("cpuinfo")); + if (!buffer_or_error) + GTEST_SKIP() << toString(buffer_or_error.takeError()); + + // At this point we shouldn't fail parsing the core ids + Expected> core_ids = GetAvailableLogicalCoreIDs(); + ASSERT_TRUE((bool)core_ids); + ASSERT_GT((int)core_ids->size(), 0) << "We must see at least one core"; +}