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_conversion: { +// 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,59 @@ llvm::json::Path path); llvm::json::Value toJSON(const TraceIntelPTStartRequest &packet); + +/// 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. +struct PerfTscConversionValues { + // units should be uint32_t, uint16_t, uint64_t, respectively but since this will be converted to json these must be int64_t. + // This would only possibly be problematic for m_time_zero. + // TODO: how should the above issue be handled? Currently blindly casting time_zero to narrower type. + int64_t m_time_mult; + int64_t m_time_shift; + int64_t m_time_zero; +}; + +/// TSC to nanosecond conversion. +struct TscConverter { + virtual ~TscConverter() = default; + /// Convert TSC value to nanoseconds. + virtual uint64_t TscToNanos(uint64_t tsc) = 0; + virtual llvm::json::Value toJSON() = 0; +}; + +/// Conversion based on values define in perf_event_mmap_page. +struct PerfTscConverter : TscConverter { + PerfTscConverter() = default; + PerfTscConverter(uint32_t time_mult, uint16_t time_shift, uint64_t time_zero) : + m_perf_conversion{time_mult, time_shift, static_cast(time_zero)} {} + uint64_t TscToNanos(uint64_t tsc) override; + llvm::json::Value toJSON() override; + + PerfTscConversionValues m_perf_conversion; +}; + +/// Conversion based on frequency conversion value defined in sys/fs file(s). +//struct FreqTscConverter : TscConverter { +// FreqTscConverter(uint64_t freq_conversion) = default; +// FreqTscConverter(uint64_t freq_conversion) : m_freq_conversion(freq_conversion) {} +// uint64_t TscToNanos(uint64_t tsc) override; +// uint64_t m_freq_conversion; +//}; + +/// jLLDBTraceGetState gdb-remote packet +/// Contains additional information related to TSC -> nanosecond conversion. +struct TraceIntelPTGetStateResponse : TraceGetStateResponse { + /// Implementor of `TscConverter` interface. + /// `nullptr` if no TscConverter exists. + std::shared_ptr tsc_converter; +}; + +bool fromJSON(const llvm::json::Value &value, std::shared_ptr &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 @@ -235,6 +235,8 @@ /// Dispose of all traces void Clear(); + static std::shared_ptr tsc_converter; + 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"; +std::shared_ptr IntelPTManager::tsc_converter = nullptr; + enum IntelPTConfigFileType { Hex = 0, // 0 or 1 @@ -252,6 +254,10 @@ reinterpret_cast(base), munmap_delete(buffer_size + page_size)); + if (m_mmap_meta->cap_user_time_zero) { + IntelPTManager::tsc_converter = std::make_shared(PerfTscConverter(m_mmap_meta->time_mult, m_mmap_meta->time_shift, m_mmap_meta->time_zero)); + } + m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size; m_mmap_meta->aux_size = buffer_size; @@ -628,7 +634,7 @@ if (!cpu_info) return cpu_info.takeError(); - TraceGetStateResponse state; + TraceIntelPTGetStateResponse state; state.processBinaryData.push_back( {"cpuInfo", static_cast(cpu_info->size())}); @@ -642,6 +648,9 @@ state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(), thread_states.end()); } + + state.tsc_converter = IntelPTManager::tsc_converter; + return toJSON(state); } 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 converter. nullptr if no conversion exists. + std::shared_ptr m_tsc_converter; }; } // 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,7 +17,9 @@ #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" +#include using namespace lldb; using namespace lldb_private; @@ -185,13 +187,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 +211,10 @@ m_thread_decoders.emplace( thread_state.tid, std::make_unique(thread, *this)); } + if (!m_tsc_converter && state->tsc_converter) + m_tsc_converter = state->tsc_converter; + + 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,19 @@ 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; - } + 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; - } + if (live_process_state) { + 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,55 @@ return base; } +uint64_t PerfTscConverter::TscToNanos(uint64_t tsc) { + // conversion logic here + return 1; +} + +bool fromJSON(const llvm::json::Value &value, std::shared_ptr &tsc_converter, llvm::json::Path path) { + std::string tsc_converter_kind; + ObjectMapper o(value, path); + + bool ret = o.map("kind", tsc_converter_kind); + + if (tsc_converter_kind == "perf") { + std::shared_ptr perf_tsc_converter_sp = std::make_shared(PerfTscConverter()); + ret = ret && o.map("time_mult", perf_tsc_converter_sp->m_perf_conversion.m_time_mult) && o.map("time_shift", perf_tsc_converter_sp->m_perf_conversion.m_time_shift) && o.map("time_zero", perf_tsc_converter_sp->m_perf_conversion.m_time_zero); + // should we check ret here? + tsc_converter = perf_tsc_converter_sp; + } else if (tsc_converter_kind == "freq") { + // TODO + tsc_converter = nullptr; + } else { + tsc_converter = nullptr; + } + return ret; +} + +bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, Path path) { + ObjectMapper o(value, path); + bool base_bool = o && fromJSON(value, (TraceGetStateResponse &)packet, path) && o.map("tsc_conversion", packet.tsc_converter); + // call perftscconverter + // get an instance of perftscconverter and wrap it in optionaltoJSON(); + base.getAsObject()->try_emplace("tsc_conversion", std::move(tsc_converter_json)); + } + return base; +} } // namespace lldb_private