Index: lldb/docs/lldb-gdb-remote.txt =================================================================== --- lldb/docs/lldb-gdb-remote.txt +++ lldb/docs/lldb-gdb-remote.txt @@ -451,7 +451,8 @@ // "size": , // Size in bytes of this thread data. // }, -// }] +// }], +// ... other parameters specific to the provided tracing type // } // // NOTES @@ -463,6 +464,19 @@ // Binary data kinds: // - threadTraceBuffer: trace buffer for a thread. // - cpuInfo: contents of the /proc/cpuinfo file. +// Additional parameters in the output schema: +// +// tsc_rate: { +// kind: "perf", +// time_mult: int, +// time_shift: int, +// time_zero: int +// | +// kind: "freq" +// freq_conversion: int, +// } + +} //---------------------------------------------------------------------- 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,13 @@ /// /// 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 + /// String representation of the jLLDBTraceGetState response. + /// + /// \return + /// Unique pointer to the packet response, nullptr if response parsing failed. + virtual std::unique_ptr + DoRefreshLiveProcessState(llvm::Expected json_string) = 0; /// Method to be invoked by the plug-in to refresh the live process state. /// Index: lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h =================================================================== --- lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h +++ lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h @@ -11,6 +11,7 @@ #include "lldb/Utility/TraceGDBRemotePackets.h" + /// See docs/lldb-gdb-remote.txt for more information. namespace lldb_private { @@ -38,6 +39,48 @@ llvm::json::Path path); llvm::json::Value toJSON(const TraceIntelPTStartRequest &packet); + +/// TSC to nanosecond conversion. +class TimestampCounterRate { + public: + virtual ~TimestampCounterRate() = default; + /// Convert TSC value to nanoseconds. This represents the number of nanoseconds since + /// the TSC register's reset. + virtual uint64_t ToNanos(uint64_t tsc) = 0; + virtual llvm::json::Value toJSON() = 0; +}; + +typedef std::shared_ptr TimestampCounterRateSP; + +/// TSC to nanoseconds conversion values defined in struct perf_event_mmap_page. +/// See https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more information. +class PerfTimestampCounterRate : public TimestampCounterRate { + public: + PerfTimestampCounterRate() = default; + 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(static_cast(time_zero)) {} + uint64_t ToNanos(uint64_t tsc) override; + llvm::json::Value toJSON() override; + + // TODO: make implementation details private. + uint32_t m_time_mult; + uint16_t m_time_shift; + uint64_t m_time_zero; +}; + +/// jLLDBTraceGetState gdb-remote packet +/// Contains additional information related to TSC -> nanosecond conversion. +struct TraceIntelPTGetStateResponse : TraceGetStateResponse { + /// `nullptr` if no tsc conversion rate exists. + llvm::Optional 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 @@ -208,6 +208,8 @@ IntelPTManager(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {} static bool IsSupported(); + static void GetPerfTscRate(perf_event_mmap_page &mmap_meta); + /// If "process tracing" is enabled, then trace the given thread. llvm::Error OnThreadCreated(lldb::tid_t tid); @@ -235,6 +237,8 @@ /// Dispose of all traces void Clear(); + 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 @@ -42,6 +42,8 @@ const char *kPSBPeriodBitOffsetFile = "/sys/bus/event_source/devices/intel_pt/format/psb_period"; +llvm::Optional IntelPTManager::g_tsc_rate = llvm::None; + enum IntelPTConfigFileType { Hex = 0, // 0 or 1 @@ -252,6 +254,8 @@ reinterpret_cast(base), munmap_delete(buffer_size + page_size)); + IntelPTManager::GetPerfTscRate(*m_mmap_meta); + m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size; m_mmap_meta->aux_size = buffer_size; @@ -628,7 +632,7 @@ if (!cpu_info) return cpu_info.takeError(); - TraceGetStateResponse state; + TraceIntelPTGetStateResponse state; state.processBinaryData.push_back( {"cpuInfo", static_cast(cpu_info->size())}); @@ -642,6 +646,9 @@ state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), thread_states.end()); } + + state.tsc_rate = IntelPTManager::g_tsc_rate; + return toJSON(state); } @@ -687,3 +694,13 @@ ClearProcessTracing(); m_thread_traces.Clear(); } + +void IntelPTManager::GetPerfTscRate(perf_event_mmap_page &mmap_meta) { + if (!IntelPTManager::g_tsc_rate) { + if (mmap_meta.cap_user_time_zero) { + IntelPTManager::g_tsc_rate = std::make_shared(mmap_meta.time_mult, mmap_meta.time_shift, mmap_meta.time_zero); + } + } else { + IntelPTManager::g_tsc_rate = nullptr; + } +} 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; + std::unique_ptr DoRefreshLiveProcessState( + llvm::Expected json_string) override; bool IsTraced(lldb::tid_t tid) override; @@ -183,6 +184,8 @@ std::map> m_thread_decoders; /// Error gotten after a failed live process update, if any. llvm::Optional m_live_refresh_error; + /// TSC to nano conversion rate. `nullptr` if no conversion exists. + llvm::Optional m_tsc_rate; }; } // namespace trace_intel_pt 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; @@ -185,13 +186,22 @@ Process *TraceIntelPT::GetLiveProcess() { return m_live_process; } -void TraceIntelPT::DoRefreshLiveProcessState( - Expected state) { +std::unique_ptr TraceIntelPT::DoRefreshLiveProcessState( + Expected json_string) { + + if (!json_string) { + m_live_refresh_error = toString(json_string.takeError()); + return nullptr; + } + m_thread_decoders.clear(); + Expected state = + json::parse(*json_string, "TraceIntelPTGetStateResponse"); + if (!state) { m_live_refresh_error = toString(state.takeError()); - return; + return nullptr; } for (const TraceThreadState &thread_state : state->tracedThreads) { @@ -200,6 +210,9 @@ m_thread_decoders.emplace( thread_state.tid, std::make_unique(thread, *this)); } + m_tsc_rate = state->tsc_rate; + + return std::make_unique(state.get()); } bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 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,28 +188,17 @@ m_stop_id = new_stop_id; m_live_thread_data.clear(); - Expected json_string = GetLiveProcessState(); - if (!json_string) { - DoRefreshLiveProcessState(json_string.takeError()); - return; - } - Expected live_process_state = - json::parse(*json_string, "TraceGetStateResponse"); - if (!live_process_state) { - DoRefreshLiveProcessState(live_process_state.takeError()); - return; - } + if (std::unique_ptr live_process_state = DoRefreshLiveProcessState(GetLiveProcessState())) { + for (const TraceThreadState &thread_state : + live_process_state->tracedThreads) { + for (const TraceBinaryData &item : thread_state.binaryData) + m_live_thread_data[thread_state.tid][item.kind] = item.size; + } - for (const TraceThreadState &thread_state : - live_process_state->tracedThreads) { - for (const TraceBinaryData &item : thread_state.binaryData) - m_live_thread_data[thread_state.tid][item.kind] = item.size; + for (const TraceBinaryData &item : live_process_state->processBinaryData) + m_live_process_data[item.kind] = item.size; } - 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/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) { + // conversion logic here + return 1; +} + +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") { + int a, b, c; + if (!o.map("a", a) || !o.map("b", b) || !o.map("c", c)) + return false; + tsc_rate = std::make_shared((uint32_t)a, (uint16_t)b, (uint64_t)c); + } + return false; +} + +bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, Path path) { + ObjectMapper o(value, path); + bool base_bool = o && fromJSON(value, (TraceGetStateResponse &)packet, path) && o.mapOptional("tsc_rate", packet.tsc_rate); + return base_bool; + +} + +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}, // TODO: handle the potential lossy conversion correctly + }); +} + +json::Value toJSON(const TraceIntelPTGetStateResponse &packet) { + json::Value base = toJSON((const TraceGetStateResponse &)packet); + auto tsc_rate = packet.tsc_rate; + // is there a better way to check that it's not None nor nullptr? + if (tsc_rate && *tsc_rate) { + json::Value tsc_converter_json = tsc_rate.getValue()->toJSON(); + base.getAsObject()->try_emplace("tsc_rate", std::move(tsc_converter_json)); + } + return base; +} } // namespace lldb_private