Index: lldb/docs/lldb-gdb-remote.txt =================================================================== --- lldb/docs/lldb-gdb-remote.txt +++ lldb/docs/lldb-gdb-remote.txt @@ -451,18 +451,45 @@ // "size": , // Size in bytes of this thread data. // }, -// }] +// ], +// "counters"?: [ +// { +// "kind": , +// The available kinds are described below. +// +// ... additional parameters specific to the provided counter_conversion kind +// }, +// ] // } // // NOTES // - "traceThreads" includes all thread traced by both "process tracing" and // "thread tracing". +// - "counters" optionally includes information related to counter values in the trace. // // INTEL PT // // Binary data kinds: // - threadTraceBuffer: trace buffer for a thread. // - cpuInfo: contents of the /proc/cpuinfo file. +// +// Counter kinds: +// tsc-perf-conversion: +// +// This counter kind allows converting Intel processor's TSC values to a wall time. +// It is available through the Linux perf_event API when cap_user_time and cap_user_time_zero +// are set. +// See the documentation of time_zero in +// https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more information about +// the calculation and the meaning of the values in the schema below. +/// +// Additional params in the schema when "kind" is "tsc-perf_conversion": +// +// { +// "time_mult": , +// "time_shift": , +// "time_zero": , +// } //---------------------------------------------------------------------- send packet: jLLDBTraceGetState:{"type":}] Index: lldb/include/lldb/Utility/TraceGDBRemotePackets.h =================================================================== --- lldb/include/lldb/Utility/TraceGDBRemotePackets.h +++ 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" #include "lldb/lldb-enumerations.h" @@ -116,6 +117,29 @@ llvm::json::Value toJSON(const TraceThreadState &packet); +template class TraceCounterConversion { +public: + virtual ~TraceCounterConversion() = default; + + /// Convert from raw counter value to the target type. + /// + /// \param[in] raw_counter_value + /// The raw counter value to be converted. + /// + /// \return + /// The converted counter value. + virtual ToType DoConversion(uint64_t raw_counter_value) = 0; + + /// Serialize trace counter conversion values to JSON. + /// + /// \return + /// \a llvm::json::Value representing the trace counter conversion object. + virtual llvm::json::Value toJSON() = 0; +}; + +using TraceTscConversionSP = + std::shared_ptr>; + struct TraceGetStateResponse { std::vector tracedThreads; std::vector processBinaryData; Index: lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h =================================================================== --- lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h +++ lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h @@ -11,6 +11,8 @@ #include "lldb/Utility/TraceGDBRemotePackets.h" +#include + /// See docs/lldb-gdb-remote.txt for more information. namespace lldb_private { @@ -40,6 +42,60 @@ llvm::json::Value toJSON(const TraceIntelPTStartRequest &packet); /// \} +/// jLLDBTraceGetState gdb-remote packet +/// \{ + +/// TSC to wall time conversion values defined in the Linux perf_event_open API +/// when the capibilities cap_user_time and cap_user_time_zero are set. See the +/// See the documentation of `time_zero` in +/// https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more +/// information. +class LinuxPerfTscConversion + : public TraceCounterConversion { +public: + /// Create new \a LinuxPerfTscConversion object from the conversion values + /// defined in the Linux perf_event_open API. + LinuxPerfTscConversion(uint32_t time_mult, uint16_t time_shift, + uint64_t time_zero) + : m_time_mult(time_mult), m_time_shift(time_shift), + m_time_zero(time_zero) {} + + /// See \a ToWallTime. + std::chrono::nanoseconds DoConversion(uint64_t raw_counter_value) override; + + llvm::json::Value toJSON() override; + +private: + /// Convert TSC value to nanosecond wall time. + // See 'time_zero' section of + // https://man7.org/linux/man-pages/man2/perf_event_open.2.html + /// + /// \a param[in] tsc + /// The TSC value to be converted. + /// + /// \return + /// Nanosecond wall time. + std::chrono::nanoseconds ToWallTime(uint64_t tsc); + + uint32_t m_time_mult; + uint16_t m_time_shift; + uint64_t m_time_zero; +}; + +struct TraceIntelPTGetStateResponse : TraceGetStateResponse { + /// TSC conversion params if they exists, otherwise `nullptr`. + TraceTscConversionSP tsc_conversion; +}; + +bool fromJSON(const llvm::json::Value &value, + TraceTscConversionSP &tsc_conversion, llvm::json::Path path); + +bool fromJSON(const llvm::json::Value &value, + TraceIntelPTGetStateResponse &packet, llvm::json::Path path); + +llvm::json::Value toJSON(const TraceIntelPTGetStateResponse &packet); +/// \} + } // namespace lldb_private #endif // LLDB_UTILITY_TRACEINTELPTGDBREMOTEPACKETS_H Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.h =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTCollector.h +++ lldb/source/Plugins/Process/Linux/IntelPTCollector.h @@ -185,7 +185,7 @@ /// Main class that manages intel-pt process and thread tracing. class IntelPTCollector { public: - IntelPTCollector(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {} + IntelPTCollector(lldb::pid_t pid); static bool IsSupported(); @@ -235,6 +235,8 @@ /// Threads traced due to "process tracing". Only one active "process tracing" /// instance is assumed for a single process. llvm::Optional m_process_trace; + /// TSC to wall time conversion. + TraceTscConversionSP m_tsc_conversion; }; } // namespace process_linux Index: lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp +++ lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp @@ -13,6 +13,7 @@ #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Host/linux/Support.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -522,6 +523,19 @@ /// IntelPTCollector +IntelPTCollector::IntelPTCollector(lldb::pid_t pid) + : m_pid(pid), m_thread_traces(pid) { + if (Expected tsc_conversion = + FetchPerfTscConversionParameters()) + m_tsc_conversion = TraceTscConversionSP(&*tsc_conversion); + else { + m_tsc_conversion = TraceTscConversionSP(nullptr); + // The error condition is indicated to the client via the nullptr. + // TODO: is this appropriate and should we log the specific error? + consumeError(tsc_conversion.takeError()); + } +} + Error IntelPTCollector::TraceStop(lldb::tid_t tid) { if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) return m_process_trace->TraceStop(tid); Index: lldb/source/Plugins/Process/Linux/Perf.h =================================================================== --- lldb/source/Plugins/Process/Linux/Perf.h +++ lldb/source/Plugins/Process/Linux/Perf.h @@ -15,6 +15,7 @@ #ifndef LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H #define LLDB_SOURCE_PLUGINS_PROCESS_LINUX_PERF_H +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "lldb/lldb-types.h" #include "llvm/Support/Error.h" @@ -232,29 +233,10 @@ resource_handle::MmapUP m_aux_base; }; -/// TSC to nanoseconds conversion values defined by the Linux perf_event API -/// when the capibilities cap_user_time and cap_user_time_zero are set. See the -/// documentation of `time_zero` in -/// https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more -/// information. -struct PerfTscConversionParameters { - uint32_t m_time_mult; - uint16_t m_time_shift; - uint64_t m_time_zero; - - /// Convert TSC value to nanosecond wall time. - /// - /// \a param[in] tsc - /// The TSC value to be converted. - /// - /// \return - /// Nanosecond wall time. - std::chrono::nanoseconds ToWallTime(uint64_t tsc); -}; /// Fetch \a PerfTscConversionParameters from \a perf_event_mmap_page, if /// available. -llvm::Expected FetchPerfTscConversionParameters(); +llvm::Expected FetchPerfTscConversionParameters(); } // namespace process_linux } // namespace lldb_private Index: lldb/source/Plugins/Process/Linux/Perf.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/Perf.cpp +++ lldb/source/Plugins/Process/Linux/Perf.cpp @@ -9,6 +9,8 @@ #include "Perf.h" #include "lldb/lldb-types.h" +// Should this be included here and in the header? +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" @@ -25,7 +27,7 @@ using namespace process_linux; using namespace llvm; -Expected +Expected lldb_private::process_linux::FetchPerfTscConversionParameters() { lldb::pid_t pid = getpid(); perf_event_attr attr; @@ -43,8 +45,8 @@ perf_event_mmap_page &mmap_metada = perf_event->GetMetadataPage(); if (mmap_metada.cap_user_time && mmap_metada.cap_user_time_zero) { - return PerfTscConversionParameters{ - mmap_metada.time_mult, mmap_metada.time_shift, mmap_metada.time_zero}; + return LinuxPerfTscConversion{mmap_metada.time_mult, mmap_metada.time_shift, + mmap_metada.time_zero}; } else { auto err_cap = !mmap_metada.cap_user_time ? "cap_user_time" : "cap_user_time_zero"; @@ -56,16 +58,6 @@ } } -std::chrono::nanoseconds PerfTscConversionParameters::ToWallTime(uint64_t tsc) { - // See 'time_zero' section of - // https://man7.org/linux/man-pages/man2/perf_event_open.2.html - uint64_t quot = tsc >> m_time_shift; - uint64_t rem_flag = (((uint64_t)1 << m_time_shift) - 1); - uint64_t rem = tsc & rem_flag; - return std::chrono::nanoseconds{m_time_zero + quot * m_time_mult + - ((rem * m_time_mult) >> m_time_shift)}; -} - void resource_handle::MmapDeleter::operator()(void *ptr) { if (m_bytes && ptr != nullptr) munmap(ptr, m_bytes); Index: lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp =================================================================== --- lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp +++ lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -7,6 +7,10 @@ //===----------------------------------------------------------------------===// #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" +#include "llvm/Support/JSON.h" + +#include +#include using namespace llvm; using namespace llvm::json; @@ -43,4 +47,69 @@ return base; } +std::chrono::nanoseconds +LinuxPerfTscConversion::DoConversion(uint64_t raw_counter_value) { + return ToWallTime(raw_counter_value); +} + +llvm::json::Value LinuxPerfTscConversion::toJSON() { + return json::Value(json::Object{ + {"kind", "tsc-perf-conversion"}, + {"time_mult", (int64_t)m_time_mult}, + {"time_shift", (int64_t)m_time_shift}, + {"time_zero", (int64_t)m_time_zero}, + }); +} + +std::chrono::nanoseconds LinuxPerfTscConversion::ToWallTime(uint64_t tsc) { + uint64_t quot = tsc >> m_time_shift; + uint64_t rem_flag = (((uint64_t)1 << m_time_shift) - 1); + uint64_t rem = tsc & rem_flag; + return std::chrono::nanoseconds{m_time_zero + quot * m_time_mult + + ((rem * m_time_mult) >> m_time_shift)}; +} + +bool fromJSON(const llvm::json::Value &value, + TraceTscConversionSP &tsc_conversion, llvm::json::Path path) { + std::string kind; + ObjectMapper o(value, path); + + if (!o || !o.map("kind", kind)) + return false; + if (kind == "tsc-perf-conversion") { + int64_t time_mult, time_shift, time_zero; + if (!o.map("time_mult", time_mult) || !o.map("time_shift", time_shift) || + !o.map("time_zero", time_zero)) + return false; + + tsc_conversion = TraceTscConversionSP(new LinuxPerfTscConversion{ + static_cast(time_mult), + static_cast(time_shift), + static_cast(time_zero), + }); + return true; + } + path.report("Unsupported TSC conversion kind"); + return false; +} + +bool fromJSON(const llvm::json::Value &value, + TraceIntelPTGetStateResponse &packet, llvm::json::Path path) { + ObjectMapper o(value, path); + return o && fromJSON(value, (TraceGetStateResponse &)packet, path) && + o.mapOptional("counters", packet.tsc_conversion); +} + +llvm::json::Value toJSON(const TraceIntelPTGetStateResponse &packet) { + json::Value base = toJSON((const TraceGetStateResponse &)packet); + if (packet.tsc_conversion) { + // TODO: why can't the fromJSON above for TraceTscConversionSP ? + // bc a different fromJSON func is invoked (no path param n such) + std::vector counters{packet.tsc_conversion->toJSON()}; + base.getAsObject()->try_emplace("counters", std::move(counters)); + } + + return base; +} + } // namespace lldb_private Index: lldb/unittests/Process/Linux/PerfTests.cpp =================================================================== --- lldb/unittests/Process/Linux/PerfTests.cpp +++ lldb/unittests/Process/Linux/PerfTests.cpp @@ -58,8 +58,7 @@ const int SLEEP_SECS = 1; std::chrono::nanoseconds SLEEP_NANOS{std::chrono::seconds(SLEEP_SECS)}; - Expected params = - FetchPerfTscConversionParameters(); + Expected params = FetchPerfTscConversionParameters(); // Skip the test if the conversion parameters aren't available. if (!params) @@ -76,8 +75,8 @@ GTEST_SKIP() << toString(tsc_after_sleep.takeError()); std::chrono::nanoseconds converted_tsc_diff = - params->ToWallTime(*tsc_after_sleep) - - params->ToWallTime(*tsc_before_sleep); + params->DoConversion(*tsc_after_sleep) - + params->DoConversion(*tsc_before_sleep); std::chrono::microseconds acceptable_overhead(500);