diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt --- a/lldb/docs/lldb-gdb-remote.txt +++ b/lldb/docs/lldb-gdb-remote.txt @@ -261,6 +261,110 @@ send packet: jLLDBTraceSupportedType read packet: {"name": , "description", }/E;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": +// "tid": +// "params": { +// +// } +// } +// +// intel-pt +// The schema for the "params" field for the intel-pt tracing technology is +// +// { +// "bufferSizeInKB": +// "perf_config": +// } +//---------------------------------------------------------------------- + +send packet: jLLDBTraceStart:{"type":,"tid":,"params":}] +read packet: OK/E;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": +// "tid": +// } +//---------------------------------------------------------------------- + +send packet: jLLDBTraceStop:{"type":,"tid":}] +read packet: OK/E;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": +// "query": +// "params": { +// +// } +// } +// +// 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": , +// "model": , +// "stepping": +// } +// +// intel-pt query "buffer" +// +// The schema for the "params" field in this query is +// +// { +// "tid": +// } +// +// The schema for the return value is +// +// { +// "data": +// } +//---------------------------------------------------------------------- + +send packet: jLLDBTraceQueryData:{"type":,"query":}] +read packet: {return value}/E;AAAAAAAAA + +send packet: jLLDBTraceQueryData:{"type":,"query":,"params":}] +read packet: {return value}/E;AAAAAAAAA + //---------------------------------------------------------------------- // jTraceStart: // diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h --- a/lldb/include/lldb/Core/PluginManager.h +++ b/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, diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h --- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ b/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(); + } + + /// 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(); + } + + /// 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 GetIntelPTBuffer(lldb::tid_t tid) { + return llvm::make_error(); + } + + /// 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 GetIntelPTCPUConfig() { + return llvm::make_error(); + } + /// \} + /// StartTracing API for starting a tracing instance with the /// TraceOptions on a specific thread or process. /// diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -2555,6 +2555,48 @@ /// not supported for the inferior. virtual llvm::Expected 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(); + } + + /// 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(); + } + + /// 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 + TraceQueryData(const llvm::json::Value &query) { + return llvm::make_error(); + } + // This calls a function of the form "void * (*)(void)". bool CallVoidArgVoidPtrReturn(const Address *address, lldb::addr_t &returned_func, diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h --- a/lldb/include/lldb/Target/Trace.h +++ b/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 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 + 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 + static llvm::Expected + 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 + static llvm::Expected 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 + DoQueryData(Process &process, const llvm::json::Value &packet); }; +template +llvm::Error Trace::StartTracingThread(Thread &thread, const TParams ¶ms) { + TraceStartPacket packet(GetPluginName().GetStringRef(), + thread.GetID(), params); + return DoStartTracingThread(thread, llvm::json::toJSON(packet)); +} + +template +llvm::Expected +Trace::QueryData(Process &process, llvm::StringRef trace_type, + llvm::StringRef query, const TParams ¶ms) { + TraceQueryDataPacket packet(trace_type, query, params); + + llvm::Expected response = + DoQueryData(process, llvm::json::toJSON(packet)); + + if (response) + return llvm::json::parse(*response); + else + return response.takeError(); +} + +template +llvm::Expected Trace::QueryData(Process &process, + llvm::StringRef trace_type, + llvm::StringRef query) { + TraceQueryDataSimplePacket packet(trace_type, query); + + llvm::Expected response = + DoQueryData(process, llvm::json::toJSON(packet)); + + if (response) + return llvm::json::parse(*response); + else + return response.takeError(); +} + } // namespace lldb_private #endif // LLDB_TARGET_TRACE_H diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -168,6 +168,9 @@ eServerPacketType_jTraceStop, // deprecated eServerPacketType_jTraceConfigRead, // deprecated + eServerPacketType_jLLDBTraceQueryData, + eServerPacketType_jLLDBTraceStart, + eServerPacketType_jLLDBTraceStop, eServerPacketType_jLLDBTraceSupportedType, }; diff --git a/lldb/include/lldb/Utility/TraceOptions.h b/lldb/include/lldb/Utility/TraceOptions.h --- a/lldb/include/lldb/Utility/TraceOptions.h +++ b/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 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; +/// \} + +/// 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 +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 data; +}; + +struct TraceIntelPTCPUConfig { + int64_t family; + int64_t model; + int64_t stepping; + std::string vendor; +}; + +using TraceIntelPTQueryBufferPacket = + TraceQueryDataPacket; +/// \} + +} // 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 +bool fromJSON(const Value &value, + lldb_private::TraceStartPacket &packet, Path path) { + ObjectMapper o(value, path); + return o && + fromJSON(value, (lldb_private::TraceStartSimplePacket &)packet, + path) && + o.map("params", packet.params); +} + +template +Value toJSON(const lldb_private::TraceStartPacket &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 +bool fromJSON(const Value &value, + lldb_private::TraceQueryDataPacket &packet, Path path) { + ObjectMapper o(value, path); + return o && + fromJSON(value, (lldb_private::TraceQueryDataSimplePacket &)packet, + path) && + o.map("params", packet.params); +} + +template +Value toJSON(const lldb_private::TraceQueryDataPacket &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 diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h --- a/lldb/include/lldb/lldb-private-interfaces.h +++ b/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 (*TraceCreateInstance)( +typedef llvm::Expected (*TraceCreateInstanceForSessionFile)( const llvm::json::Value &trace_session_file, llvm::StringRef session_file_dir, lldb_private::Debugger &debugger); +typedef llvm::Expected (*TraceCreateInstanceForLiveProcess)( + Process &process); typedef lldb::CommandObjectSP (*TraceGetStartCommand)( CommandInterpreter &interpreter); diff --git a/lldb/source/Commands/CommandObjectThreadUtil.h b/lldb/source/Commands/CommandObjectThreadUtil.h --- a/lldb/source/Commands/CommandObjectThreadUtil.h +++ b/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 diff --git a/lldb/source/Commands/CommandObjectThreadUtil.cpp b/lldb/source/Commands/CommandObjectThreadUtil.cpp --- a/lldb/source/Commands/CommandObjectThreadUtil.cpp +++ b/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 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); +} diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -1008,16 +1008,21 @@ #pragma mark Trace -struct TraceInstance : public PluginInstance { - TraceInstance(ConstString name, std::string description, - CallbackType create_callback, llvm::StringRef schema, - TraceGetStartCommand get_start_command) - : PluginInstance(name, std::move(description), - create_callback), - schema(schema), get_start_command(get_start_command) {} +struct TraceInstance + : public PluginInstance { + 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( + 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 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) diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/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 GetIntelPTBuffer(lldb::tid_t tid) override; + + llvm::Expected GetIntelPTCPUConfig() override; + lldb::user_id_t StartTrace(const TraceOptions &config, Status &error) override; diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/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 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 +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 +NativeProcessLinux::GetIntelPTCPUConfig() { + return ProcessorTraceMonitor::GetIntelPTCPUConfig(); +} + lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config, Status &error) { if (config.getType() != TraceType::eTraceTypeProcessorTrace) diff --git a/lldb/source/Plugins/Process/Linux/ProcessorTrace.h b/lldb/source/Plugins/Process/Linux/ProcessorTrace.h --- a/lldb/source/Plugins/Process/Linux/ProcessorTrace.h +++ b/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 GetAuxBuffer(); llvm::MutableArrayRef GetDataBuffer(); @@ -99,10 +116,27 @@ static Status GetCPUType(TraceOptions &config); + /// See \a NativeProcessProtocol::GetIntelPTCPUConfig. + static llvm::Expected GetIntelPTCPUConfig(); + static llvm::Expected 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 + 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 GetIntelPTBuffer(); + Status ReadPerfTraceAux(llvm::MutableArrayRef &buffer, size_t offset = 0); diff --git a/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp b/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp --- a/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp +++ b/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 #include @@ -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( - llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size)); + llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size)); numpages = std::max(1, numpages); - bufsize = page_size * numpages; - - numpages = static_cast( - 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 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(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( reinterpret_cast(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(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( - reinterpret_cast(mmap_aux), munmap_delete(bufsize)); - return error; + reinterpret_cast(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 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(-1)) && - (model != static_cast(-1)) && - (stepping != static_cast(-1)) && (!vendor_id.empty())) { - auto params_dict = std::make_shared(); - 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 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(); + 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(); + 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(); - intel_custom_params->AddItem( - intel_custom_params_key, - StructuredData::ObjectSP(std::move(params_dict))); +llvm::Expected +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 @@ -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 ProcessorTraceMonitor::GetIntelPTBuffer() { + TraceIntelPTBuffer buffer(m_mmap_meta->aux_size); + MutableArrayRef 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 &buffer, size_t offset) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -522,6 +522,13 @@ llvm::Expected SendGetSupportedTraceType(); + llvm::Error SendTraceStart(const llvm::json::Value &options); + + llvm::Error SendTraceStop(lldb::tid_t tid, llvm::StringRef trace_type); + + llvm::Expected + SendTraceQueryData(const llvm::json::Value &query); + protected: LazyBool m_supports_not_sending_acks; LazyBool m_supports_thread_suffix; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/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 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) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/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 value); + private: GDBRemoteCommunicationServer(const GDBRemoteCommunicationServer &) = delete; const GDBRemoteCommunicationServer & diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/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 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 value) { + if (!value) + return SendErrorResponse(value.takeError()); + return SendJSONResponse(*value); +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/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); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/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 stop_packet = + json::parse(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 start_packet = + json::parse(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(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(intel_pt_start_packet->params.buffer_size_in_kb), + static_cast(intel_pt_start_packet->params.perf_config))) + return SendErrorResponse(std::move(err)); + + return SendOKResponse(); +} + +template static Expected toJSON(Expected 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 query_data_packet = + llvm::json::parse(packet.Peek()); + if (!query_data_packet) + return SendErrorResponse(Status(query_data_packet.takeError())); + + auto handle_intelpt_buffer_query = [&] { + if (auto buffer_packet = + json::parse(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) { diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -177,6 +177,14 @@ llvm::Expected 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 + 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; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/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 +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(); diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h +++ b/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 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 " diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp +++ b/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 @@ -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(); } diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h --- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h +++ b/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 &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 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 m_decoded_thread; + uint32_t m_stop_id; +}; + } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp --- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp +++ b/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 CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu, - const FileSpec &trace_file) { - ErrorOr> 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 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(errcode)); - // The libipt library does not modify the trace buffer, hence the following - // cast is safe. - config.begin = - reinterpret_cast(const_cast(trace.getBufferStart())); - config.end = - reinterpret_cast(const_cast(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 +CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu, + const FileSpec &trace_file) { + ErrorOr> 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 trace_data( + // The libipt library does not modify the trace buffer, hence the + // following cast is safe. + reinterpret_cast(const_cast(trace.getBufferStart())), + trace.getBufferSize()); + return CreateDecoderAndDecode(process, pt_cpu, trace_data); +} + +static std::vector +CreateDecoderAndDecode(Thread &thread, const pt_cpu &pt_cpu) { + if (Expected buffer = + TraceIntelPT::QueryTraceBuffer(thread)) + return CreateDecoderAndDecode(*thread.GetProcess(), pt_cpu, + MutableArrayRef(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; +} diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/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 - 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 + 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 QueryTraceBuffer(Thread &thread); + + static llvm::Expected QueryCPUConfig(Process &process); + private: friend class TraceIntelPTSessionFileParser; @@ -73,6 +86,8 @@ TraceIntelPT(const pt_cpu &pt_cpu, const std::vector> &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, ThreadTraceDecoder> - m_trace_threads; + std::map> m_thread_decoders; }; } // namespace trace_intel_pt diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/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 -TraceIntelPT::CreateInstance(const json::Value &trace_session_file, - StringRef session_file_dir, Debugger &debugger) { +Expected 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(thread, m_pt_cpu)); + return Error::success(); +} + +Expected TraceIntelPT::QueryTraceBuffer(Thread &thread) { + TraceIntelPTQueryBufferParams params = {static_cast(thread.GetID())}; + return QueryData(*thread.GetProcess(), "intel-pt", + "buffer", params); +} + +Expected TraceIntelPT::QueryCPUConfig(Process &process) { + if (Expected cpu_config = + QueryData(process, "intel-pt", "pt_cpu")) + return TraceIntelPTSessionFileParser::ParsePTCPU(*cpu_config); + else + return cpu_config.takeError(); +} + +Expected TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { + Expected 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> &traced_threads) : m_pt_cpu(pt_cpu) { for (const std::shared_ptr &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(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) { diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td +++ b/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.">; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h +++ b/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 &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, diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp +++ b/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(pt_cpu.family), static_cast(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, diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -72,6 +72,19 @@ return createInvalidPlugInError(json_session.trace.type); } +Expected 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 Trace::FindPluginSchema(StringRef name) { ConstString plugin_name(name); StringRef schema = PluginManager::GetTraceSchema(plugin_name); @@ -266,3 +279,13 @@ return index < end_position; }); } + +Expected 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); +} diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/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': diff --git a/lldb/source/Utility/TraceOptions.cpp b/lldb/source/Utility/TraceOptions.cpp --- a/lldb/source/Utility/TraceOptions.cpp +++ b/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 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 diff --git a/lldb/test/API/commands/trace/TestTraceStartStop.py b/lldb/test/API/commands/trace/TestTraceStartStop.py --- a/lldb/test/API/commands/trace/TestTraceStartStop.py +++ b/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 [ ...] []"]) - 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