Index: lldb/docs/lldb-gdb-remote.txt =================================================================== --- lldb/docs/lldb-gdb-remote.txt +++ lldb/docs/lldb-gdb-remote.txt @@ -451,7 +451,12 @@ // "size": , // Size in bytes of this thread 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. +// } // } // // NOTES @@ -463,6 +468,26 @@ // Binary data kinds: // - threadTraceBuffer: trace buffer for a thread. // - cpuInfo: contents of the /proc/cpuinfo file. +// +// Counter info kinds: +// tsc-perf-zero-conversion: +// +// This field 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. +/// +// Sub-schema for this field: +// +// { +// "tsc-perf-zero-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,31 @@ llvm::json::Value toJSON(const TraceThreadState &packet); +/// Interface for different algorithms used to convert trace +/// counters into different units. +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 Convert(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 TraceTscConversionUP = + std::unique_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,10 @@ #include "lldb/Utility/TraceGDBRemotePackets.h" +#include "llvm/Support/JSON.h" + +#include + /// See docs/lldb-gdb-remote.txt for more information. namespace lldb_private { @@ -40,6 +44,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 LinuxPerfZeroTscConversion + : public TraceCounterConversion { +public: + /// Create new \a LinuxPerfZeroTscConversion object from the conversion values + /// defined in the Linux perf_event_open API. + LinuxPerfZeroTscConversion(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) {} + + /// Convert TSC value to nanosecond wall time. The beginning of time (0 + /// nanoseconds) is defined by the kernel at boot time and has no particularly + /// useful meaning. On the other hand, this value is constant for an entire + /// trace session. + // See 'time_zero' section of + // https://man7.org/linux/man-pages/man2/perf_event_open.2.html + /// + /// \param[in] tsc + /// The TSC value to be converted. + /// + /// \return + /// Nanosecond wall time. + std::chrono::nanoseconds Convert(uint64_t raw_counter_value) override; + + llvm::json::Value toJSON() override; + +private: + uint32_t m_time_mult; + uint16_t m_time_shift; + uint64_t m_time_zero; +}; + +struct TraceIntelPTGetStateResponse : TraceGetStateResponse { + /// The TSC to wall time conversion if it exists, otherwise \b nullptr. + TraceTscConversionUP tsc_conversion; +}; + +bool fromJSON(const llvm::json::Value &value, + TraceTscConversionUP &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. + TraceTscConversionUP 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 @@ -522,6 +522,17 @@ /// IntelPTCollector +IntelPTCollector::IntelPTCollector(lldb::pid_t pid) + : m_pid(pid), m_thread_traces(pid) { + if (Expected tsc_conversion = + LoadPerfTscConversionParameters()) + m_tsc_conversion = + std::make_unique(*tsc_conversion); + else + LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(), + "unable to load TSC to wall time conversion: {0}"); +} + 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,9 @@ 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 +/// Load \a PerfTscConversionParameters from \a perf_event_mmap_page, if /// available. -llvm::Expected FetchPerfTscConversionParameters(); +llvm::Expected LoadPerfTscConversionParameters(); } // 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 @@ -8,15 +8,9 @@ #include "Perf.h" -#include "lldb/lldb-types.h" - -#include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" -#include -#include -#include #include #include #include @@ -25,8 +19,8 @@ using namespace process_linux; using namespace llvm; -Expected -lldb_private::process_linux::FetchPerfTscConversionParameters() { +Expected +lldb_private::process_linux::LoadPerfTscConversionParameters() { lldb::pid_t pid = getpid(); perf_event_attr attr; memset(&attr, 0, sizeof(attr)); @@ -43,7 +37,7 @@ perf_event_mmap_page &mmap_metada = perf_event->GetMetadataPage(); if (mmap_metada.cap_user_time && mmap_metada.cap_user_time_zero) { - return PerfTscConversionParameters{ + return LinuxPerfZeroTscConversion{ mmap_metada.time_mult, mmap_metada.time_shift, mmap_metada.time_zero}; } else { auto err_cap = @@ -56,16 +50,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/Plugins/Process/POSIX/ProcessPOSIXLog.h =================================================================== --- lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h +++ lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h @@ -23,7 +23,8 @@ Registers = Log::ChannelFlag<4>, Thread = Log::ChannelFlag<5>, Watchpoints = Log::ChannelFlag<6>, - LLVM_MARK_AS_BITMASK_ENUM(Watchpoints) + Trace = Log::ChannelFlag<7>, + LLVM_MARK_AS_BITMASK_ENUM(Trace) }; LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); Index: lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp =================================================================== --- lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp +++ lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -43,4 +43,69 @@ return base; } +std::chrono::nanoseconds +LinuxPerfZeroTscConversion::Convert(uint64_t raw_counter_value) { + uint64_t quot = raw_counter_value >> m_time_shift; + uint64_t rem_flag = (((uint64_t)1 << m_time_shift) - 1); + uint64_t rem = raw_counter_value & rem_flag; + return std::chrono::nanoseconds{m_time_zero + quot * m_time_mult + + ((rem * m_time_mult) >> m_time_shift)}; +} + +json::Value LinuxPerfZeroTscConversion::toJSON() { + return json::Value(json::Object{ + {"kind", "tsc-perf-zero-conversion"}, + {"time_mult", static_cast(m_time_mult)}, + {"time_shift", static_cast(m_time_shift)}, + {"time_zero", static_cast(m_time_zero)}, + }); +} + +bool fromJSON(const json::Value &value, TraceTscConversionUP &tsc_conversion, + json::Path path) { + ObjectMapper o(value, path); + + int64_t time_mult, time_shift, time_zero; + if (!o || !o.map("time_mult", time_mult) || + !o.map("time_shift", time_shift) || !o.map("time_zero", time_zero)) + return false; + + tsc_conversion = + std::make_unique(LinuxPerfZeroTscConversion{ + static_cast(time_mult), static_cast(time_shift), + static_cast(time_zero)}); + + return true; +} + +bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, + json::Path path) { + ObjectMapper o(value, path); + if (!o || !fromJSON(value, (TraceGetStateResponse &)packet, path)) + return false; + + const Object &obj = *(value.getAsObject()); + if (const json::Value *counters = obj.get("counters")) { + json::Path subpath = path.field("counters"); + ObjectMapper ocounters(*counters, subpath); + if (!ocounters || !ocounters.mapOptional("tsc-perf-zero-conversion", + packet.tsc_conversion)) + return false; + } + return true; +} + +json::Value toJSON(const TraceIntelPTGetStateResponse &packet) { + json::Value base = toJSON((const TraceGetStateResponse &)packet); + + if (packet.tsc_conversion) { + std::vector counters{}; + base.getAsObject()->try_emplace( + "counters", json::Object{{"tsc-perf-zero-conversion", + packet.tsc_conversion->toJSON()}}); + } + + 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,8 @@ const int SLEEP_SECS = 1; std::chrono::nanoseconds SLEEP_NANOS{std::chrono::seconds(SLEEP_SECS)}; - Expected params = - FetchPerfTscConversionParameters(); + Expected params = + LoadPerfTscConversionParameters(); // Skip the test if the conversion parameters aren't available. if (!params) @@ -76,8 +76,7 @@ GTEST_SKIP() << toString(tsc_after_sleep.takeError()); std::chrono::nanoseconds converted_tsc_diff = - params->ToWallTime(*tsc_after_sleep) - - params->ToWallTime(*tsc_before_sleep); + params->Convert(*tsc_after_sleep) - params->Convert(*tsc_before_sleep); std::chrono::microseconds acceptable_overhead(500);