wallace created this revision. wallace added a reviewer: clayborg. Herald added subscribers: lldb-commits, dang. Herald added a project: LLDB. wallace requested review of this revision. Herald added a subscriber: JDevlieghere.
This implements the interactive trace start and stop methods. There's a lot of boilerplate code due to the gdb-remote protocol, but the main changes are: - Implementation of the jLLDBTraceStop packet, which is quite simple. - Implementation of the jLLDBTraceStart packet, which accepts plug-in specific params. - Implementation of the jLLDBTraceQueryData, used for querying in json format various data, in a generic way so that it can be resused by trace plug-ins. I'm using it for querying the trace buffers and cpu config needed to decode. - Created a set of well-defined structures that represent all packets and params send across the gdb-remote protocol, so that serialization and deserialization with JSON is as strongly-typed as possible. - Created an IntelPTDecoder for live threads, that use the debugger's stop id as checkpoint for its internal cache. - Added tests Depends on D90729 <https://reviews.llvm.org/D90729>. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D91679 Files: lldb/docs/lldb-gdb-remote.txt lldb/include/lldb/Core/PluginManager.h lldb/include/lldb/Host/common/NativeProcessProtocol.h lldb/include/lldb/Target/Process.h lldb/include/lldb/Target/Trace.h lldb/include/lldb/Utility/StringExtractorGDBRemote.h lldb/include/lldb/Utility/TraceOptions.h lldb/include/lldb/lldb-private-interfaces.h lldb/source/Commands/CommandObjectThreadUtil.cpp lldb/source/Commands/CommandObjectThreadUtil.h lldb/source/Core/PluginManager.cpp lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp lldb/source/Plugins/Process/Linux/NativeProcessLinux.h lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp lldb/source/Plugins/Process/Linux/ProcessorTrace.h lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h lldb/source/Target/Trace.cpp lldb/source/Utility/StringExtractorGDBRemote.cpp lldb/source/Utility/TraceOptions.cpp lldb/test/API/commands/trace/TestTraceStartStop.py
Index: lldb/test/API/commands/trace/TestTraceStartStop.py =================================================================== --- lldb/test/API/commands/trace/TestTraceStartStop.py +++ lldb/test/API/commands/trace/TestTraceStartStop.py @@ -3,6 +3,8 @@ from lldbsuite.test import lldbutil from lldbsuite.test.decorators import * +ADDRESS_REGEX = '0x[0-9a-fA-F]*' + class TestTraceLoad(TestBase): mydir = TestBase.compute_mydir(__file__) @@ -49,20 +51,57 @@ self.expect("r") + # This fails because "trace start" hasn't been called yet + self.expect("thread trace stop", error=True, + substrs=["error: Process is not being traced"]) + + # the help command should be the intel-pt one now self.expect("help thread trace start", substrs=["Start tracing one or more threads with intel-pt.", "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"]) - self.expect("thread trace start", - patterns=["would trace tid .* with size_in_kb 4 and custom_config 0"]) - - self.expect("thread trace start --size 20 --custom-config 1", - patterns=["would trace tid .* with size_in_kb 20 and custom_config 1"]) - - # This fails because "trace stop" is not yet implemented. + # We start tracing with a small buffer size + self.expect("thread trace start --size 1") + + # We fail if we try to trace again + self.expect("thread trace start", error=True, + substrs=["error: tracing already active on this thread"]) + + # We can reconstruct the single instruction executed in the first line + self.expect("n") + self.expect("thread trace dump instructions", + patterns=[f'''thread #1: tid = .*, total instructions = 1 + a.out`main \+ 4 at main.cpp:2 + \[0\] {ADDRESS_REGEX} movl''']) + + # We can reconstruct the instructions up to the second line + self.expect("n") + self.expect("thread trace dump instructions", + patterns=[f'''thread #1: tid = .*, total instructions = 5 + a.out`main \+ 4 at main.cpp:2 + \[0\] {ADDRESS_REGEX} movl .* + a.out`main \+ 11 at main.cpp:4 + \[1\] {ADDRESS_REGEX} movl .* + \[2\] {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 + a.out`main \+ 28 at main.cpp:4 + \[3\] {ADDRESS_REGEX} cmpl .* + \[4\] {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5''']) + + # We stop tracing + self.expect("thread trace stop") + + # We can't stop twice self.expect("thread trace stop", error=True, - substrs=["error: Process is not being traced"]) + substrs=["error: Process is not being traced."]) + + # We trace again from scratch + self.expect("thread trace start") + self.expect("n") + self.expect("thread trace dump instructions", + patterns=[f'''thread #1: tid = .*, total instructions = 1 + a.out`main \+ 20 at main.cpp:5 + \[0\] {ADDRESS_REGEX} xorl''']) self.expect("c") # Now the process has finished, so the commands should fail Index: lldb/source/Utility/TraceOptions.cpp =================================================================== --- lldb/source/Utility/TraceOptions.cpp +++ lldb/source/Utility/TraceOptions.cpp @@ -8,11 +8,45 @@ #include "lldb/Utility/TraceOptions.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractor.h" + using namespace lldb_private; namespace llvm { namespace json { +bool fromJSON(const Value &value, TraceStopPacket &packet, Path path) { + ObjectMapper o(value, path); + return o && o.map("type", packet.type) && o.map("tid", packet.tid); +} + +Value toJSON(const TraceStopPacket &packet) { + return Object{{"type", packet.type}, {"tid", packet.tid}}; +} + +Value toJSON(const TraceQueryDataSimplePacket &packet) { + return Object{{"query", packet.query}, {"type", packet.type}}; +} + +bool fromJSON(const Value &value, TraceIntelPTStartPacketParams &packet, + Path path) { + ObjectMapper o(value, path); + return o && o.map("bufferSizeInKB", packet.buffer_size_in_kb) && + o.map("perfConfig", packet.perf_config); +} + +bool fromJSON(const Value &value, TraceStartSimplePacket &packet, Path path) { + ObjectMapper o(value, path); + return o && o.map("type", packet.type) && o.map("tid", packet.tid); +} + +bool fromJSON(const Value &value, TraceQueryDataSimplePacket &packet, + Path path) { + ObjectMapper o(value, path); + return o && o.map("type", packet.type) && o.map("query", packet.query); +} + bool fromJSON(const Value &value, TraceTypeInfo &info, Path path) { ObjectMapper o(value, path); if (!o) @@ -21,5 +55,55 @@ return o.map("name", info.name); } +bool fromJSON(const Value &value, TraceIntelPTCPUConfig &pt_cpu, Path path) { + ObjectMapper o(value, path); + return o && o.map("vendor", pt_cpu.vendor) && + o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) && + o.map("stepping", pt_cpu.stepping); +} + +bool fromJSON(const Value &value, TraceIntelPTBuffer &response, Path path) { + ObjectMapper o(value, path); + std::string string_data; + if (!o || !o.map("data", string_data)) + return false; + + StringExtractor extractor(string_data); + response.data.resize(string_data.size() / 2); + MutableArrayRef<uint8_t> data_ref(response.data); + extractor.GetHexBytesAvail(data_ref); + return true; +} + +bool fromJSON(const Value &value, TraceIntelPTQueryBufferParams ¶ms, + Path path) { + ObjectMapper o(value, path); + return o && o.map("tid", params.tid); +} + +Value toJSON(const TraceIntelPTStartPacketParams &packet) { + return Object{{"bufferSizeInKB", packet.buffer_size_in_kb}, + {"perfConfig", packet.perf_config}}; +} + +Value toJSON(const TraceIntelPTCPUConfig &cpu_config) { + return Object{{"family", cpu_config.family}, + {"model", cpu_config.model}, + {"stepping", cpu_config.stepping}, + {"vendor", cpu_config.vendor}}; +} + +Value toJSON(const TraceIntelPTBuffer &buffer) { + StreamString stream; + for (const uint8_t &datum : buffer.data) + stream.PutHex8(datum); + + return json::Object{{"data", stream.GetString().str()}}; +} + +Value toJSON(const TraceIntelPTQueryBufferParams ¶ms) { + return json::Object{{"tid", params.tid}}; +} + } // namespace json } // namespace llvm Index: lldb/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- lldb/source/Utility/StringExtractorGDBRemote.cpp +++ lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -310,8 +310,15 @@ return eServerPacketType_jTraceStart; if (PACKET_STARTS_WITH("jTraceStop:")) return eServerPacketType_jTraceStop; + if (PACKET_MATCHES("jLLDBTraceSupportedType")) return eServerPacketType_jLLDBTraceSupportedType; + if (PACKET_STARTS_WITH("jLLDBTraceStop:")) + return eServerPacketType_jLLDBTraceStop; + if (PACKET_STARTS_WITH("jLLDBTraceStart:")) + return eServerPacketType_jLLDBTraceStart; + if (PACKET_STARTS_WITH("jLLDBTraceQueryData:")) + return eServerPacketType_jLLDBTraceQueryData; break; case 'v': Index: lldb/source/Target/Trace.cpp =================================================================== --- lldb/source/Target/Trace.cpp +++ lldb/source/Target/Trace.cpp @@ -72,6 +72,19 @@ return createInvalidPlugInError(json_session.trace.type); } +Expected<lldb::TraceSP> Trace::FindPlugin(llvm::StringRef plugin_name, + Process &process) { + if (!process.IsLiveDebugSession()) + return createStringError(inconvertibleErrorCode(), + "Can't trace non-live processes"); + + ConstString name(plugin_name); + if (auto create_callback = + PluginManager::GetTraceCreateCallbackForLiveProcess(name)) + return create_callback(process); + return createInvalidPlugInError(plugin_name); +} + Expected<StringRef> Trace::FindPluginSchema(StringRef name) { ConstString plugin_name(name); StringRef schema = PluginManager::GetTraceSchema(plugin_name); @@ -266,3 +279,13 @@ return index < end_position; }); } + +Expected<std::string> Trace::DoQueryData(Process &process, + const json::Value &packet) { + return process.TraceQueryData(packet); +} + +Error Trace::DoStartTracingThread(Thread &thread, + const llvm::json::Value &packet) { + return thread.GetProcess()->StartTracingThread(packet); +} 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 @@ -19,16 +19,9 @@ class TraceIntelPTSessionFileParser : public TraceSessionFileParser { public: - struct JSONPTCPU { - std::string vendor; - int64_t family; - int64_t model; - int64_t stepping; - }; - struct JSONTraceIntelPTSettings : TraceSessionFileParser::JSONTracePluginSettings { - JSONPTCPU pt_cpu; + TraceIntelPTCPUConfig pt_cpu; }; /// See \a TraceSessionFileParser::TraceSessionFileParser for the description @@ -55,9 +48,9 @@ CreateTraceIntelPTInstance(const pt_cpu &pt_cpu, std::vector<ParsedProcess> &parsed_processes); -private: - pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu); + static pt_cpu ParsePTCPU(const TraceIntelPTCPUConfig &pt_cpu); +private: const llvm::json::Value &m_trace_session_file; }; @@ -67,12 +60,6 @@ namespace llvm { namespace json { -bool fromJSON( - const Value &value, - lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::JSONPTCPU - &pt_cpu, - Path path); - bool fromJSON(const Value &value, lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser:: JSONTraceIntelPTSettings &plugin_settings, 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 @@ -13,6 +13,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/ThreadList.h" #include "lldb/Target/ThreadTrace.h" +#include "lldb/Utility/TraceOptions.h" using namespace lldb; using namespace lldb_private; @@ -35,7 +36,8 @@ return schema; } -pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) { +pt_cpu +TraceIntelPTSessionFileParser::ParsePTCPU(const TraceIntelPTCPUConfig &pt_cpu) { return {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown, static_cast<uint16_t>(pt_cpu.family), static_cast<uint8_t>(pt_cpu.model), @@ -73,14 +75,6 @@ namespace llvm { namespace json { -bool fromJSON(const Value &value, - TraceIntelPTSessionFileParser::JSONPTCPU &pt_cpu, Path path) { - ObjectMapper o(value, path); - return o && o.map("vendor", pt_cpu.vendor) && - o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) && - o.map("stepping", pt_cpu.stepping); -} - bool fromJSON( const Value &value, TraceIntelPTSessionFileParser::JSONTraceIntelPTSettings &plugin_settings, Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td @@ -4,13 +4,14 @@ def thread_trace_start_intel_pt_size: Option<"size", "s">, Group<1>, Arg<"Value">, - Desc<"The size of the trace in KB. The kernel rounds it down to the nearest" - " multiple of 4. Defaults to 4.">; - def thread_trace_start_intel_pt_custom_config: Option<"custom-config", "c">, + Desc<"The size of the trace in KB. It is rounded up to the nearest pwoer of" + " 2. Defaults to 4.">; + def thread_trace_start_intel_pt_custom_config: Option<"perf-config", "c">, Group<1>, Arg<"Value">, - Desc<"Low level bitmask configuration for the kernel based on the values " - "in `grep -H /sys/bus/event_source/devices/intel_pt/format/*`. " + Desc<"Custom low level perf event bitmask configuration for the kernel " + "based on the values in " + "`grep -H /sys/bus/event_source/devices/intel_pt/format/*`. " "See https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt" " for more information. Defaults to 0.">; } 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 @@ -45,8 +45,12 @@ /// \return /// A trace instance or an error in case of failures. static llvm::Expected<lldb::TraceSP> - CreateInstance(const llvm::json::Value &trace_session_file, - llvm::StringRef session_file_dir, Debugger &debugger); + CreateInstanceForSessionFile(const llvm::json::Value &trace_session_file, + llvm::StringRef session_file_dir, + Debugger &debugger); + + static llvm::Expected<lldb::TraceSP> + CreateInstanceForLiveProcess(Process &process); static ConstString GetPluginNameStatic(); @@ -64,6 +68,15 @@ size_t GetCursorPosition(const Thread &thread) override; + llvm::Error StopTracingThread(const Thread &thread) override; + + llvm::Error StartTracingThread(Thread &thread, + const TraceIntelPTStartPacketParams ¶ms); + + static llvm::Expected<TraceIntelPTBuffer> QueryTraceBuffer(Thread &thread); + + static llvm::Expected<pt_cpu> QueryCPUConfig(Process &process); + private: friend class TraceIntelPTSessionFileParser; @@ -73,6 +86,8 @@ TraceIntelPT(const pt_cpu &pt_cpu, const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads); + TraceIntelPT(const pt_cpu &pt_cpu) : m_pt_cpu(pt_cpu), m_thread_decoders(){}; + /// Decode the trace of the given thread that, i.e. recontruct the traced /// instructions. That trace must be managed by this class. /// @@ -86,8 +101,7 @@ const DecodedThread *Decode(const Thread &thread); pt_cpu m_pt_cpu; - std::map<std::pair<lldb::pid_t, lldb::tid_t>, ThreadTraceDecoder> - m_trace_threads; + std::map<const Thread *, std::unique_ptr<ThreadDecoder>> m_thread_decoders; }; } // 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 @@ -28,12 +28,13 @@ void TraceIntelPT::Initialize() { PluginManager::RegisterPlugin( - GetPluginNameStatic(), "Intel Processor Trace", CreateInstance, + GetPluginNameStatic(), "Intel Processor Trace", + CreateInstanceForSessionFile, CreateInstanceForLiveProcess, TraceIntelPTSessionFileParser::GetSchema(), GetStartCommand); } void TraceIntelPT::Terminate() { - PluginManager::UnregisterPlugin(CreateInstance); + PluginManager::UnregisterPlugin(CreateInstanceForSessionFile); } ConstString TraceIntelPT::GetPluginNameStatic() { @@ -55,31 +56,78 @@ void TraceIntelPT::Dump(Stream *s) const {} -Expected<TraceSP> -TraceIntelPT::CreateInstance(const json::Value &trace_session_file, - StringRef session_file_dir, Debugger &debugger) { +Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile( + const json::Value &trace_session_file, StringRef session_file_dir, + Debugger &debugger) { return TraceIntelPTSessionFileParser(debugger, trace_session_file, session_file_dir) .Parse(); } +Error TraceIntelPT::StopTracingThread(const Thread &thread) { + auto it = m_thread_decoders.find(&thread); + if (it == m_thread_decoders.end()) + return createStringError(inconvertibleErrorCode(), + "thread is not being traced"); + if (Error err = + thread.GetProcess()->StopTracingThread(thread.GetID(), "intel-pt")) + return err; + + m_thread_decoders.erase(it); + + // We clear the trace instance for the target we are not tracing any threads. + if (m_thread_decoders.empty()) + thread.GetProcess()->GetTarget().SetTrace(nullptr); + return Error::success(); +} + +Error TraceIntelPT::StartTracingThread( + Thread &thread, const TraceIntelPTStartPacketParams ¶ms) { + if (Error err = Trace::StartTracingThread(thread, params)) + return err; + m_thread_decoders.emplace( + &thread, std::make_unique<LiveThreadDecoder>(thread, m_pt_cpu)); + return Error::success(); +} + +Expected<TraceIntelPTBuffer> TraceIntelPT::QueryTraceBuffer(Thread &thread) { + TraceIntelPTQueryBufferParams params = {static_cast<int64_t>(thread.GetID())}; + return QueryData<TraceIntelPTBuffer>(*thread.GetProcess(), "intel-pt", + "buffer", params); +} + +Expected<pt_cpu> TraceIntelPT::QueryCPUConfig(Process &process) { + if (Expected<TraceIntelPTCPUConfig> cpu_config = + QueryData<TraceIntelPTCPUConfig>(process, "intel-pt", "pt_cpu")) + return TraceIntelPTSessionFileParser::ParsePTCPU(*cpu_config); + else + return cpu_config.takeError(); +} + +Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { + Expected<pt_cpu> pt_cpu = QueryCPUConfig(process); + if (!pt_cpu) + return pt_cpu.takeError(); + + TraceSP instance(new TraceIntelPT(*pt_cpu)); + process.GetTarget().SetTrace(instance); + return instance; +} + TraceIntelPT::TraceIntelPT( const pt_cpu &pt_cpu, const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads) : m_pt_cpu(pt_cpu) { for (const std::shared_ptr<ThreadTrace> &thread : traced_threads) - m_trace_threads.emplace( - std::piecewise_construct, - std::forward_as_tuple(thread->GetProcess()->GetID(), thread->GetID()), - std::forward_as_tuple(thread, pt_cpu)); + m_thread_decoders.emplace( + thread.get(), std::make_unique<ThreadTraceDecoder>(thread, pt_cpu)); } const DecodedThread *TraceIntelPT::Decode(const Thread &thread) { - auto it = m_trace_threads.find( - std::make_pair(thread.GetProcess()->GetID(), thread.GetID())); - if (it == m_trace_threads.end()) + auto it = m_thread_decoders.find(&thread); + if (it == m_thread_decoders.end()) return nullptr; - return &it->second.Decode(); + return &it->second->Decode(); } size_t TraceIntelPT::GetCursorPosition(const Thread &thread) { Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h +++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h @@ -18,24 +18,33 @@ namespace lldb_private { namespace trace_intel_pt { -/// \a lldb_private::ThreadTrace decoder that stores the output from decoding, -/// avoiding recomputations, as decoding is expensive. -class ThreadTraceDecoder { +/// Base class that handles the decoding of a thread and its trace buffer/ file. +class ThreadDecoder { +public: + virtual ~ThreadDecoder() = default; + + /// Decode the thread and store the result internally, to avoid + /// recomputations. + /// + /// \return + /// A \a DecodedThread instance. + virtual const DecodedThread &Decode() = 0; +}; + +/// Decoder implementation for \a lldb_private::ThreadTrace, which are non-live +/// processes that come trace session files. +class ThreadTraceDecoder : public ThreadDecoder { public: /// \param[in] trace_thread /// The thread whose trace file will be decoded. /// /// \param[in] pt_cpu - /// The libipt cpu used when recording the trace. + /// The libipt cpu used when decoding the trace. ThreadTraceDecoder(const std::shared_ptr<ThreadTrace> &trace_thread, const pt_cpu &pt_cpu) : m_trace_thread(trace_thread), m_pt_cpu(pt_cpu), m_decoded_thread() {} - /// Decode the thread and store the result internally. - /// - /// \return - /// A \a DecodedThread instance. - const DecodedThread &Decode(); + const DecodedThread &Decode() override; private: ThreadTraceDecoder(const ThreadTraceDecoder &other) = delete; @@ -46,6 +55,31 @@ llvm::Optional<DecodedThread> m_decoded_thread; }; +class LiveThreadDecoder : public ThreadDecoder { +public: + /// \param[in] thread + /// The thread whose traces will be decoded. + /// + /// \param[in] pt_cpu + /// The libipt cpu used when decoding the trace. + LiveThreadDecoder(Thread &thread, const pt_cpu &pt_cpu); + + /// Decode the current trace for the thread. + /// + /// Internally the result is saved to avoid recomputations. If the debugger's + /// stop id has changed between calls, the cached result is discarded. + const DecodedThread &Decode() override; + +private: + LiveThreadDecoder(const LiveThreadDecoder &other) = delete; + LiveThreadDecoder &operator=(const LiveThreadDecoder &other) = delete; + + lldb::ThreadSP m_thread_sp; + pt_cpu m_pt_cpu; + llvm::Optional<DecodedThread> m_decoded_thread; + uint32_t m_stop_id; +}; + } // namespace trace_intel_pt } // namespace lldb_private 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 @@ -10,10 +10,12 @@ #include "llvm/Support/MemoryBuffer.h" +#include "TraceIntelPT.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadTrace.h" +#include "lldb/Utility/StringExtractor.h" using namespace lldb; using namespace lldb_private; @@ -166,14 +168,7 @@ static std::vector<IntelPTInstruction> CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu, - const FileSpec &trace_file) { - ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = - MemoryBuffer::getFile(trace_file.GetPath()); - if (std::error_code err = trace_or_error.getError()) - return makeInstructionListFromError(errorCodeToError(err)); - - MemoryBuffer &trace = **trace_or_error; - + MutableArrayRef<uint8_t> buffer) { pt_config config; pt_config_init(&config); config.cpu = pt_cpu; @@ -181,12 +176,8 @@ if (int errcode = pt_cpu_errata(&config.errata, &config.cpu)) return makeInstructionListFromError(make_error<IntelPTError>(errcode)); - // The libipt library does not modify the trace buffer, hence the following - // cast is safe. - config.begin = - reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())); - config.end = - reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd())); + config.begin = buffer.data(); + config.end = buffer.data() + buffer.size(); pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config); if (!decoder) @@ -204,6 +195,33 @@ return instructions; } +static std::vector<IntelPTInstruction> +CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu, + const FileSpec &trace_file) { + ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = + MemoryBuffer::getFile(trace_file.GetPath()); + if (std::error_code err = trace_or_error.getError()) + return makeInstructionListFromError(errorCodeToError(err)); + + MemoryBuffer &trace = **trace_or_error; + MutableArrayRef<uint8_t> trace_data( + // The libipt library does not modify the trace buffer, hence the + // following cast is safe. + reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())), + trace.getBufferSize()); + return CreateDecoderAndDecode(process, pt_cpu, trace_data); +} + +static std::vector<IntelPTInstruction> +CreateDecoderAndDecode(Thread &thread, const pt_cpu &pt_cpu) { + if (Expected<TraceIntelPTBuffer> buffer = + TraceIntelPT::QueryTraceBuffer(thread)) + return CreateDecoderAndDecode(*thread.GetProcess(), pt_cpu, + MutableArrayRef<uint8_t>(buffer->data)); + else + return makeInstructionListFromError(buffer.takeError()); +} + const DecodedThread &ThreadTraceDecoder::Decode() { if (!m_decoded_thread.hasValue()) { m_decoded_thread = DecodedThread( @@ -213,3 +231,21 @@ return *m_decoded_thread; } + +LiveThreadDecoder::LiveThreadDecoder(Thread &thread, const pt_cpu &pt_cpu) + : m_thread_sp(thread.shared_from_this()), m_pt_cpu(pt_cpu), + m_decoded_thread(), m_stop_id(thread.GetProcess()->GetStopID()) {} + +const DecodedThread &LiveThreadDecoder::Decode() { + uint32_t new_stop_id = m_thread_sp->GetProcess()->GetStopID(); + if (new_stop_id != m_stop_id) { + m_stop_id = new_stop_id; + m_decoded_thread = None; + } + + if (!m_decoded_thread.hasValue()) + m_decoded_thread = + DecodedThread(CreateDecoderAndDecode(*m_thread_sp, m_pt_cpu)); + + return *m_decoded_thread; +} Index: lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h +++ lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h @@ -16,7 +16,7 @@ namespace lldb_private { namespace trace_intel_pt { -class CommandObjectTraceStartIntelPT : public CommandObjectIterateOverThreads { +class CommandObjectTraceStartIntelPT : public CommandObjecThreadTraceStart { public: class CommandOptions : public Options { public: @@ -31,13 +31,13 @@ llvm::ArrayRef<OptionDefinition> GetDefinitions() override; - size_t m_size_in_kb; - uint32_t m_custom_config; + size_t m_buffer_size_in_kb; + uint32_t m_perf_config; }; CommandObjectTraceStartIntelPT(CommandInterpreter &interpreter) - : CommandObjectIterateOverThreads( - interpreter, "thread trace start", + : CommandObjecThreadTraceStart( + "intel-pt", interpreter, "thread trace start", "Start tracing one or more threads with intel-pt. " "Defaults to the current thread. Thread indices can be " "specified as arguments.\n Use the thread-index \"all\" to trace " Index: lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp +++ lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp @@ -8,7 +8,9 @@ #include "CommandObjectTraceStartIntelPT.h" +#include "TraceIntelPT.h" #include "lldb/Host/OptionParser.h" +#include "lldb/Target/Process.h" #include "lldb/Target/Trace.h" using namespace lldb; @@ -33,17 +35,17 @@ error.SetErrorStringWithFormat("invalid integer value for option '%s'", option_arg.str().c_str()); else - m_size_in_kb = size_in_kb; + m_buffer_size_in_kb = size_in_kb; break; } case 'c': { - int32_t custom_config; - if (option_arg.empty() || option_arg.getAsInteger(0, custom_config) || - custom_config < 0) + int32_t perf_config; + if (option_arg.empty() || option_arg.getAsInteger(0, perf_config) || + perf_config < 0) error.SetErrorStringWithFormat("invalid integer value for option '%s'", option_arg.str().c_str()); else - m_custom_config = custom_config; + m_perf_config = perf_config; break; } default: @@ -54,8 +56,8 @@ void CommandObjectTraceStartIntelPT::CommandOptions::OptionParsingStarting( ExecutionContext *execution_context) { - m_size_in_kb = 4; - m_custom_config = 0; + m_buffer_size_in_kb = 4; + m_perf_config = 0; } llvm::ArrayRef<OptionDefinition> @@ -65,9 +67,19 @@ bool CommandObjectTraceStartIntelPT::HandleOneThread( lldb::tid_t tid, CommandReturnObject &result) { - result.AppendMessageWithFormat( - "would trace tid %" PRIu64 " with size_in_kb %zu and custom_config %d\n", - tid, m_options.m_size_in_kb, m_options.m_custom_config); - result.SetStatus(eReturnStatusSuccessFinishResult); + + TraceIntelPTStartPacketParams options; + options.buffer_size_in_kb = m_options.m_buffer_size_in_kb; + options.perf_config = m_options.m_perf_config; + + Thread &thread = + *m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByID(tid); + TraceIntelPT *trace = + (TraceIntelPT *)m_exe_ctx.GetTargetRef().GetTrace().get(); + + if (Error err = trace->StartTracingThread(thread, options)) + result.SetError(toString(std::move(err))); + else + result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -177,6 +177,14 @@ llvm::Expected<TraceTypeInfo> GetSupportedTraceType() override; + llvm::Error StopTracingThread(lldb::tid_t tid, + llvm::StringRef trace_type) override; + + llvm::Error StartTracingThread(const llvm::json::Value &options) override; + + llvm::Expected<std::string> + TraceQueryData(const llvm::json::Value &query) override; + Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override; Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override; Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1228,6 +1228,21 @@ return m_gdb_comm.SendGetSupportedTraceType(); } +llvm::Error ProcessGDBRemote::StopTracingThread(lldb::tid_t tid, + llvm::StringRef trace_type) { + return m_gdb_comm.SendTraceStop(tid, trace_type); +} + +llvm::Error +ProcessGDBRemote::StartTracingThread(const llvm::json::Value &options) { + return m_gdb_comm.SendTraceStart(options); +} + +llvm::Expected<std::string> +ProcessGDBRemote::TraceQueryData(const llvm::json::Value &query) { + return m_gdb_comm.SendTraceQueryData(query); +} + void ProcessGDBRemote::DidExit() { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -166,6 +166,12 @@ PacketResult Handle_jLLDBTraceSupportedType(StringExtractorGDBRemote &packet); + PacketResult Handle_jLLDBTraceStart(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceStop(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceQueryData(StringExtractorGDBRemote &packet); + PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet); PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -194,6 +194,15 @@ RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupportedType, &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupportedType); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStart, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStop, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceQueryData, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceQueryData); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g, &GDBRemoteCommunicationServerLLGS::Handle_g); @@ -1256,6 +1265,111 @@ return SendPacketNoLock(escaped_response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceStop:"); + Expected<TraceStopPacket> stop_packet = + json::parse<TraceStopPacket>(packet.Peek()); + if (!stop_packet) + return SendErrorResponse(stop_packet.takeError()); + + if (stop_packet->type != "intel-pt") + return SendErrorResponse( + Status("Unsupported tracing type '%s'", stop_packet->type.c_str())); + + if (Error err = m_debugged_process_up->StopIntelPTTrace(stop_packet->tid)) + return SendErrorResponse(std::move(err)); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceStart:"); + Expected<TraceStartSimplePacket> start_packet = + json::parse<TraceStartSimplePacket>(packet.Peek()); + if (!start_packet) + return SendErrorResponse(start_packet.takeError()); + + if (start_packet->type != "intel-pt") + return SendErrorResponse( + Status("Unsupported tracing type '%s'", start_packet->type.c_str())); + + auto intel_pt_start_packet = + json::parse<TraceIntelPTStartPacket>(packet.Peek()); + if (!intel_pt_start_packet) + return SendErrorResponse(intel_pt_start_packet.takeError()); + + if (Error err = m_debugged_process_up->StartIntelPTTrace( + intel_pt_start_packet->tid, + static_cast<size_t>(intel_pt_start_packet->params.buffer_size_in_kb), + static_cast<uint32_t>(intel_pt_start_packet->params.perf_config))) + return SendErrorResponse(std::move(err)); + + return SendOKResponse(); +} + +template <typename T> static Expected<json::Value> toJSON(Expected<T> obj) { + if (obj) + return json::toJSON(*obj); + else + return obj.takeError(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceQueryData( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceQueryData:"); + llvm::Expected<TraceQueryDataSimplePacket> query_data_packet = + llvm::json::parse<TraceQueryDataSimplePacket>(packet.Peek()); + if (!query_data_packet) + return SendErrorResponse(Status(query_data_packet.takeError())); + + auto handle_intelpt_buffer_query = [&] { + if (auto buffer_packet = + json::parse<TraceIntelPTQueryBufferPacket>(packet.Peek())) + return SendJSONResponse(toJSON( + m_debugged_process_up->GetIntelPTBuffer(buffer_packet->params.tid))); + else + return SendErrorResponse(buffer_packet.takeError()); + }; + + auto handle_intelpt_cpu_config_query = [&] { + return SendJSONResponse( + toJSON(m_debugged_process_up->GetIntelPTCPUConfig())); + }; + + if (query_data_packet->type == "intel-pt") { + if (query_data_packet->query == "buffer") + return handle_intelpt_buffer_query(); + else if (query_data_packet->query == "pt_cpu") + return handle_intelpt_cpu_config_query(); + } + + return SendErrorResponse(Status( + "Unsupported query '%s' for tracing type '%s'", + query_data_packet->query.c_str(), query_data_packet->type.c_str())); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead( StringExtractorGDBRemote &packet) { Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -73,6 +73,13 @@ PacketResult SendOKResponse(); + /// Serialize and send a JSON object response. + PacketResult SendJSONResponse(const llvm::json::Value &value); + + /// Serialize and send a JSON object response, or respond with an error if the + /// input object is an \a llvm::Error. + PacketResult SendJSONResponse(llvm::Expected<llvm::json::Value> value); + private: GDBRemoteCommunicationServer(const GDBRemoteCommunicationServer &) = delete; const GDBRemoteCommunicationServer & Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -16,11 +16,13 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringExtractorGDBRemote.h" #include "lldb/Utility/UnimplementedError.h" +#include "llvm/Support/JSON.h" #include <cstring> using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; +using namespace llvm; GDBRemoteCommunicationServer::GDBRemoteCommunicationServer( const char *comm_name, const char *listener_name) @@ -151,3 +153,21 @@ bool GDBRemoteCommunicationServer::HandshakeWithClient() { return GetAck() == PacketResult::Success; } + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendJSONResponse(const json::Value &value) { + std::string json_string; + raw_string_ostream os(json_string); + os << value; + os.flush(); + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(json_string.c_str(), json_string.size()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendJSONResponse(Expected<json::Value> value) { + if (!value) + return SendErrorResponse(value.takeError()); + return SendJSONResponse(*value); +} Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -522,6 +522,13 @@ llvm::Expected<TraceTypeInfo> SendGetSupportedTraceType(); + llvm::Error SendTraceStart(const llvm::json::Value &options); + + llvm::Error SendTraceStop(lldb::tid_t tid, llvm::StringRef trace_type); + + llvm::Expected<std::string> + SendTraceQueryData(const llvm::json::Value &query); + protected: LazyBool m_supports_not_sending_acks; LazyBool m_supports_thread_suffix; Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -3480,6 +3480,94 @@ "failed to send packet: jLLDBTraceSupportedType"); } +llvm::Error +GDBRemoteCommunicationClient::SendTraceStop(lldb::tid_t tid, + llvm::StringRef trace_type) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceStop:"); + + TraceStopPacket packet(trace_type, tid); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << llvm::json::toJSON(packet); + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) + return llvm::Error::success(); + return response.GetStatus().ToError(); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceStop"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceStop '%s'", + escaped_packet.GetData()); +} + +llvm::Error +GDBRemoteCommunicationClient::SendTraceStart(const llvm::json::Value &options) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceStart:"); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << options; + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) + return llvm::Error::success(); + return response.GetStatus().ToError(); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceStart"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceStart '%s'", + escaped_packet.GetData()); +} + +llvm::Expected<std::string> GDBRemoteCommunicationClient::SendTraceQueryData( + const llvm::json::Value &query) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceQueryData:"); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << query; + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (!response.IsNormalResponse()) + return response.GetStatus().ToError(); + return std::string(response.Peek()); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceQueryData"); + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceQueryData '%s'", + escaped_packet.GetData()); +} + Status GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid, TraceOptions &options) { Index: lldb/source/Plugins/Process/Linux/ProcessorTrace.h =================================================================== --- lldb/source/Plugins/Process/Linux/ProcessorTrace.h +++ lldb/source/Plugins/Process/Linux/ProcessorTrace.h @@ -79,8 +79,25 @@ void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; } - Status StartTrace(lldb::pid_t pid, lldb::tid_t tid, - const TraceOptions &config); + /// Start tracing a thread + /// + /// \param[in] pid + /// The pid of the process whose thread will be traced. + /// + /// \param[in] tid + /// The tid of the thread to trace. + /// + /// \param[in] buffer_size_in_kb + /// See \a NativeProcessProtocol::StartIntelPTTrace. + /// + /// \param[in] perf_config + /// See \a NativeProcessProtocol::StartIntelPTTrace. + /// + /// \return + /// \a llvm::Error::success if tracing was successful, or an + /// \a llvm::Error otherwise. + llvm::Error StartTrace(lldb::pid_t pid, lldb::tid_t tid, + size_t buffer_size_in_kb, uint32_t perf_config = 0); llvm::MutableArrayRef<uint8_t> GetAuxBuffer(); llvm::MutableArrayRef<uint8_t> GetDataBuffer(); @@ -99,10 +116,27 @@ static Status GetCPUType(TraceOptions &config); + /// See \a NativeProcessProtocol::GetIntelPTCPUConfig. + static llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig(); + static llvm::Expected<ProcessorTraceMonitorUP> Create(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config, bool useProcessSettings); + /// Start tracing a thread. + /// + /// See \a StartTrace. + /// + /// \return + /// A \a ProcessorTraceMonitorUP instance if tracing was successful, or + /// an \a llvm::Error otherwise. + static llvm::Expected<ProcessorTraceMonitorUP> + Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size_in_kb, + uint32_t perf_config); + + /// See \a NativeProcessProtocol::GetIntelPTBuffer. + llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(); + Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer, size_t offset = 0); Index: lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp +++ lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp @@ -16,6 +16,7 @@ #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "ProcessorTrace.h" #include "lldb/Host/linux/Support.h" +#include "lldb/Utility/StreamString.h" #include <sys/ioctl.h> #include <sys/syscall.h> @@ -67,27 +68,22 @@ bool ProcessorTraceMonitor::IsSupported() { return (bool)GetOSEventType(); } -Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid, - const TraceOptions &config) { +Error ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid, + size_t buffer_size_in_kb, + uint32_t perf_config) { #ifndef PERF_ATTR_SIZE_VER5 llvm_unreachable("perf event not supported"); #else - Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); LLDB_LOG(log, "called thread id {0}", tid); uint64_t page_size = getpagesize(); - uint64_t bufsize = config.getTraceBufferSize(); - uint64_t metabufsize = config.getMetaDataBufferSize(); + uint64_t buffer_size = buffer_size_in_kb * 1024; uint64_t numpages = static_cast<uint64_t>( - llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size)); + llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size)); numpages = std::max<uint64_t>(1, numpages); - bufsize = page_size * numpages; - - numpages = static_cast<uint64_t>( - llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size)); - metabufsize = page_size * numpages; + buffer_size = page_size * numpages; perf_event_attr attr; memset(&attr, 0, sizeof(attr)); @@ -98,66 +94,58 @@ attr.exclude_hv = 1; attr.exclude_idle = 1; attr.mmap = 1; + attr.config = perf_config; Expected<uint32_t> intel_pt_type = GetOSEventType(); - if (!intel_pt_type) { - error = intel_pt_type.takeError(); - return error; - } + if (!intel_pt_type) + return intel_pt_type.takeError(); LLDB_LOG(log, "intel pt type {0}", *intel_pt_type); attr.type = *intel_pt_type; - LLDB_LOG(log, "meta buffer size {0}", metabufsize); - LLDB_LOG(log, "buffer size {0} ", bufsize); - - if (error.Fail()) { - LLDB_LOG(log, "Status in custom config"); - - return error; - } + LLDB_LOG(log, "buffer size {0} ", buffer_size); errno = 0; auto fd = syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0); if (fd == -1) { LLDB_LOG(log, "syscall error {0}", errno); - error.SetErrorString("perf event syscall Failed"); - return error; + return createStringError(inconvertibleErrorCode(), + "perf event syscall failed"); } m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close()); errno = 0; auto base = - mmap(nullptr, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0); + mmap(nullptr, (buffer_size + page_size), PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { LLDB_LOG(log, "mmap base error {0}", errno); - error.SetErrorString("Meta buffer allocation failed"); - return error; + return createStringError(inconvertibleErrorCode(), + "Meta buffer allocation failed"); } m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>( reinterpret_cast<perf_event_mmap_page *>(base), - munmap_delete(metabufsize + page_size)); + 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 = bufsize; + m_mmap_meta->aux_size = buffer_size; errno = 0; - auto mmap_aux = mmap(nullptr, bufsize, PROT_READ, MAP_SHARED, fd, + auto mmap_aux = mmap(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, static_cast<long int>(m_mmap_meta->aux_offset)); if (mmap_aux == MAP_FAILED) { LLDB_LOG(log, "second mmap done {0}", errno); - error.SetErrorString("Trace buffer allocation failed"); - return error; + return createStringError(inconvertibleErrorCode(), + "Trace buffer allocation failed"); } m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>( - reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize)); - return error; + reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(buffer_size)); + return Error::success(); #endif } @@ -180,19 +168,17 @@ #endif } -Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) { - - Status error; - uint64_t cpu_family = -1; - uint64_t model = -1; - uint64_t stepping = -1; +Expected<TraceIntelPTCPUConfig> ProcessorTraceMonitor::GetIntelPTCPUConfig() { + int64_t cpu_family = -1; + int64_t model = -1; + int64_t stepping = -1; std::string vendor_id; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); auto BufferOrError = getProcFile("cpuinfo"); if (!BufferOrError) - return BufferOrError.getError(); + return Status(BufferOrError.getError()).ToError(); LLDB_LOG(log, "GetCPUType Function"); @@ -226,29 +212,51 @@ } LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id); - if ((cpu_family != static_cast<uint64_t>(-1)) && - (model != static_cast<uint64_t>(-1)) && - (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) { - auto params_dict = std::make_shared<StructuredData::Dictionary>(); - params_dict->AddIntegerItem("cpu_family", cpu_family); - params_dict->AddIntegerItem("cpu_model", model); - params_dict->AddIntegerItem("cpu_stepping", stepping); - params_dict->AddStringItem("cpu_vendor", vendor_id); + if ((cpu_family != -1) && (model != -1) && (stepping != -1) && + (!vendor_id.empty())) { + return TraceIntelPTCPUConfig{cpu_family, model, stepping, + vendor_id == "GenuineIntel" ? "intel" + : "unknown"}; + } + } + return createStringError(inconvertibleErrorCode(), "cpu info not found"); +} - llvm::StringRef intel_custom_params_key("intel-pt"); +Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) { + Expected<TraceIntelPTCPUConfig> cpu_info_or_err = GetIntelPTCPUConfig(); + if (!cpu_info_or_err) + return Status(cpu_info_or_err.takeError()); + TraceIntelPTCPUConfig &cpu_info = *cpu_info_or_err; + + auto params_dict = std::make_shared<StructuredData::Dictionary>(); + params_dict->AddIntegerItem("cpu_family", cpu_info.family); + params_dict->AddIntegerItem("cpu_model", cpu_info.model); + params_dict->AddIntegerItem("cpu_stepping", cpu_info.stepping); + params_dict->AddStringItem("cpu_vendor", cpu_info.vendor); + + llvm::StringRef intel_custom_params_key("intel-pt"); + + auto intel_custom_params = std::make_shared<StructuredData::Dictionary>(); + intel_custom_params->AddItem( + intel_custom_params_key, + StructuredData::ObjectSP(std::move(params_dict))); + + config.setTraceParams(intel_custom_params); + return Status(); +} - auto intel_custom_params = std::make_shared<StructuredData::Dictionary>(); - intel_custom_params->AddItem( - intel_custom_params_key, - StructuredData::ObjectSP(std::move(params_dict))); +llvm::Expected<ProcessorTraceMonitorUP> +ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid, + size_t buffer_size_in_kb, uint32_t config) { + ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor); - config.setTraceParams(intel_custom_params); - return error; // we are done - } - } + if (llvm::Error err = + pt_monitor_up->StartTrace(pid, tid, buffer_size_in_kb, config)) + return std::move(err); - error.SetErrorString("cpu info not found"); - return error; + pt_monitor_up->SetThreadID(tid); + pt_monitor_up->SetTraceID(m_trace_num++); + return std::move(pt_monitor_up); } llvm::Expected<ProcessorTraceMonitorUP> @@ -266,7 +274,7 @@ ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor); - error = pt_monitor_up->StartTrace(pid, tid, config); + error = pt_monitor_up->StartTrace(pid, tid, config.getTraceBufferSize()); if (error.Fail()) return error.ToError(); @@ -281,6 +289,15 @@ return std::move(pt_monitor_up); } +Expected<TraceIntelPTBuffer> ProcessorTraceMonitor::GetIntelPTBuffer() { + TraceIntelPTBuffer buffer(m_mmap_meta->aux_size); + MutableArrayRef<uint8_t> buffer_ref(buffer.data); + Status error = ReadPerfTraceAux(buffer_ref, 0); + if (error.Fail()) + return error.ToError(); + return std::move(buffer); +} + Status ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) { Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -101,6 +101,15 @@ return getProcFile(GetID(), "auxv"); } + llvm::Error StopIntelPTTrace(lldb::tid_t tid) override; + + llvm::Error StartIntelPTTrace(lldb::tid_t tid, size_t buffer_size_in_kb, + uint32_t perf_config) override; + + llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(lldb::tid_t tid) override; + + llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig() override; + lldb::user_id_t StartTrace(const TraceOptions &config, Status &error) override; Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2033,6 +2033,51 @@ return m_pt_proces_trace_id; } +Error NativeProcessLinux::StopIntelPTTrace(lldb::tid_t tid) { + const auto &trace_instance = m_processor_trace_monitor.find(tid); + if (trace_instance == m_processor_trace_monitor.end()) + return createStringError(inconvertibleErrorCode(), "thread not traced"); + + return StopProcessorTracingOnThread(trace_instance->second->GetTraceID(), tid) + .ToError(); +} + +Error NativeProcessLinux::StartIntelPTTrace(lldb::tid_t tid, + size_t buffer_size_in_kb, + uint32_t perf_config) { + if (!GetThreadByID(tid)) + return createStringError(inconvertibleErrorCode(), "invalid thread id"); + + const auto &trace_instance = m_processor_trace_monitor.find(tid); + if (trace_instance != m_processor_trace_monitor.end()) + return createStringError(inconvertibleErrorCode(), + "tracing already active on this thread"); + + Expected<ProcessorTraceMonitorUP> trace_monitor = + ProcessorTraceMonitor::Create(GetID(), tid, buffer_size_in_kb, + perf_config); + if (!trace_monitor) + return trace_monitor.takeError(); + + m_processor_trace_monitor.try_emplace(tid, std::move(*trace_monitor)); + return Error::success(); +} + +Expected<TraceIntelPTBuffer> +NativeProcessLinux::GetIntelPTBuffer(lldb::tid_t tid) { + const auto &trace_instance = m_processor_trace_monitor.find(tid); + if (trace_instance == m_processor_trace_monitor.end()) + return createStringError(inconvertibleErrorCode(), + "thread not currently traced"); + + return trace_instance->second->GetIntelPTBuffer(); +} + +llvm::Expected<TraceIntelPTCPUConfig> +NativeProcessLinux::GetIntelPTCPUConfig() { + return ProcessorTraceMonitor::GetIntelPTCPUConfig(); +} + lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config, Status &error) { if (config.getType() != TraceType::eTraceTypeProcessorTrace) Index: lldb/source/Core/PluginManager.cpp =================================================================== --- lldb/source/Core/PluginManager.cpp +++ lldb/source/Core/PluginManager.cpp @@ -1008,16 +1008,21 @@ #pragma mark Trace -struct TraceInstance : public PluginInstance<TraceCreateInstance> { - TraceInstance(ConstString name, std::string description, - CallbackType create_callback, llvm::StringRef schema, - TraceGetStartCommand get_start_command) - : PluginInstance<TraceCreateInstance>(name, std::move(description), - create_callback), - schema(schema), get_start_command(get_start_command) {} +struct TraceInstance + : public PluginInstance<TraceCreateInstanceForSessionFile> { + TraceInstance( + ConstString name, std::string description, + CallbackType create_callback_for_session_file, + TraceCreateInstanceForLiveProcess create_callback_for_live_process, + llvm::StringRef schema, TraceGetStartCommand get_start_command) + : PluginInstance<TraceCreateInstanceForSessionFile>( + name, std::move(description), create_callback_for_session_file), + schema(schema), get_start_command(get_start_command), + create_callback_for_live_process(create_callback_for_live_process) {} llvm::StringRef schema; TraceGetStartCommand get_start_command; + TraceCreateInstanceForLiveProcess create_callback_for_live_process; }; typedef PluginInstances<TraceInstance> TraceInstances; @@ -1027,23 +1032,35 @@ return g_instances; } -bool PluginManager::RegisterPlugin(ConstString name, const char *description, - TraceCreateInstance create_callback, - llvm::StringRef schema, - TraceGetStartCommand get_start_command) { +bool PluginManager::RegisterPlugin( + ConstString name, const char *description, + TraceCreateInstanceForSessionFile create_callback_for_session_file, + TraceCreateInstanceForLiveProcess create_callback_for_live_process, + llvm::StringRef schema, TraceGetStartCommand get_start_command) { return GetTracePluginInstances().RegisterPlugin( - name, description, create_callback, schema, get_start_command); + name, description, create_callback_for_session_file, + create_callback_for_live_process, schema, get_start_command); } -bool PluginManager::UnregisterPlugin(TraceCreateInstance create_callback) { - return GetTracePluginInstances().UnregisterPlugin(create_callback); +bool PluginManager::UnregisterPlugin( + TraceCreateInstanceForSessionFile create_callback_for_session_file) { + return GetTracePluginInstances().UnregisterPlugin( + create_callback_for_session_file); } -TraceCreateInstance +TraceCreateInstanceForSessionFile PluginManager::GetTraceCreateCallback(ConstString plugin_name) { return GetTracePluginInstances().GetCallbackForName(plugin_name); } +TraceCreateInstanceForLiveProcess +PluginManager::GetTraceCreateCallbackForLiveProcess(ConstString plugin_name) { + for (const TraceInstance &instance : GetTracePluginInstances().GetInstances()) + if (instance.name == plugin_name) + return instance.create_callback_for_live_process; + return nullptr; +} + llvm::StringRef PluginManager::GetTraceSchema(ConstString plugin_name) { for (const TraceInstance &instance : GetTracePluginInstances().GetInstances()) if (instance.name == plugin_name) Index: lldb/source/Commands/CommandObjectThreadUtil.h =================================================================== --- lldb/source/Commands/CommandObjectThreadUtil.h +++ lldb/source/Commands/CommandObjectThreadUtil.h @@ -76,6 +76,23 @@ bool m_add_return = true; }; +/// Base class that should be extended by Trace plug-ins to implement +/// thread-level tracing. +class CommandObjecThreadTraceStart : public CommandObjectIterateOverThreads { +public: + CommandObjecThreadTraceStart(llvm::StringRef trace_plugin_name, + CommandInterpreter &interpreter, + const char *name, const char *help, + const char *syntax, uint32_t flags) + : CommandObjectIterateOverThreads(interpreter, name, help, syntax, flags), + m_trace_plugin_name(trace_plugin_name.str()) {} + + bool DoExecute(Args &command, CommandReturnObject &result) override; + +private: + std::string m_trace_plugin_name; +}; + } // namespace lldb_private #endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H Index: lldb/source/Commands/CommandObjectThreadUtil.cpp =================================================================== --- lldb/source/Commands/CommandObjectThreadUtil.cpp +++ lldb/source/Commands/CommandObjectThreadUtil.cpp @@ -156,3 +156,20 @@ } return true; } + +bool CommandObjecThreadTraceStart::DoExecute(Args &command, + CommandReturnObject &result) { + ProcessSP process_sp = m_exe_ctx.GetProcessSP(); + /// We create a new Trace instance so that plug-ins can assume it exists. + if (!process_sp->GetTarget().GetTrace()) { + Expected<TraceSP> trace = + Trace::FindPlugin(m_trace_plugin_name, *process_sp); + if (!trace) { + result.SetStatus(eReturnStatusFailed); + result.AppendErrorWithFormat("couldn't start tracing the process. %s", + toString(trace.takeError()).c_str()); + return false; + } + } + return CommandObjectIterateOverThreads::DoExecute(command, result); +} Index: lldb/include/lldb/lldb-private-interfaces.h =================================================================== --- lldb/include/lldb/lldb-private-interfaces.h +++ lldb/include/lldb/lldb-private-interfaces.h @@ -111,9 +111,11 @@ const char *repl_options); typedef int (*ComparisonFunction)(const void *, const void *); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); -typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstance)( +typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForSessionFile)( const llvm::json::Value &trace_session_file, llvm::StringRef session_file_dir, lldb_private::Debugger &debugger); +typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForLiveProcess)( + Process &process); typedef lldb::CommandObjectSP (*TraceGetStartCommand)( CommandInterpreter &interpreter); Index: lldb/include/lldb/Utility/TraceOptions.h =================================================================== --- lldb/include/lldb/Utility/TraceOptions.h +++ lldb/include/lldb/Utility/TraceOptions.h @@ -68,11 +68,175 @@ /// the lldb-server. StructuredData::DictionarySP m_trace_params; }; -} + +/// jLLDBTraceStop gdb-remote packet structures +/// \{ +struct TraceStopPacket { + TraceStopPacket() {} + + TraceStopPacket(llvm::StringRef type, int64_t tid) : type(type), tid(tid) {} + + std::string type; + int64_t tid; +}; +///} + +/// jLLDBTraceStart gdb-remote packet structures +/// \{ +struct TraceStartSimplePacket { + TraceStartSimplePacket() {} + + TraceStartSimplePacket(llvm::StringRef type, int64_t tid) + : type(type), tid(tid) {} + + std::string type; + int64_t tid; +}; + +template <typename TParams> struct TraceStartPacket : TraceStartSimplePacket { + TraceStartPacket() {} + + TraceStartPacket(llvm::StringRef type, int64_t tid, const TParams ¶ms) + : TraceStartSimplePacket(type, tid), params(params) {} + + TParams params; +}; + +struct TraceIntelPTStartPacketParams { + int64_t buffer_size_in_kb; + int64_t perf_config; +}; + +using TraceIntelPTStartPacket = TraceStartPacket<TraceIntelPTStartPacketParams>; +/// \} + +/// jTraceQueryData gdb-remote packet structures +/// \{ +struct TraceQueryDataSimplePacket { + TraceQueryDataSimplePacket() {} + + TraceQueryDataSimplePacket(llvm::StringRef type, llvm::StringRef query) + : type(type), query(query) {} + std::string type; + std::string query; +}; + +template <typename TParams> +struct TraceQueryDataPacket : TraceQueryDataSimplePacket { + TraceQueryDataPacket() {} + + TraceQueryDataPacket(llvm::StringRef type, llvm::StringRef query, + const TParams ¶ms) + : TraceQueryDataSimplePacket(type, query), params(params) {} + + TParams params; +}; + +struct TraceIntelPTQueryBufferParams { + int64_t tid; +}; + +struct TraceIntelPTBuffer { + TraceIntelPTBuffer() : data() {} + + TraceIntelPTBuffer(size_t size) : data(size) {} + + std::vector<uint8_t> data; +}; + +struct TraceIntelPTCPUConfig { + int64_t family; + int64_t model; + int64_t stepping; + std::string vendor; +}; + +using TraceIntelPTQueryBufferPacket = + TraceQueryDataPacket<TraceIntelPTQueryBufferParams>; +/// \} + +} // namespace lldb_private namespace llvm { namespace json { +/// jLLDBTraceStop +/// \{ +bool fromJSON(const Value &value, lldb_private::TraceStopPacket &packet, + Path path); + +Value toJSON(const lldb_private::TraceStopPacket &packet); +///} + +/// jLLDBTraceStart +/// \{ +bool fromJSON(const Value &value, lldb_private::TraceStartSimplePacket &packet, + Path path); + +bool fromJSON(const Value &value, + lldb_private::TraceIntelPTStartPacketParams &packet, Path path); + +Value toJSON(const lldb_private::TraceIntelPTStartPacketParams &packet); + +template <typename TParams> +bool fromJSON(const Value &value, + lldb_private::TraceStartPacket<TParams> &packet, Path path) { + ObjectMapper o(value, path); + return o && + fromJSON(value, (lldb_private::TraceStartSimplePacket &)packet, + path) && + o.map("params", packet.params); +} + +template <typename TParams> +Value toJSON(const lldb_private::TraceStartPacket<TParams> &packet) { + return Object{{"tid", packet.tid}, + {"type", packet.type}, + {"params", toJSON(packet.params)}}; +} +/// \} + +/// jLLDBTraceQueryData +/// \{ +bool fromJSON(const Value &value, + lldb_private::TraceQueryDataSimplePacket &packet, Path path); + +Value toJSON(const lldb_private::TraceQueryDataSimplePacket &packet); + +bool fromJSON(const Value &value, + lldb_private::TraceIntelPTQueryBufferParams ¶ms, Path path); + +Value toJSON(const lldb_private::TraceIntelPTQueryBufferParams &buffer); + +template <typename TParams> +bool fromJSON(const Value &value, + lldb_private::TraceQueryDataPacket<TParams> &packet, Path path) { + ObjectMapper o(value, path); + return o && + fromJSON(value, (lldb_private::TraceQueryDataSimplePacket &)packet, + path) && + o.map("params", packet.params); +} + +template <typename TParams> +Value toJSON(const lldb_private::TraceQueryDataPacket<TParams> &packet) { + return llvm::json::Object{{"query", packet.query}, + {"type", packet.type}, + {"params", toJSON(packet.params)}}; +} + +bool fromJSON(const Value &value, lldb_private::TraceIntelPTBuffer &response, + Path path); + +Value toJSON(const lldb_private::TraceIntelPTBuffer &buffer); + +bool fromJSON(const Value &value, lldb_private::TraceIntelPTCPUConfig &pt_cpu, + Path path); + +Value toJSON(const lldb_private::TraceIntelPTCPUConfig &cpu_config); + +/// \} + bool fromJSON(const Value &value, lldb_private::TraceTypeInfo &info, Path path); } // namespace json Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h =================================================================== --- lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -168,6 +168,9 @@ eServerPacketType_jTraceStop, // deprecated eServerPacketType_jTraceConfigRead, // deprecated + eServerPacketType_jLLDBTraceQueryData, + eServerPacketType_jLLDBTraceStart, + eServerPacketType_jLLDBTraceStop, eServerPacketType_jLLDBTraceSupportedType, }; Index: lldb/include/lldb/Target/Trace.h =================================================================== --- lldb/include/lldb/Target/Trace.h +++ lldb/include/lldb/Target/Trace.h @@ -12,7 +12,9 @@ #include "llvm/Support/JSON.h" #include "lldb/Core/PluginInterface.h" +#include "lldb/Target/Thread.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/TraceOptions.h" #include "lldb/Utility/UnimplementedError.h" #include "lldb/lldb-private.h" @@ -97,6 +99,21 @@ FindPlugin(Debugger &debugger, const llvm::json::Value &trace_session_file, llvm::StringRef session_file_dir); + /// Find a trace plug-in to trace a live process. + /// + /// \param[in] plugin_name + /// Plug-in name to search. + /// + /// \param[in] process + /// Live process to trace. + /// + /// \return + /// A \a TraceSP instance, or an \a llvm::Error if the plug-in name + /// doesn't match any registered plug-ins or tracing couldn't be + /// started. + static llvm::Expected<lldb::TraceSP> FindPlugin(llvm::StringRef plugin_name, + Process &process); + /// Get the schema of a Trace plug-in given its name. /// /// \param[in] plugin_name @@ -196,8 +213,102 @@ /// \return /// The total number of instructions in the trace. virtual size_t GetInstructionCount(const Thread &thread) = 0; + +protected: + /// Start tracing a thread. + /// + /// Base implementation that should be invoked by plug-ins to start tracing. + /// + /// \param[in] thread + /// The thread to trace. + /// + /// \param[in] params + /// json-serializable params specific to the thread plug-in. + /// + /// \return + /// \a llvm::Error::success if the operation was successful, or + /// \a llvm::Error otherwise. + template <typename TParams> + llvm::Error StartTracingThread(Thread &thread, const TParams ¶ms); + + /// Query tracing-related data. + /// + /// Base implementation that should be invoked by plug-ins to query data. + /// + /// \param[in] process + /// The process that owns the queried data. + /// + /// \param[in] trace_type + /// The tracing technology, e.g. intel-pt, arm-coresight, etc. + /// + /// \param[in] query + /// An identifier for the requested data. + /// + /// \param[in] params + /// JSON-serializable params specific to the query. + /// + /// \return + /// A JSON-serializable templated object holding the requested data, or + /// an \a llvm::Error in case of failures. + template <typename TResponse, typename TParams> + static llvm::Expected<TResponse> + QueryData(Process &process, llvm::StringRef trace_type, llvm::StringRef query, + const TParams ¶ms); + + /// Version of \a Query Data that doesn't require parameters for the query. + template <typename TResponse> + static llvm::Expected<TResponse> QueryData(Process &process, + llvm::StringRef trace_type, + llvm::StringRef query); + +private: + /// Helper class to reduce the implementation of the templated \a + /// StartTracingThread. + llvm::Error DoStartTracingThread(Thread &thread, + const llvm::json::Value &packet); + + /// Helper class to reduce the implementation of the templated \a QueryData. + static llvm::Expected<std::string> + DoQueryData(Process &process, const llvm::json::Value &packet); }; +template <typename TParams> +llvm::Error Trace::StartTracingThread(Thread &thread, const TParams ¶ms) { + TraceStartPacket<TParams> packet(GetPluginName().GetStringRef(), + thread.GetID(), params); + return DoStartTracingThread(thread, llvm::json::toJSON(packet)); +} + +template <typename TResponse, typename TParams> +llvm::Expected<TResponse> +Trace::QueryData(Process &process, llvm::StringRef trace_type, + llvm::StringRef query, const TParams ¶ms) { + TraceQueryDataPacket<TParams> packet(trace_type, query, params); + + llvm::Expected<std::string> response = + DoQueryData(process, llvm::json::toJSON(packet)); + + if (response) + return llvm::json::parse<TResponse>(*response); + else + return response.takeError(); +} + +template <typename TResponse> +llvm::Expected<TResponse> Trace::QueryData(Process &process, + llvm::StringRef trace_type, + llvm::StringRef query) { + TraceQueryDataSimplePacket packet(trace_type, query); + + llvm::Expected<std::string> response = + DoQueryData(process, llvm::json::toJSON(packet)); + + if (response) + return llvm::json::parse<TResponse>(*response); + else + return response.takeError(); +} + } // namespace lldb_private #endif // LLDB_TARGET_TRACE_H Index: lldb/include/lldb/Target/Process.h =================================================================== --- lldb/include/lldb/Target/Process.h +++ lldb/include/lldb/Target/Process.h @@ -2555,6 +2555,48 @@ /// not supported for the inferior. virtual llvm::Expected<TraceTypeInfo> GetSupportedTraceType(); + /// Start tracing a thread. It fails if the thread is already being traced. + /// + /// \param[in] options + /// JSON structure with the parameters to start the tracing. + /// + /// \return + /// \a llvm::Error::success if the operation was successful, or an + /// \a llvm::Error otherwise. + virtual llvm::Error StartTracingThread(const llvm::json::Value &options) { + return llvm::make_error<UnimplementedError>(); + } + + /// Stop tracing a thread. + /// + /// \param[in] tid + /// The thread id to stop tracing. + /// + /// \param[in] trace_type + /// The trace technology name to start tracing with, e.g. intel-pt, + /// arm-coresight, etc. + /// + /// \return + /// \a llvm::Error::success if stopping was successful, or an + /// \a llvm::Error otherwise. + virtual llvm::Error StopTracingThread(lldb::tid_t tid, + llvm::StringRef trace_type) { + return llvm::make_error<UnimplementedError>(); + } + + /// Query tracing-related data + /// + /// \param[in] query + /// JSON structure with the parameters that describe the query. + /// + /// \return + /// An unparsed JSON string with the queried data, or an \a llvm::Error + /// in case of failures. + virtual llvm::Expected<std::string> + TraceQueryData(const llvm::json::Value &query) { + return llvm::make_error<UnimplementedError>(); + } + // This calls a function of the form "void * (*)(void)". bool CallVoidArgVoidPtrReturn(const Address *address, lldb::addr_t &returned_func, Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -306,6 +306,67 @@ MainLoop &mainloop) const = 0; }; + /// Intel PT interface + /// \{ + + /// Stop tracing a thread. + /// + /// \param[in] tid + /// The thread id to stop tracing. + /// + /// \return + /// \a llvm::Error::success if stopping was successful, or an + /// \a llvm::Error otherwise. + virtual llvm::Error StopIntelPTTrace(lldb::tid_t tid) { + return llvm::make_error<UnimplementedError>(); + } + + /// Start tracing a thread. It fails if the thread is already being traced. + /// + /// \param[in] tid + /// The thread id to start tracing. + /// + /// \param[in] buffer_size_in_kb + /// The size of the circular trace buffer. It is rounded up to the nearest + /// power of 2. + /// + /// \param[in] perf_config + /// Low level perf event config. See + /// https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt + /// for more information + /// + /// \return + /// \a llvm::Error::success if the operation was successful, or an + /// \a llvm::Error otherwise. + virtual llvm::Error StartIntelPTTrace(lldb::tid_t tid, + size_t buffer_size_in_kb, + uint32_t perf_config = 0) { + return llvm::make_error<UnimplementedError>(); + } + + /// Get the trace buffer for a thread. + /// + /// \param[in] tid + /// The thread id of the thread whose trace buffer is requested. + /// + /// \return + /// A \a TraceIntelPTBuffer object, or an \a llvm::Error in case of + /// failures. + virtual llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(lldb::tid_t tid) { + return llvm::make_error<UnimplementedError>(); + } + + /// Get the Intel CPU config for the current host, which is used for decoding + /// trace buffers. + /// + /// \return + /// A \a TraceIntelPTCPUConfig object, or an \a llvm::Error in case of + /// failures. + virtual llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig() { + return llvm::make_error<UnimplementedError>(); + } + /// \} + /// StartTracing API for starting a tracing instance with the /// TraceOptions on a specific thread or process. /// Index: lldb/include/lldb/Core/PluginManager.h =================================================================== --- lldb/include/lldb/Core/PluginManager.h +++ lldb/include/lldb/Core/PluginManager.h @@ -331,14 +331,20 @@ GetSymbolVendorCreateCallbackAtIndex(uint32_t idx); // Trace - static bool RegisterPlugin(ConstString name, const char *description, - TraceCreateInstance create_callback, - llvm::StringRef schema, - TraceGetStartCommand get_start_command); + static bool RegisterPlugin( + ConstString name, const char *description, + TraceCreateInstanceForSessionFile create_callback_for_session_file, + TraceCreateInstanceForLiveProcess create_callback_for_live_process, + llvm::StringRef schema, TraceGetStartCommand get_start_command); + + static bool + UnregisterPlugin(TraceCreateInstanceForSessionFile create_callback); - static bool UnregisterPlugin(TraceCreateInstance create_callback); + static TraceCreateInstanceForSessionFile + GetTraceCreateCallback(ConstString plugin_name); - static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name); + static TraceCreateInstanceForLiveProcess + GetTraceCreateCallbackForLiveProcess(ConstString plugin_name); static lldb::CommandObjectSP GetTraceStartCommand(llvm::StringRef plugin_name, Index: lldb/docs/lldb-gdb-remote.txt =================================================================== --- lldb/docs/lldb-gdb-remote.txt +++ lldb/docs/lldb-gdb-remote.txt @@ -261,6 +261,110 @@ send packet: jLLDBTraceSupportedType read packet: {"name": <name>, "description", <description>}/E<error code>;AAAAAAAAA +//---------------------------------------------------------------------- +// jLLDBTraceStart +// +// BRIEF +// Start tracing a thread using a provided tracing technology. The input +// is specified as a JSON object. If tracing started succesfully, an OK +// response is returned, or an error otherwise. +// +// SCHEMA +// The schema for the input is +// +// { +// "type": <tracing technology name, e.g. intel-pt, arm-coresight> +// "tid": <thread id trace in decimal> +// "params": { +// <JSON object with parameters specific to the selected tracing technology> +// } +// } +// +// intel-pt +// The schema for the "params" field for the intel-pt tracing technology is +// +// { +// "bufferSizeInKB": <trace buffer size in KB in decimal> +// "perf_config": <custom low-level perf event intel-pt config in decimal> +// } +//---------------------------------------------------------------------- + +send packet: jLLDBTraceStart:{"type":<type>,"tid":<tid>,"params":<params>}] +read packet: OK/E<error code>;AAAAAAAAA + +//---------------------------------------------------------------------- +// jLLDBTraceStop +// +// BRIEF +// Stop tracing a thread using a provided tracing technology. The input +// is specified as a JSON object. If tracing was stopped succesfully, an OK +// response is returned, or an error otherwise. +// +// SCHEMA +// The schema for the input is +// +// { +// "type": <tracing technology name, e.g. intel-pt, arm-coresight> +// "tid": <thread id trace in decimal> +// } +//---------------------------------------------------------------------- + +send packet: jLLDBTraceStop:{"type":<type>,"tid":<tid>}] +read packet: OK/E<error code>;AAAAAAAAA + +//---------------------------------------------------------------------- +// jLLDBTraceQueryData +// +// BRIEF +// Get custom data given a trace technology and a query identifier. The input +// is specified as a JSON object. The response is a JSON object that +// depends on the queried data, or an error in case of failures. +// +// SCHEMA +// The schema for the input is +// +// { +// "type": <tracing technology name, e.g. intel-pt, arm-coresight> +// "query": <identifier for the data> +// "params": { +// <optional JSON object with parameters specific to the queried data> +// } +// } +// +// intel-pt query "pt_cpu" +// +// This query doesn't not have any input params. +// +// The schema for the return value is +// +// { +// "vendor": "intel" | "unknown", +// "family": <decimal>, +// "model": <decimal>, +// "stepping": <decimal> +// } +// +// intel-pt query "buffer" +// +// The schema for the "params" field in this query is +// +// { +// "tid": <thread id trace in decimal> +// } +// +// The schema for the return value is +// +// { +// "data": <contents of the trace buffer in HEX8 format> +// } +//---------------------------------------------------------------------- + +send packet: jLLDBTraceQueryData:{"type":<type>,"query":<query>}] +read packet: {return value}/E<error code>;AAAAAAAAA + +send packet: jLLDBTraceQueryData:{"type":<type>,"query":<query>,"params":<params>}] +read packet: {return value}/E<error code>;AAAAAAAAA + //---------------------------------------------------------------------- // jTraceStart: //
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits