Index: lldb/docs/lldb-gdb-remote.txt =================================================================== --- lldb/docs/lldb-gdb-remote.txt +++ lldb/docs/lldb-gdb-remote.txt @@ -451,7 +451,16 @@ // "size": , // Size in bytes of this thread data. // }, -// }] +// }], +// "tsc_conversion"?: Optional<{ +// "kind": , +// Timestamp counter conversion rate kind, e.g. perf. +// The kind strings can be found in the overriden toJSON method +// of each TimestampCounterRate subclass. The kind determines +// what fields are available in the remainder of the response. +// +// ... additional parameters specific to the provided tracing type and tsc_conversion kind +// }> // } // // NOTES @@ -463,6 +472,33 @@ // Binary data kinds: // - threadTraceBuffer: trace buffer for a thread. // - cpuInfo: contents of the /proc/cpuinfo file. +// +// Additional params in the tsc_conversion output schema when kind == "perf": +// { +// "time_mult": , +// time_mult field of the Linux perf_event_mmap_page struct available when +// cap_usr_time is set. Used in TSC to nanosecond conversion calculation defined +// by the Linux perf_event API. +// +// "time_shift": , +// time_shift field of the Linux perf_event_mmap_page struct available when +// cap_usr_time is set. Used in TSC to nanosecond conversion calculation defined +// by the Linux perf_event API. +// +// "time_zero": , +// time_zero field of the Linux perf_event_mmap_page struct available when +// cap_usr_time_zero is set. Used in TSC to nanosecond conversion calculation defined +// by the Linux perf_event API. +// } +// +// Notes: +// - See https://en.wikipedia.org/wiki/Time_Stamp_Counter for information on the timestamp counter (TSC). +// - "tsc_conversion" optionally represents the information for converting the timestamp counter to the +// number of nanoseconds since its reset. +// - "time_mult", "time_shift" and "time_zero" are the three values needed to convert TSC to nanoseconds defined by +// 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 the conversion calculation. +// //---------------------------------------------------------------------- send packet: jLLDBTraceGetState:{"type":}] Index: lldb/include/lldb/Target/Trace.h =================================================================== --- lldb/include/lldb/Target/Trace.h +++ lldb/include/lldb/Target/Trace.h @@ -302,10 +302,14 @@ /// /// This is invoked by RefreshLiveProcessState when a new state is found. /// - /// \param[in] state - /// The jLLDBTraceGetState response. - virtual void - DoRefreshLiveProcessState(llvm::Expected state) = 0; + /// \param[in] json_string + /// JSON string representing the jLLDBTraceGetState response, + /// it may be invalid. + /// + /// \return + /// The packet response, None if response parsing failed. + virtual llvm::Optional + DoRefreshLiveProcessState(llvm::Expected json_string) = 0; /// Method to be invoked by the plug-in to refresh the live process state. /// Index: lldb/include/lldb/Target/TraceCursor.h =================================================================== --- lldb/include/lldb/Target/TraceCursor.h +++ lldb/include/lldb/Target/TraceCursor.h @@ -189,6 +189,9 @@ /// The timestamp or \b llvm::None if not available. virtual llvm::Optional GetTimestampCounter() = 0; + // TODO: add docs and rename once naming convention is agreed upon + virtual llvm::Optional GetNanos() = 0; + /// \return /// The \a lldb::TraceInstructionControlFlowType categories the /// instruction the cursor is pointing at falls into. If the cursor points Index: lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h =================================================================== --- lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h +++ lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h @@ -38,6 +38,50 @@ llvm::json::Path path); llvm::json::Value toJSON(const TraceIntelPTStartRequest &packet); + +/// Class that can be extended to represent different CPU Timestamp Counter (TSC) conversion +/// rates, which can be used to convert TSCs to common units of time, such as nanoseconds. More +/// on TSCs can be found here https://en.wikipedia.org/wiki/Time_Stamp_Counter. +// TODO: update after naming convention is agreed upon +class TimestampCounterRate { + public: + virtual ~TimestampCounterRate() = default; + /// Convert a TSC value to nanoseconds. This represents the number of nanoseconds since + /// the TSC's reset. + virtual uint64_t ToNanos(uint64_t tsc) = 0; + virtual llvm::json::Value toJSON() = 0; +}; + +using TimestampCounterRateSP = std::shared_ptr ; + +/// 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. +class PerfTimestampCounterRate : public TimestampCounterRate { + public: + PerfTimestampCounterRate(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) {} + uint64_t ToNanos(uint64_t tsc) override; + llvm::json::Value toJSON() override; + + private: + uint32_t m_time_mult; + uint16_t m_time_shift; + uint64_t m_time_zero; +}; + +/// jLLDBTraceGetState gdb-remote packet +struct TraceIntelPTGetStateResponse : TraceGetStateResponse { + /// Timestamp counter rate if one exists, otherwise `nullptr`. + TimestampCounterRateSP tsc_rate; +}; + +bool fromJSON(const llvm::json::Value &value, TimestampCounterRateSP &tsc_converter, 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 Index: lldb/source/Plugins/Process/Linux/IntelPTManager.h =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTManager.h +++ lldb/source/Plugins/Process/Linux/IntelPTManager.h @@ -23,6 +23,36 @@ namespace process_linux { +namespace resource_handle { + +class munmap_delete { + size_t m_length; + +public: + munmap_delete(size_t length) : m_length(length) {} + void operator()(void *ptr) { + if (m_length) + munmap(ptr, m_length); + } +}; + +class file_close { + +public: + file_close() = default; + void operator()(int *ptr) { + if (ptr == nullptr) + return; + if (*ptr == -1) + return; + close(*ptr); + std::default_delete()(ptr); +} +}; + +} // namespace resource_handle + + /// This class keeps track of one tracing instance of /// Intel(R) Processor Trace on Linux OS at thread level. /// @@ -32,34 +62,9 @@ class IntelPTThreadTrace { - class munmap_delete { - size_t m_length; - - public: - munmap_delete(size_t length) : m_length(length) {} - void operator()(void *ptr) { - if (m_length) - munmap(ptr, m_length); - } - }; - - class file_close { - - public: - file_close() = default; - void operator()(int *ptr) { - if (ptr == nullptr) - return; - if (*ptr == -1) - return; - close(*ptr); - std::default_delete()(ptr); - } - }; - - std::unique_ptr m_mmap_meta; - std::unique_ptr m_mmap_aux; - std::unique_ptr m_fd; + std::unique_ptr m_mmap_meta; + std::unique_ptr m_mmap_aux; + std::unique_ptr m_fd; lldb::tid_t m_tid; /// Start tracing a thread @@ -88,8 +93,8 @@ llvm::MutableArrayRef GetDataBuffer() const; IntelPTThreadTrace() - : m_mmap_meta(nullptr, munmap_delete(0)), - m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()) {} + : m_mmap_meta(nullptr, resource_handle::munmap_delete(0)), + m_mmap_aux(nullptr, resource_handle::munmap_delete(0)), m_fd(nullptr, resource_handle::file_close()) {} public: /// Get the content of /proc/cpuinfo that can be later used to decode traces. @@ -203,11 +208,20 @@ }; /// Main class that manages intel-pt process and thread tracing. -class IntelPTManager { +class IntelPTCollector { public: - IntelPTManager(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {} + IntelPTCollector(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {} static bool IsSupported(); + /// Start tracing a thread + /// + /// \param[in] pid + /// The process id of the thread being traced. + /// + /// \return + /// \a The timestamp counter rate if one exists, \a nullptr if no conversion exists. + static TimestampCounterRateSP GetTscRate(lldb::pid_t pid); + /// If "process tracing" is enabled, then trace the given thread. llvm::Error OnThreadCreated(lldb::tid_t tid); @@ -235,6 +249,9 @@ /// Dispose of all traces void Clear(); + /// Server-side cache of timestamp counter rate + static llvm::Optional g_tsc_rate; + private: llvm::Error TraceStop(lldb::tid_t tid); Index: lldb/source/Plugins/Process/Linux/IntelPTManager.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/IntelPTManager.cpp +++ lldb/source/Plugins/Process/Linux/IntelPTManager.cpp @@ -8,8 +8,10 @@ #include #include +#include #include +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" @@ -18,8 +20,10 @@ #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Host/linux/Support.h" #include "lldb/Utility/StreamString.h" +#include "lldb/lldb-types.h" #include +#include #include using namespace lldb; @@ -42,6 +46,8 @@ const char *kPSBPeriodBitOffsetFile = "/sys/bus/event_source/devices/intel_pt/format/psb_period"; +llvm::Optional IntelPTCollector::g_tsc_rate = llvm::None; + enum IntelPTConfigFileType { Hex = 0, // 0 or 1 @@ -236,7 +242,7 @@ "perf event syscall failed"); } - m_fd = std::unique_ptr(new int(fd), file_close()); + m_fd = std::unique_ptr(new int(fd), resource_handle::file_close()); errno = 0; auto base = @@ -248,9 +254,9 @@ "Meta buffer allocation failed"); } - m_mmap_meta = std::unique_ptr( + m_mmap_meta = std::unique_ptr( reinterpret_cast(base), - munmap_delete(buffer_size + page_size)); + resource_handle::munmap_delete(buffer_size + page_size)); m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size; m_mmap_meta->aux_size = buffer_size; @@ -264,8 +270,8 @@ return createStringError(inconvertibleErrorCode(), "Trace buffer allocation failed"); } - m_mmap_aux = std::unique_ptr( - reinterpret_cast(mmap_aux), munmap_delete(buffer_size)); + m_mmap_aux = std::unique_ptr( + reinterpret_cast(mmap_aux), resource_handle::munmap_delete(buffer_size)); return Error::success(); #endif } @@ -564,15 +570,15 @@ return m_thread_traces; } -/// IntelPTManager +/// IntelPTCollector -Error IntelPTManager::TraceStop(lldb::tid_t tid) { +Error IntelPTCollector::TraceStop(lldb::tid_t tid) { if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) return m_process_trace->TraceStop(tid); return m_thread_traces.TraceStop(tid); } -Error IntelPTManager::TraceStop(const TraceStopRequest &request) { +Error IntelPTCollector::TraceStop(const TraceStopRequest &request) { if (request.IsProcessTracing()) { Clear(); return Error::success(); @@ -585,7 +591,7 @@ } } -Error IntelPTManager::TraceStart( +Error IntelPTCollector::TraceStart( const TraceIntelPTStartRequest &request, const std::vector &process_threads) { if (request.IsProcessTracing()) { @@ -609,13 +615,13 @@ } } -Error IntelPTManager::OnThreadCreated(lldb::tid_t tid) { +Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) { if (!IsProcessTracingEnabled()) return Error::success(); return m_process_trace->TraceStart(tid); } -Error IntelPTManager::OnThreadDestroyed(lldb::tid_t tid) { +Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) { if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) return m_process_trace->TraceStop(tid); else if (m_thread_traces.TracesThread(tid)) @@ -623,12 +629,12 @@ return Error::success(); } -Expected IntelPTManager::GetState() const { +Expected IntelPTCollector::GetState() const { Expected> cpu_info = IntelPTThreadTrace::GetCPUInfo(); if (!cpu_info) return cpu_info.takeError(); - TraceGetStateResponse state; + TraceIntelPTGetStateResponse state; state.processBinaryData.push_back( {"cpuInfo", static_cast(cpu_info->size())}); @@ -642,18 +648,20 @@ state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), thread_states.end()); } + + state.tsc_rate = IntelPTCollector::GetTscRate(m_pid); return toJSON(state); } Expected -IntelPTManager::GetTracedThread(lldb::tid_t tid) const { +IntelPTCollector::GetTracedThread(lldb::tid_t tid) const { if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid)) return m_process_trace->GetThreadTraces().GetTracedThread(tid); return m_thread_traces.GetTracedThread(tid); } Expected> -IntelPTManager::GetBinaryData(const TraceGetBinaryDataRequest &request) const { +IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const { if (request.kind == "threadTraceBuffer") { if (Expected trace = GetTracedThread(*request.tid)) @@ -668,9 +676,9 @@ request.kind.c_str()); } -void IntelPTManager::ClearProcessTracing() { m_process_trace = None; } +void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; } -bool IntelPTManager::IsSupported() { +bool IntelPTCollector::IsSupported() { Expected intel_pt_type = GetOSEventType(); if (!intel_pt_type) { llvm::consumeError(intel_pt_type.takeError()); @@ -679,11 +687,70 @@ return true; } -bool IntelPTManager::IsProcessTracingEnabled() const { +bool IntelPTCollector::IsProcessTracingEnabled() const { return (bool)m_process_trace; } -void IntelPTManager::Clear() { +void IntelPTCollector::Clear() { ClearProcessTracing(); m_thread_traces.Clear(); } + +llvm::Expected PerfGetTscRate(lldb::pid_t pid) { + Log *log = GetLog(POSIXLog::Ptrace); + perf_event_attr attr; + memset(&attr, 0, sizeof(attr)); + // Set the minimum attributes necessary to access the mmap metadata page. + attr.size = sizeof(attr); + attr.type = PERF_TYPE_SOFTWARE; + attr.config = PERF_COUNT_SW_DUMMY; + attr.sample_type = PERF_SAMPLE_TIME; + + errno = 0; + auto fd = + syscall(SYS_perf_event_open, &attr, pid, -1, -1, 0); + if (fd == -1) { + LLDB_LOG(log, "syscall error {0}", errno); + return createStringError(inconvertibleErrorCode(), "perf event syscall failed"); + } + auto fd_handle = std::unique_ptr(new int(fd), resource_handle::file_close()); + + uint64_t page_size = getpagesize(); + errno = 0; + auto base = + mmap(nullptr, page_size, PROT_READ, MAP_SHARED, fd, 0); + + if (base == MAP_FAILED) { + // TODO: should logging be done in this function or should the error just be returned here and let + // the caller log/consume the error? + LLDB_LOG(log, "mmap base error {0}", errno); + return createStringError(inconvertibleErrorCode(), "perf mmap meta allocation failed"); + } + auto perf_mmap_meta_handle = + std::unique_ptr( + reinterpret_cast(base), + resource_handle::munmap_delete(page_size)); + + if (perf_mmap_meta_handle->cap_user_time && perf_mmap_meta_handle->cap_user_time_zero) { + return std::make_shared(perf_mmap_meta_handle->time_mult, perf_mmap_meta_handle->time_shift, perf_mmap_meta_handle->time_zero); + } else { + auto err_cap = !perf_mmap_meta_handle->cap_user_time ? "cap_user_time" : "cap_user_time_zero"; + LLDB_LOG(log, "{0} not supported. TSC cannot be converted to time unit", err_cap); + return createStringError(inconvertibleErrorCode(), "TSC cannot be converted to time unit due to unsupported capabilities"); + } +} + +TimestampCounterRateSP IntelPTCollector::GetTscRate(lldb::pid_t pid) { +#ifndef PERF_ATTR_SIZE_VER5 + llvm_unreachable("Intel PT Linux perf event not supported"); +#else + if (!g_tsc_rate) { + g_tsc_rate = nullptr; + // Try to get a TSC rate from each different source until one succeeds + // Add additional sources of TSC rate, as necessary + if (llvm::Expected perf_tsc_rate = PerfGetTscRate(pid)) + g_tsc_rate = *perf_tsc_rate; + } + return *g_tsc_rate; +#endif +} Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -241,7 +241,7 @@ Status PopulateMemoryRegionCache(); /// Manages Intel PT process and thread traces. - IntelPTManager m_intel_pt_manager; + IntelPTCollector m_intel_pt_collector; // Handle a clone()-like event. bool MonitorClone(NativeThreadLinux &parent, lldb::pid_t child_pid, 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_intel_pt_manager(pid) { + m_main_loop(mainloop), m_intel_pt_collector(pid) { if (m_terminal_fd != -1) { Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); assert(status.Success()); @@ -983,7 +983,7 @@ e; // Save the error, but still attempt to detach from other threads. } - m_intel_pt_manager.Clear(); + m_intel_pt_collector.Clear(); return error; } @@ -1666,7 +1666,7 @@ Status NativeProcessLinux::NotifyTracersOfNewThread(lldb::tid_t tid) { Log *log = GetLog(POSIXLog::Thread); - Status error(m_intel_pt_manager.OnThreadCreated(tid)); + Status error(m_intel_pt_collector.OnThreadCreated(tid)); if (error.Fail()) LLDB_LOG(log, "Failed to trace a new thread with intel-pt, tid = {0}. {1}", tid, error.AsCString()); @@ -1675,7 +1675,7 @@ Status NativeProcessLinux::NotifyTracersOfThreadDestroyed(lldb::tid_t tid) { Log *log = GetLog(POSIXLog::Thread); - Status error(m_intel_pt_manager.OnThreadDestroyed(tid)); + Status error(m_intel_pt_collector.OnThreadDestroyed(tid)); if (error.Fail()) LLDB_LOG(log, "Failed to stop a destroyed thread with intel-pt, tid = {0}. {1}", @@ -1938,7 +1938,7 @@ } llvm::Expected NativeProcessLinux::TraceSupported() { - if (IntelPTManager::IsSupported()) + if (IntelPTCollector::IsSupported()) return TraceSupportedResponse{"intel-pt", "Intel Processor Trace"}; return NativeProcessProtocol::TraceSupported(); } @@ -1951,7 +1951,7 @@ std::vector process_threads; for (auto &thread : m_threads) process_threads.push_back(thread->GetID()); - return m_intel_pt_manager.TraceStart(*request, process_threads); + return m_intel_pt_collector.TraceStart(*request, process_threads); } else return request.takeError(); } @@ -1961,19 +1961,19 @@ Error NativeProcessLinux::TraceStop(const TraceStopRequest &request) { if (request.type == "intel-pt") - return m_intel_pt_manager.TraceStop(request); + return m_intel_pt_collector.TraceStop(request); return NativeProcessProtocol::TraceStop(request); } Expected NativeProcessLinux::TraceGetState(StringRef type) { if (type == "intel-pt") - return m_intel_pt_manager.GetState(); + return m_intel_pt_collector.GetState(); return NativeProcessProtocol::TraceGetState(type); } Expected> NativeProcessLinux::TraceGetBinaryData( const TraceGetBinaryDataRequest &request) { if (request.type == "intel-pt") - return m_intel_pt_manager.GetBinaryData(request); + return m_intel_pt_collector.GetBinaryData(request); return NativeProcessProtocol::TraceGetBinaryData(request); } Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h +++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h @@ -22,6 +22,9 @@ namespace lldb_private { namespace trace_intel_pt { +// Forward declaration for use in DecodedThread class +class TraceIntelPT; + /// Class for representing a libipt decoding error. class IntelPTError : public llvm::ErrorInfo { public: @@ -128,7 +131,8 @@ public: DecodedThread(lldb::ThreadSP thread_sp, std::vector &&instructions, - size_t raw_trace_size); + size_t raw_trace_size, + TraceIntelPT &trace); /// Constructor with a single error signaling a complete failure of the /// decoding process. @@ -144,6 +148,9 @@ /// Get a new cursor for the decoded thread. lldb::TraceCursorUP GetCursor(); + /// Get the TSC conversion params for the decoded thread. + TimestampCounterRateSP GetTscConversionParams() const; + /// Get the size in bytes of the corresponding Intel PT raw trace /// /// \return @@ -154,6 +161,7 @@ lldb::ThreadSP m_thread_sp; std::vector m_instructions; size_t m_raw_trace_size; + TimestampCounterRateSP m_tsc_conversion_params; }; using DecodedThreadSP = std::shared_ptr; Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp +++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp @@ -105,9 +105,10 @@ DecodedThread::DecodedThread(ThreadSP thread_sp, std::vector &&instructions, - size_t raw_trace_size) + size_t raw_trace_size, + TraceIntelPT &trace) : m_thread_sp(thread_sp), m_instructions(std::move(instructions)), - m_raw_trace_size(raw_trace_size) { + m_raw_trace_size(raw_trace_size), m_tsc_conversion_params(trace.GetTimestampCounterRate()) { if (m_instructions.empty()) m_instructions.emplace_back( createStringError(inconvertibleErrorCode(), "empty trace")); @@ -116,3 +117,7 @@ lldb::TraceCursorUP DecodedThread::GetCursor() { return std::make_unique(m_thread_sp, shared_from_this()); } + +TimestampCounterRateSP DecodedThread::GetTscConversionParams() const { + return m_tsc_conversion_params; +} Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp +++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp @@ -258,7 +258,7 @@ m_trace_thread->GetTraceFile(), raw_trace_size)) return std::make_shared(m_trace_thread->shared_from_this(), std::move(*instructions), - raw_trace_size); + raw_trace_size, m_trace); else return std::make_shared(m_trace_thread->shared_from_this(), instructions.takeError()); @@ -272,7 +272,7 @@ if (Expected> instructions = DecodeLiveThread(*m_thread_sp, m_trace, raw_trace_size)) return std::make_shared( - m_thread_sp, std::move(*instructions), raw_trace_size); + m_thread_sp, std::move(*instructions), raw_trace_size, m_trace); else return std::make_shared(m_thread_sp, instructions.takeError()); Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h +++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h @@ -30,6 +30,8 @@ llvm::Optional GetTimestampCounter() override; + llvm::Optional GetNanos() override; + lldb::TraceInstructionControlFlowType GetInstructionControlFlowType() override; Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp @@ -7,11 +7,13 @@ //===----------------------------------------------------------------------===// #include "TraceCursorIntelPT.h" + #include "DecodedThread.h" -#include "TraceIntelPT.h" +#include #include + using namespace lldb; using namespace lldb_private; using namespace lldb_private::trace_intel_pt; @@ -89,6 +91,14 @@ return m_decoded_thread_sp->GetInstructions()[m_pos].GetTimestampCounter(); } +Optional TraceCursorIntelPT::GetNanos() { + if (Optional tsc = GetTimestampCounter()) { + if (TimestampCounterRateSP conversion_params_sp = m_decoded_thread_sp->GetTscConversionParams()) + return conversion_params_sp->ToNanos(*tsc); + } + return llvm::None; +} + TraceInstructionControlFlowType TraceCursorIntelPT::GetInstructionControlFlowType() { lldb::addr_t next_load_address = Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -12,6 +12,7 @@ #include "IntelPTDecoder.h" #include "TraceIntelPTSessionFileParser.h" #include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "lldb/lldb-types.h" #include "llvm/Support/raw_ostream.h" @@ -74,8 +75,8 @@ llvm::Optional GetRawTraceSize(Thread &thread); - void DoRefreshLiveProcessState( - llvm::Expected state) override; + llvm::Optional DoRefreshLiveProcessState( + llvm::Expected json_string) override; bool IsTraced(lldb::tid_t tid) override; @@ -142,6 +143,9 @@ llvm::Expected GetCPUInfo(); + // TODO: rename and add docs after naming convention is agreed upon + TimestampCounterRateSP GetTimestampCounterRate(); + /// Get the current traced live process. /// /// \return @@ -159,7 +163,8 @@ /// files are fixed. TraceIntelPT( const pt_cpu &cpu_info, - const std::vector &traced_threads); + const std::vector &traced_threads, + const TimestampCounterRateSP tsc_conversion_params_sp); /// Constructor for live processes TraceIntelPT(Process &live_process) @@ -183,8 +188,13 @@ std::map> m_thread_decoders; /// Error gotten after a failed live process update, if any. llvm::Optional m_live_refresh_error; + /// \a llvm::None indicates that the program has not yet attempted to fetch the timestamp conversion rate. + /// After attempting to fetch, this represents the timestamp counter rate if one exists, otherwise `nullptr`. + llvm::Optional m_tsc_rate; }; +using TraceIntelPTSP = std::shared_ptr; + } // namespace trace_intel_pt } // namespace lldb_private 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 @@ -17,6 +17,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/TraceGDBRemotePackets.h" #include "llvm/ADT/None.h" using namespace lldb; @@ -76,8 +77,9 @@ TraceIntelPT::TraceIntelPT( const pt_cpu &cpu_info, - const std::vector &traced_threads) - : m_cpu_info(cpu_info) { + const std::vector &traced_threads, + const TimestampCounterRateSP tsc_conversion_params_sp) + : m_cpu_info(cpu_info), m_tsc_rate(tsc_conversion_params_sp) { for (const ThreadPostMortemTraceSP &thread : traced_threads) m_thread_decoders.emplace( thread->GetID(), @@ -183,15 +185,28 @@ return *m_cpu_info; } +TimestampCounterRateSP TraceIntelPT::GetTimestampCounterRate() { + return m_tsc_rate ? *m_tsc_rate : nullptr; +} + Process *TraceIntelPT::GetLiveProcess() { return m_live_process; } -void TraceIntelPT::DoRefreshLiveProcessState( - Expected state) { +llvm::Optional TraceIntelPT::DoRefreshLiveProcessState( + Expected json_string) { + + if (!json_string) { + m_live_refresh_error = toString(json_string.takeError()); + return llvm::None; + } + m_thread_decoders.clear(); + Expected state = + json::parse(*json_string, "TraceIntelPTGetStateResponse"); + if (!state) { m_live_refresh_error = toString(state.takeError()); - return; + return llvm::None; } for (const TraceThreadState &thread_state : state->tracedThreads) { @@ -200,6 +215,8 @@ m_thread_decoders.emplace( thread_state.tid, std::make_unique(thread, *this)); } + m_tsc_rate = state->tsc_rate; + return state.get(); } bool TraceIntelPT::IsTraced(lldb::tid_t tid) { Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h @@ -9,6 +9,7 @@ #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H +#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" #include "../common/TraceJSONStructs.h" #include @@ -34,6 +35,7 @@ struct JSONTraceIntelPTTrace { std::string type; JSONTraceIntelPTCPUInfo cpuInfo; + TimestampCounterRateSP tsc_conversion_params; }; struct JSONTraceIntelPTSession { @@ -43,6 +45,7 @@ struct JSONTraceIntelPTSettings : JSONTracePluginSettings { JSONTraceIntelPTCPUInfo cpuInfo; + TimestampCounterRateSP tsc_conversion_params; }; } // namespace trace_intel_pt Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp @@ -22,8 +22,9 @@ bool fromJSON(const Value &value, JSONTraceIntelPTSettings &plugin_settings, Path path) { ObjectMapper o(value, path); - return o && o.map("cpuInfo", plugin_settings.cpuInfo) && - fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path); + return o && o.map("cpuInfo", plugin_settings.cpuInfo) + && o.mapOptional("tsc_conversion_params", plugin_settings.tsc_conversion_params) + && fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path); } bool fromJSON(const json::Value &value, JSONTraceIntelPTCPUInfo &cpu_info, @@ -45,6 +46,7 @@ llvm::json::Object json_trace; json_trace["type"] = trace.type; json_trace["cpuInfo"] = toJSON(trace.cpuInfo); + json_trace["tsc_conversion_params"] = trace.tsc_conversion_params->toJSON(); return std::move(json_trace); } Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h @@ -43,7 +43,8 @@ lldb::TraceSP CreateTraceIntelPTInstance(const pt_cpu &cpu_info, - std::vector &parsed_processes); + std::vector &parsed_processes, + TimestampCounterRateSP tsc_conversion_params_sp); private: static pt_cpu ParsePTCPU(const JSONTraceIntelPTCPUInfo &cpu_info); Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp @@ -19,6 +19,10 @@ StringRef TraceIntelPTSessionFileParser::GetSchema() { static std::string schema; if (schema.empty()) { + // TODO: how to cleanly describe tsc_conversion_params in the schema strings + // given its potential different variants and it being optional? + // For now just putting perf variant until + // feedback is gathered schema = TraceSessionFileParser::BuildSchema(R"({ "type": "intel-pt", "cpuInfo": { @@ -26,6 +30,12 @@ "family": integer, "model": integer, "stepping": integer + }, + "tsc_conversion_params"?: { + "kind": "perf", + "time_mult": integer, + "time_shift": integer, + "time_zero": integer } })"); } @@ -41,13 +51,14 @@ } TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance( - const pt_cpu &cpu_info, std::vector &parsed_processes) { + const pt_cpu &cpu_info, std::vector &parsed_processes, + TimestampCounterRateSP tsc_conversion_params_sp) { std::vector threads; for (const ParsedProcess &parsed_process : parsed_processes) threads.insert(threads.end(), parsed_process.threads.begin(), parsed_process.threads.end()); - TraceSP trace_instance(new TraceIntelPT(cpu_info, threads)); + TraceSP trace_instance(new TraceIntelPT(cpu_info, threads, tsc_conversion_params_sp)); for (const ParsedProcess &parsed_process : parsed_processes) parsed_process.target_sp->SetTrace(trace_instance); @@ -63,7 +74,7 @@ if (Expected> parsed_processes = ParseCommonSessionFile(session)) return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.cpuInfo), - *parsed_processes); + *parsed_processes, session.trace.tsc_conversion_params); else return parsed_processes.takeError(); } Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp @@ -75,5 +75,6 @@ return cpu_info.takeError(); return JSONTraceIntelPTTrace{"intel-pt", - JSONTraceIntelPTCPUInfo(cpu_info.get())}; + JSONTraceIntelPTCPUInfo(cpu_info.get()), + trace_ipt.GetTimestampCounterRate()}; } Index: lldb/source/Target/Trace.cpp =================================================================== --- lldb/source/Target/Trace.cpp +++ lldb/source/Target/Trace.cpp @@ -18,6 +18,7 @@ #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Stream.h" +#include "lldb/Utility/TraceGDBRemotePackets.h" using namespace lldb; using namespace lldb_private; @@ -187,20 +188,12 @@ m_stop_id = new_stop_id; m_live_thread_data.clear(); - Expected json_string = GetLiveProcessState(); - if (!json_string) { - DoRefreshLiveProcessState(json_string.takeError()); + llvm::Optional live_process_state = DoRefreshLiveProcessState(GetLiveProcessState()); + if (!live_process_state) return; - } - Expected live_process_state = - json::parse(*json_string, "TraceGetStateResponse"); - if (!live_process_state) { - DoRefreshLiveProcessState(live_process_state.takeError()); - return; - } for (const TraceThreadState &thread_state : - live_process_state->tracedThreads) { + live_process_state->tracedThreads) { for (const TraceBinaryData &item : thread_state.binaryData) m_live_thread_data[thread_state.tid][item.kind] = item.size; } @@ -208,7 +201,6 @@ for (const TraceBinaryData &item : live_process_state->processBinaryData) m_live_process_data[item.kind] = item.size; - DoRefreshLiveProcessState(std::move(live_process_state)); } uint32_t Trace::GetStopID() { Index: lldb/source/Target/TraceInstructionDumper.cpp =================================================================== --- lldb/source/Target/TraceInstructionDumper.cpp +++ lldb/source/Target/TraceInstructionDumper.cpp @@ -185,6 +185,10 @@ if (Optional timestamp = m_cursor_up->GetTimestampCounter()) s.Printf("0x%016" PRIx64, *timestamp); + // TODO: temporary hacky way to see new timestamp functionality + // How do we want to incorporate the nanosecon values into the dump output? + if (Optional timestamp_nanos = m_cursor_up->GetNanos()) + s.Printf(" NANOS 0x%016" PRIx64, *timestamp_nanos); else s.Printf("unavailable"); Index: lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp =================================================================== --- lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp +++ lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Utility/TraceIntelPTGDBRemotePackets.h" +#include "lldb/Utility/TraceGDBRemotePackets.h" using namespace llvm; using namespace llvm::json; @@ -43,4 +44,51 @@ return base; } +uint64_t PerfTimestampCounterRate::ToNanos(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 m_time_zero + quot * m_time_mult + ((rem * m_time_mult) >> m_time_shift); +} + +bool fromJSON(const llvm::json::Value &value, TimestampCounterRateSP &tsc_rate, llvm::json::Path path) { + std::string tsc_rate_kind; + ObjectMapper o(value, path); + + if(!o.map("kind", tsc_rate_kind)) + return false; + + if (tsc_rate_kind == "perf") { + 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_rate = std::make_shared((uint32_t)time_mult, (uint16_t)time_shift, (uint64_t)time_zero); + return true; + } + path.report("Unsupported TSC rate kind"); + return false; +} + +bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, Path path) { + ObjectMapper o(value, path); + return o && fromJSON(value, (TraceGetStateResponse &)packet, path) && o.mapOptional("tsc_rate", packet.tsc_rate); +} + +llvm::json::Value PerfTimestampCounterRate::toJSON() { + return json::Value(json::Object{ + {"kind", "perf"}, + {"time_mult", (int64_t) m_time_mult}, + {"time_shift", (int64_t) m_time_shift}, + {"time_zero", (int64_t) m_time_zero}, + }); +} + +json::Value toJSON(const TraceIntelPTGetStateResponse &packet) { + json::Value base = toJSON((const TraceGetStateResponse &)packet); + if (packet.tsc_rate) + base.getAsObject()->try_emplace("tsc_rate", packet.tsc_rate->toJSON()); + + return base; +} } // namespace lldb_private