Index: docs/lldb-gdb-remote.txt =================================================================== --- docs/lldb-gdb-remote.txt +++ docs/lldb-gdb-remote.txt @@ -209,6 +209,161 @@ read packet: OK //---------------------------------------------------------------------- +// JTrace:start: +// +// BRIEF +// Packet for starting trace of type lldb::TraceType. The following +// parameters (mandatory and optional) should be appended to the packet +// although there is no specific order imposed. The parameters need to +// formatted as a semi-colon seperated list of "Name:Value" pairs. +// Different tracing types could require different custom parameters. +// Such custom tracing parameters if needed should be collectively +// specified in a JSON dictionary and the dictionary can be appended +// to this packet (as Value corresponding to "jparams"). Since sending +// JSON data over gdb-remote protocol has certain limitations, binary +// escaping convention is used for JSON data. +// +// Following is the list of parameters - +// +// Name Value (Hex Encoded 64-bit integer) (O)Optional/ +// (except jparams which should be a (M)Mandatory +// JSON dictionary) +// ========== ==================================================== +// +// type The type of trace to start (see M +// lldb-enumerations for TraceType) +// +// buffersize The size of the buffer to allocate M +// for trace gathering. +// +// threadid The id of the thread to start tracing O +// on. +// +// metabuffersize The size of buffer to hold meta data O +// used for decoding the trace data. +// +// jparams Any parameters that are specific to O +// certain trace technologies should be +// collectively specified as a JSON +// dictionary +// ========== ==================================================== +// +// Each tracing instance is identified by a trace id which is returned +// as the reply to this packet. In case the tracing failed to begin an +// error code is returned instead. +//---------------------------------------------------------------------- + +send packet: JTrace:start:type:;buffersize:; +read packet: /E + +//---------------------------------------------------------------------- +// JTrace:stop: +// +// BRIEF +// Stop tracing instance with trace id , of course trace +// needs to be started before. +// +// Following is the list of parameters - +// +// Name Value (Hex Encoded 64-bit integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// +// traceid The trace id of the tracing instance M +// +// threadid The id of the thread to stop tracing O +// on. Since could map to +// multiple trace instances (in case it +// maps to the complete process), the +// threadid of a particular thread could +// be appended as "threadid:;" +// to stop tracing on that thread. +// ========== ==================================================== +// +// An OK response is sent in case of success else an error code is +// returned. +//---------------------------------------------------------------------- + +send packet: JTrace:stop:traceid:; +read packet: /E + +//---------------------------------------------------------------------- +// jTrace:buffer:read: +// +// BRIEF +// Packet for reading the trace for tracing instance , i.e the +// id obtained from StartTrace API. +// +// Following is the list of parameters - +// +// Name Value (Hex Encoded 64-bit integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// traceid The trace id of the tracing instance M +// +// offset The offset to start reading the data M +// from. +// +// buffersize The size of the data intended to read. M +// +// threadid The id of the thread to retrieve data O +// from. +// ========== ==================================================== +// +// The trace data is sent as raw binary data if the read was successful +// else an error code is sent. +//---------------------------------------------------------------------- + +send packet: jTrace:buffer:read:traceid:;offset:;buffersize:; +read packet: /E + +//---------------------------------------------------------------------- +// jTrace:meta:read:traceid:; +// +// BRIEF +// Similar Packet as above except it reads meta data. +//---------------------------------------------------------------------- + +/---------------------------------------------------------------------- +// jTrace:conf:read: +// +// BRIEF +// Request the trace configuration for the tracing instance with id +// . +// +// Following is the list of parameters - +// +// Name Value (Hex Encoded 64-bit integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// traceid The trace id of the tracing instance M +// +// threadid The id of the thread to obtain trace O +// configuration from. Since +// could map to multiple trace instances +// (in case it maps to the complete +// process), the threadid of a particular +// thread could be appended as +// "threadid:;" to obtain the +// trace configuration of that thread. +// ========== ==================================================== +// +// The numerical values are Hex Encoded 64-bit integers (including the +// values obtained in the response). +// In the response packet the trace configuration is sent as text, +// formatted as "Name:Value" pair seperated by a semi colon (in the +// case of custom parameters the value should be a JSON Structure with +// Name as "jparams"). Since sending JSON data over gdb-remote protocol +// has certain limitations, binary escaping convention is used for JSON +// data. +// In case the trace instance with the was not found, an +// error code is returned. +//---------------------------------------------------------------------- + +send packet: jTrace:conf:read:traceid:; +read packet: conf1:;conf2:;jparams:{"paramName":paramValue}];/E + +//---------------------------------------------------------------------- // "qRegisterInfo" // // BRIEF Index: include/lldb/API/SBTrace.h =================================================================== --- include/lldb/API/SBTrace.h +++ include/lldb/API/SBTrace.h @@ -40,7 +40,7 @@ /// /// @param[in] thread_id /// Tracing could be started for the complete process or a - /// single thread, in the first case the uid obtained would + /// single thread, in the first case the traceid obtained would /// map to all the threads existing within the process and the /// ones spawning later. The thread_id parameter can be used in /// such a scenario to select the trace data for a specific @@ -68,16 +68,17 @@ /// An error explaining what went wrong. /// /// @param[in] thread_id - /// The user id could map to a tracing instance for a thread + /// The trace id could map to a tracing instance for a thread /// or could also map to a group of threads being traced with /// the same trace options. A thread_id is normally optional /// except in the case of tracing a complete process and tracing /// needs to switched off on a particular thread. /// A situation could occur where initially a thread (lets say - /// thread A) is being individually traced with a particular uid - /// and then tracing is started on the complete process, in this - /// case thread A will continue without any change. All newly - /// spawned threads would be traced with the uid of the process. + /// thread A) is being individually traced with a particular + /// trace id and then tracing is started on the complete + /// process, in this case thread A will continue without any + /// change. All newly spawned threads would be traced with the + /// trace id of the process. /// Now if the StopTrace API is called for the whole process, /// thread A will not be stopped and must be stopped separately. //------------------------------------------------------------------ Index: include/lldb/Core/TraceOptions.h =================================================================== --- include/lldb/Core/TraceOptions.h +++ include/lldb/Core/TraceOptions.h @@ -18,8 +18,7 @@ namespace lldb_private { class TraceOptions { public: - TraceOptions() - : m_trace_params(new StructuredData::Dictionary()) {} + TraceOptions() : m_trace_params(new StructuredData::Dictionary()) {} const StructuredData::DictionarySP &getTraceParams() const { return m_trace_params; @@ -43,7 +42,7 @@ void setThreadID(lldb::tid_t thread_id) { m_thread_id = thread_id; } - lldb::tid_t getThreadID() { return m_thread_id; } + lldb::tid_t getThreadID() const { return m_thread_id; } private: lldb::TraceType m_type; Index: include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- include/lldb/Host/common/NativeProcessProtocol.h +++ include/lldb/Host/common/NativeProcessProtocol.h @@ -10,6 +10,7 @@ #ifndef liblldb_NativeProcessProtocol_h_ #define liblldb_NativeProcessProtocol_h_ +#include "lldb/Core/TraceOptions.h" #include "lldb/Host/MainLoop.h" #include "lldb/Utility/Error.h" #include "lldb/lldb-private-forward.h" @@ -308,6 +309,109 @@ static Error Attach(lldb::pid_t pid, NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &process_sp); + //------------------------------------------------------------------ + /// StartTracing API for starting a tracing instance with the + /// TraceOptions on a specific thread. + /// + /// @param[in] thread + /// The thread to start tracing on, in case whole process needs + /// to be traced use INVALID_THREAD_ID. + /// + /// @param[in] config + /// The configuration to use when starting tracing. + /// + /// @param[out] error + /// Error indicates what went wrong. + /// + /// @return + /// The API returns a user_id which can be used to get trace + /// data, trace configuration or stopping the trace instance. + /// The user_id is a key to identify and operate with a tracing + /// instance. It may refer to the complete process or a single + /// thread. + //------------------------------------------------------------------ + virtual lldb::user_id_t StartTrace(const TraceOptions &config, Error &error) { + error.SetErrorString("Not implemented"); + return LLDB_INVALID_UID; + } + + //------------------------------------------------------------------ + /// StopTracing API as the name suggests stops a tracing instance. + /// + /// @param[in] uid + /// The user id of the trace intended to be stopped. Now a + /// user_id may map to multiple threads in which case this API + /// could be used to stop the tracing for a specific thread by + /// supplying its thread id. + /// + /// @param[in] thread + /// Thread is needed when the complete process is being traced + /// and the user wishes to stop tracing on a particular thread. + /// + /// @return + /// Error indicating what went wrong. + //------------------------------------------------------------------ + virtual Error StopTrace(lldb::user_id_t uid, + lldb::tid_t thread = LLDB_INVALID_THREAD_ID) { + return Error("Not implemented"); + } + + //------------------------------------------------------------------ + /// This API provides the trace data collected in the form of raw + /// data. + /// + /// @param[in] uid thread + /// The uid and thread provide the context for the trace + /// instance. + /// + /// @param[in] buf buf_size + /// The buf and buf_size provide the destination buffer + /// where the trace data would be read to. + /// + /// @param[in] offset + /// There is possibility to read partially the trace data from + /// a specified offset where in such cases the buffer provided + /// may be smaller than the internal trace collection container. + /// + /// @return + /// The size of the data actually read. + //------------------------------------------------------------------ + virtual Error GetData(lldb::user_id_t uid, lldb::tid_t thread, + llvm::MutableArrayRef &buffer, + size_t offset = 0) { + return Error("Not implemented"); + } + + //------------------------------------------------------------------ + /// Similar API as above except it aims to provide any extra data + /// useful for decoding the actual trace data. + //------------------------------------------------------------------ + virtual Error GetMetaData(lldb::user_id_t uid, lldb::tid_t thread, + llvm::MutableArrayRef &buffer, + size_t offset = 0) { + return Error("Not implemented"); + } + + //------------------------------------------------------------------ + /// API to query the TraceOptions for a given user id + /// + /// @param[in] uid + /// The user id of the tracing instance. + /// + /// @param[in] threadid + /// The thread id of the tracing instance, in case configuration + /// for a specific thread is needed. + /// + /// @param[out] error + /// Error indicates what went wrong. + /// + /// @param[out] config + /// The actual configuration being used for tracing. + //------------------------------------------------------------------ + virtual Error GetTraceConfig(lldb::user_id_t uid, TraceOptions &config) { + return Error("Not implemented"); + } + protected: lldb::pid_t m_pid; Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -2780,7 +2780,7 @@ /// GetTraceConfig should supply the actual used trace /// configuration. //------------------------------------------------------------------ - virtual lldb::user_id_t StartTrace(lldb::TraceOptionsSP &options, + virtual lldb::user_id_t StartTrace(const TraceOptions &options, Error &error) { error.SetErrorString("Not implemented"); return LLDB_INVALID_UID; @@ -2795,9 +2795,8 @@ /// In the other case that tracing on an individual thread needs /// to be stopped a thread_id can be supplied. //------------------------------------------------------------------ - virtual void StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id, - Error &error) { - error.SetErrorString("Not implemented"); + virtual Error StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { + return Error("Not implemented"); } //------------------------------------------------------------------ @@ -2808,21 +2807,19 @@ /// may not. The thread_id should be used to select a particular /// thread for trace extraction. //------------------------------------------------------------------ - virtual size_t GetData(lldb::user_id_t uid, lldb::tid_t thread_id, - Error &error, void *buf, size_t size, - size_t offset = 0) { - error.SetErrorString("Not implemented"); - return 0; + virtual Error GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0) { + return Error("Not implemented"); } //------------------------------------------------------------------ /// Similar API as above except for obtaining meta data //------------------------------------------------------------------ - virtual size_t GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, - Error &error, void *buf, size_t size, - size_t offset = 0) { - error.SetErrorString("Not implemented"); - return 0; + virtual Error GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0) { + return Error("Not implemented"); } //------------------------------------------------------------------ @@ -2834,10 +2831,8 @@ /// configuration used by a specific thread. The thread_id specified /// should also match the uid otherwise an error will be returned. //------------------------------------------------------------------ - virtual void GetTraceConfig(lldb::user_id_t uid, Error &error, - lldb::TraceOptionsSP &options) { - error.SetErrorString("Not implemented"); - return; + virtual Error GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) { + return Error("Not implemented"); } protected: Index: include/lldb/Utility/StringExtractor.h =================================================================== --- include/lldb/Utility/StringExtractor.h +++ include/lldb/Utility/StringExtractor.h @@ -111,6 +111,8 @@ size_t GetHexByteStringTerminatedBy(std::string &str, char terminator); + bool Consume_front(const llvm::StringRef &str); + const char *Peek() { if (m_index < m_packet.size()) return m_packet.c_str() + m_index; Index: source/API/SBProcess.cpp =================================================================== --- source/API/SBProcess.cpp +++ source/API/SBProcess.cpp @@ -363,10 +363,9 @@ if (!process_sp) { error.SetErrorString("invalid process"); } else { - - uid = process_sp->StartTrace(options.m_traceoptions_sp, error.ref()); + uid = process_sp->StartTrace(*(options.m_traceoptions_sp), error.ref()); trace_instance.SetTraceUID(uid); - LLDB_LOG(log, "SBProcess::returned uid - %" PRIx64, uid); + LLDB_LOG(log, "SBProcess::returned uid - {0}", uid); } return trace_instance; } Index: source/API/SBTrace.cpp =================================================================== --- source/API/SBTrace.cpp +++ source/API/SBTrace.cpp @@ -25,37 +25,37 @@ size_t SBTrace::GetTraceData(SBError &error, void *buf, size_t size, size_t offset, lldb::tid_t thread_id) { - size_t bytes_read = 0; ProcessSP process_sp(GetSP()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + llvm::MutableArrayRef buffer(static_cast(buf), size); error.Clear(); if (!process_sp) { error.SetErrorString("invalid process"); } else { - bytes_read = process_sp->GetData(GetTraceUID(), thread_id, error.ref(), buf, - size, offset); - LLDB_LOG(log, "SBTrace::bytes_read - %" PRIx64, bytes_read); + error.SetError( + process_sp->GetData(GetTraceUID(), thread_id, buffer, offset)); + LLDB_LOG(log, "SBTrace::bytes_read - {0}", buffer.size()); } - return bytes_read; + return buffer.size(); } size_t SBTrace::GetMetaData(SBError &error, void *buf, size_t size, size_t offset, lldb::tid_t thread_id) { - size_t bytes_read = 0; ProcessSP process_sp(GetSP()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + llvm::MutableArrayRef buffer(static_cast(buf), size); error.Clear(); if (!process_sp) { error.SetErrorString("invalid process"); } else { - bytes_read = process_sp->GetMetaData(GetTraceUID(), thread_id, error.ref(), - buf, size, offset); - LLDB_LOG(log, "SBTrace::bytes_read - %" PRIx64, bytes_read); + error.SetError( + process_sp->GetMetaData(GetTraceUID(), thread_id, buffer, offset)); + LLDB_LOG(log, "SBTrace::bytes_read - {0}", buffer.size()); } - return bytes_read; + return buffer.size(); } void SBTrace::StopTrace(SBError &error, lldb::tid_t thread_id) { @@ -66,7 +66,7 @@ error.SetErrorString("invalid process"); return; } - process_sp->StopTrace(GetTraceUID(), thread_id, error.ref()); + error.SetError(process_sp->StopTrace(GetTraceUID(), thread_id)); } void SBTrace::GetTraceConfig(SBTraceOptions &options, SBError &error) { @@ -76,8 +76,8 @@ if (!process_sp) { error.SetErrorString("invalid process"); } else { - process_sp->GetTraceConfig(GetTraceUID(), error.ref(), - options.m_traceoptions_sp); + error.SetError(process_sp->GetTraceConfig(GetTraceUID(), + *(options.m_traceoptions_sp))); } } Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -189,6 +189,14 @@ PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet); + PacketResult Handle_JTrace_start(StringExtractorGDBRemote &packet); + + PacketResult Handle_jTrace_read(StringExtractorGDBRemote &packet); + + PacketResult Handle_JTrace_stop(StringExtractorGDBRemote &packet); + + PacketResult Handle_jTrace_conf_read(StringExtractorGDBRemote &packet); + PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet); PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -183,6 +183,22 @@ StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_JTrace_Start, + &GDBRemoteCommunicationServerLLGS::Handle_JTrace_start); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead, + &GDBRemoteCommunicationServerLLGS::Handle_jTrace_read); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead, + &GDBRemoteCommunicationServerLLGS::Handle_jTrace_read); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_JTrace_Stop, + &GDBRemoteCommunicationServerLLGS::Handle_JTrace_stop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead, + &GDBRemoteCommunicationServerLLGS::Handle_jTrace_conf_read); + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, [this](StringExtractorGDBRemote packet, Error &error, bool &interrupt, bool &quit) { @@ -1083,6 +1099,284 @@ } GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_JTrace_start( + StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + if (!packet.Consume_front("JTrace:start:")) + return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet "); + + TraceOptions options; + uint64_t type = std::numeric_limits::max(); + uint64_t buffersize = std::numeric_limits::max(); + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + uint64_t metabuffersize = std::numeric_limits::max(); + bool error_encountered = false; + + llvm::StringRef name, value; + StructuredData::ObjectSP json_object; + + while (packet.GetNameColonValue(name, value)) { + uint64_t extracted_value; + value.getAsInteger(16, extracted_value); + + if (name.equals("type")) + type = extracted_value; + + else if (name.equals("threadid")) + tid = extracted_value; + + else if (name.equals("buffersize")) + buffersize = extracted_value; + + else if (name.equals("metabuffersize")) + metabuffersize = extracted_value; + + else if (name.equals("jparams")) { + json_object = StructuredData::ParseJSON(value.data()); + } + + else { + error_encountered = true; + break; + } + } + + if (!json_object || + json_object->GetType() != StructuredData::Type::eTypeDictionary) + error_encountered = true; + + if (error_encountered || buffersize == std::numeric_limits::max() || + type != lldb::TraceType::eTraceTypeProcessorTrace || + packet.GetBytesLeft()) { + LLDB_LOG(log, + "Ill formed packet error_encountered = {0} buffersize = {1} type " + "= {2} packet empty = { 3 } ", + error_encountered, buffersize, type, packet.Empty()); + return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet "); + } + + options.setMetaDataBufferSize(metabuffersize); + options.setTraceBufferSize(buffersize); + options.setType(static_cast(type)); + options.setThreadID(tid); + options.setTraceParams( + std::static_pointer_cast(json_object)); + + Error error; + lldb::user_id_t uid = LLDB_INVALID_UID; + uid = m_debugged_process_sp->StartTrace(options, error); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + StreamGDBRemote response; + response.Printf("%" PRIx64, uid); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_JTrace_stop( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + if (!packet.Consume_front("JTrace:stop:")) + return SendIllFormedResponse(packet, "JTrace:stop: Ill formed packet "); + + lldb::user_id_t uid = LLDB_INVALID_UID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + bool error_encountered = false; + + llvm::StringRef name, value; + while (packet.GetNameColonValue(name, value)) { + uint64_t extracted_value; + if (!value.getAsInteger(16, extracted_value)) { + if (name.equals("threadid")) + tid = extracted_value; + + else if (name.equals("traceid")) + uid = extracted_value; + + else { + error_encountered = true; + break; + } + } + + else { + error_encountered = true; + break; + } + } + + if (error_encountered || uid == LLDB_INVALID_UID || packet.GetBytesLeft()) + return SendIllFormedResponse(packet, "JTrace:stop: Ill formed packet "); + + Error error = m_debugged_process_sp->StopTrace(uid, tid); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTrace_conf_read( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + if (!packet.Consume_front("jTrace:conf:read:")) + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + + lldb::user_id_t uid = LLDB_INVALID_UID; + lldb::tid_t threadid = LLDB_INVALID_THREAD_ID; + bool error_encountered = false; + + llvm::StringRef name, value; + while (packet.GetNameColonValue(name, value)) { + uint64_t extracted_value; + + if (!value.getAsInteger(16, extracted_value)) { + if (name.equals("threadid")) + threadid = extracted_value; + + else if (name.equals("traceid")) + uid = extracted_value; + + else { + error_encountered = true; + break; + } + } else { + error_encountered = true; + break; + } + } + + if (error_encountered || uid == LLDB_INVALID_UID || packet.GetBytesLeft()) + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + + TraceOptions options; + StreamGDBRemote response; + + options.setThreadID(threadid); + Error error = m_debugged_process_sp->GetTraceConfig(uid, options); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + response.Printf("type:%" PRIx32 ";", options.getType()); + response.Printf("buffersize:%" PRIx64 ";", options.getTraceBufferSize()); + response.Printf("metabuffersize:%" PRIx64 ";", + options.getMetaDataBufferSize()); + + const StructuredData::DictionarySP custom_params = options.getTraceParams(); + if (custom_params) { + response.Printf("jparams:"); + custom_params->Dump(response, false); + response.PutChar(';'); + } + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTrace_read( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + enum PacketType { MetaData, BufferData }; + PacketType tracetype = MetaData; + + if (packet.Consume_front("jTrace:buffer:read:")) + tracetype = BufferData; + else if (packet.Consume_front("jTrace:meta:read:")) + tracetype = MetaData; + else { + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + } + + lldb::user_id_t uid = LLDB_INVALID_UID; + + size_t byte_count = std::numeric_limits::max(); + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + size_t offset = std::numeric_limits::max(); + bool error_encountered = false; + llvm::StringRef name, value; + + while (packet.GetNameColonValue(name, value)) { + uint64_t extracted_value; + + if (!value.getAsInteger(16, extracted_value)) { + if (name.equals("buffersize")) + byte_count = extracted_value; + + else if (name.equals("threadid")) + tid = extracted_value; + + else if (name.equals("offset")) + offset = extracted_value; + + else if (name.equals("traceid")) + uid = extracted_value; + + else { + error_encountered = true; + break; + } + } else { + error_encountered = true; + break; + } + } + + if (error_encountered || uid == LLDB_INVALID_UID || packet.GetBytesLeft() || + byte_count == std::numeric_limits::max() || + offset == std::numeric_limits::max()) + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + + // Allocate the response buffer. + std::vector buffer(byte_count, '\0'); + if (buffer.empty()) + return SendErrorResponse(0x78); + + StreamGDBRemote response; + Error error; + llvm::MutableArrayRef buf(buffer); + + if (tracetype == BufferData) + error = m_debugged_process_sp->GetData(uid, tid, buf, offset); + else if (tracetype == MetaData) + error = m_debugged_process_sp->GetMetaData(uid, tid, buf, offset); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + for (size_t i = 0; i < buf.size(); ++i) + response.PutHex8(buf[i]); + + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -175,6 +175,21 @@ Error DisableWatchpoint(Watchpoint *wp, bool notify = true) override; + lldb::user_id_t StartTrace(const TraceOptions &options, + Error &error) override; + + Error StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override; + + Error GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0) override; + + Error GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0) override; + + Error GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override; + Error GetWatchpointSupportInfo(uint32_t &num) override; Error GetWatchpointSupportInfo(uint32_t &num, bool &after) override; @@ -407,6 +422,10 @@ std::map m_thread_id_to_used_usec_map; uint64_t m_last_signals_version = 0; + Error GetTraceData(StreamString &packet, lldb::user_id_t uid, + lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, size_t offset); + static bool NewThreadNotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -65,6 +65,7 @@ #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/StreamGDBRemote.h" #include "lldb/Utility/StreamString.h" // Project includes @@ -1236,6 +1237,181 @@ return error; } +lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options, + Error &error) { + + StreamString packet; + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + lldb::user_id_t ret_uid = LLDB_INVALID_UID; + + packet.PutCString("JTrace:start:"); + packet.Printf("type:%" PRIx32 ";", options.getType()); + packet.Printf("buffersize:%" PRIx64 ";", options.getTraceBufferSize()); + packet.Printf("metabuffersize:%" PRIx64 ";", options.getMetaDataBufferSize()); + + if (options.getThreadID() != LLDB_INVALID_THREAD_ID) + packet.Printf("threadid:%" PRIx64 ";", options.getThreadID()); + + StructuredData::DictionarySP custom_params = options.getTraceParams(); + if (custom_params) { + packet.Printf("jparams:"); + custom_params->Dump(packet, false); + packet.PutCString(";"); + } + StreamGDBRemote escaped_packet; + escaped_packet.PutEscapedBytes(packet.GetData(), packet.GetSize()); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(escaped_packet.GetString(), + response, true) == + GDBRemoteCommunication::PacketResult::Success) { + if (!response.IsNormalResponse()) { + error.SetError(response.GetError(), eErrorTypeGeneric); + LLDB_LOG(log, "Target does not support Tracing"); + } else { + ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + escaped_packet.GetData()); + } + return ret_uid; +} + +Error ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + StringExtractorGDBRemote response; + Error error; + + StreamString packet; + packet.PutCString("JTrace:stop:"); + packet.Printf("traceid:%" PRIx64 ";", uid); + + if (thread_id != LLDB_INVALID_THREAD_ID) + packet.Printf("threadid:%" PRIx64 ";", thread_id); + + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (!response.IsNormalResponse()) { + error.SetError(response.GetError(), eErrorTypeGeneric); + LLDB_LOG(log, "stop tracing failed"); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return error; +} + +Error ProcessGDBRemote::GetTraceData(StreamString &packet, lldb::user_id_t uid, + lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + Error error; + + packet.Printf("traceid:%" PRIx64 ";", uid); + packet.Printf("offset:%" PRIx64 ";", offset); + packet.Printf("buffersize:%" PRIx64 ";", buffer.size()); + + if (thread_id != LLDB_INVALID_THREAD_ID) + packet.Printf("threadid:%" PRIx64 ";", thread_id); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + size_t filled_size = response.GetHexBytesAvail(buffer); + buffer = llvm::MutableArrayRef(buffer.data(), filled_size); + } else { + error.SetError(response.GetError(), eErrorTypeGeneric); + buffer = buffer.slice(buffer.size()); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + buffer = buffer.slice(buffer.size()); + } + return error; +} + +Error ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset) { + + StreamString packet; + packet.PutCString("jTrace:buffer:read:"); + return GetTraceData(packet, uid, thread_id, buffer, offset); +} + +Error ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset) { + StreamString packet; + packet.PutCString("jTrace:meta:read:"); + return GetTraceData(packet, uid, thread_id, buffer, offset); +} + +Error ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid, + TraceOptions &options) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + StringExtractorGDBRemote response; + Error error; + + StreamString packet; + packet.PutCString("jTrace:conf:read:"); + packet.Printf("traceid:%" PRIx64 ";", uid); + + if (options.getThreadID() != LLDB_INVALID_THREAD_ID) + packet.Printf("threadid:%" PRIx64 ";", options.getThreadID()); + + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name, value; + StructuredData::ObjectSP json_object; + while (response.GetNameColonValue(name, value)) { + uint64_t extracted_value; + value.getAsInteger(16, extracted_value); + + if (name.equals("buffersize")) { + options.setTraceBufferSize(extracted_value); + } else if (name.equals("metabuffersize")) { + options.setMetaDataBufferSize(extracted_value); + } else if (name.equals("type")) { + options.setType(static_cast(extracted_value)); + } else if (name.equals("jparams")) { + auto json_object = StructuredData::ParseJSON(value.data()); + if (!json_object || + json_object->GetType() != StructuredData::Type::eTypeDictionary) { + error.SetErrorString("Invalid Configuration obtained"); + break; + } + options.setTraceParams( + std::static_pointer_cast( + json_object)); + } else { + error.SetErrorString("Invalid Configuration obtained"); + break; + } + } + } else { + error.SetError(response.GetError(), eErrorTypeGeneric); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return error; +} + void ProcessGDBRemote::DidExit() { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); Index: source/Utility/StringExtractor.cpp =================================================================== --- source/Utility/StringExtractor.cpp +++ source/Utility/StringExtractor.cpp @@ -280,6 +280,15 @@ return result; } +bool StringExtractor::Consume_front(const llvm::StringRef &str) { + llvm::StringRef S = GetStringRef(); + if (!S.startswith(str)) + return false; + else + m_index += str.size(); + return true; +} + size_t StringExtractor::GetHexBytes(llvm::MutableArrayRef dest, uint8_t fail_fill_value) { size_t bytes_extracted = 0; Index: source/Utility/StringExtractorGDBRemote.h =================================================================== --- source/Utility/StringExtractorGDBRemote.h +++ source/Utility/StringExtractorGDBRemote.h @@ -164,6 +164,12 @@ eServerPacketType__M, eServerPacketType__m, eServerPacketType_notify, // '%' notification + + eServerPacketType_JTrace_Start, + eServerPacketType_jTraceBufferRead, + eServerPacketType_jTraceMetaRead, + eServerPacketType_JTrace_Stop, + eServerPacketType_jTraceConfigRead, }; ServerPacketType GetServerPacketType() const; Index: source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- source/Utility/StringExtractorGDBRemote.cpp +++ source/Utility/StringExtractorGDBRemote.cpp @@ -279,6 +279,13 @@ } break; + case 'J': + if (PACKET_STARTS_WITH("JTrace:start:")) + return eServerPacketType_JTrace_Start; + if (PACKET_STARTS_WITH("JTrace:stop:")) + return eServerPacketType_JTrace_Stop; + break; + case 'j': if (PACKET_STARTS_WITH("jModulesInfo:")) return eServerPacketType_jModulesInfo; @@ -286,6 +293,12 @@ return eServerPacketType_jSignalsInfo; if (PACKET_MATCHES("jThreadsInfo")) return eServerPacketType_jThreadsInfo; + if (PACKET_STARTS_WITH("jTrace:buffer:read:")) + return eServerPacketType_jTraceBufferRead; + if (PACKET_STARTS_WITH("jTrace:conf:read:")) + return eServerPacketType_jTraceConfigRead; + if (PACKET_STARTS_WITH("jTrace:meta:read:")) + return eServerPacketType_jTraceMetaRead; break; case 'v':