Index: docs/lldb-gdb-remote.txt =================================================================== --- docs/lldb-gdb-remote.txt +++ docs/lldb-gdb-remote.txt @@ -209,6 +209,143 @@ read packet: OK //---------------------------------------------------------------------- +// QTrace:1:type:; +// +// BRIEF +// Packet for starting tracing of type lldb::TraceType. The following +// parameters (mandatory and optional) should be appended to the packet +// although there is no specific order imposed. Different tracing types +// could require different custom parameters. Any custom tracing +// parameters needed should be appended to the packet as a "Name:Value;" +// pair seperated by a semicolon, where the Value should be a JSON +// Structure. +// +// Following is the list of parameters - +// +// Name Value (Hex Encoded 64-bit integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// +// 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 M +// used for decoding the trace data. +// ========== ==================================================== +// +// Each tracing instance is identified by a user 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: QTrace:1:type:;buffersize:; +read packet: /E + +//---------------------------------------------------------------------- +// QTrace:0:userid:; +// +// BRIEF +// Stop tracing instance with user 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 +// ========== ==================================================== +// +// 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 empty response is sent in case of success else an error code is +// returned. +//---------------------------------------------------------------------- + +send packet: QTrace:0:userid:; +read packet: /E + +//---------------------------------------------------------------------- +// qTrace:buffer:read:userid:; +// +// 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 +// ========== ==================================================== +// +// 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. The exact order of the parameters is not +// fixed. +//---------------------------------------------------------------------- + +send packet: qTrace:buffer:read:userid:;offset:;buffersize:; +read packet: /E + +//---------------------------------------------------------------------- +// qTrace:meta:read:userid:; +// +// BRIEF +// Similar Packet as above except it reads meta data. +//---------------------------------------------------------------------- + +/---------------------------------------------------------------------- +// qTrace:conf:read:userid:; +// +// 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 +// ========== ==================================================== +// +// 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 ). +// In case the trace instance with the was not found, an +// error code is returned. +//---------------------------------------------------------------------- + +send packet: qTrace:conf:read:userid:; +read packet: conf1:;conf2:;/E + +//---------------------------------------------------------------------- // "qRegisterInfo" // // BRIEF 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,128 @@ 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 StartTracing(lldb::tid_t thread, TraceOptions &config, + Error &error) { + error.SetErrorString("Not implemented"); + return LLDB_INVALID_UID; + } + + //------------------------------------------------------------------ + /// Similar API as above except used to enable tracing on complete + /// process. Once tracing is enabled for the whole process, all + /// newly spawned threads will also be traced with the same config + /// but any errors occurring for starting the trace for them will + /// not be reported as those would be through async notifications. + //------------------------------------------------------------------ + virtual lldb::user_id_t StartTracingAllThreads(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 StopTracing(lldb::user_id_t uid, + lldb::tid_t thread = LLDB_INVALID_THREAD_ID) { + Error error; + error.SetErrorString("Not implemented"); + return error; + } + + //------------------------------------------------------------------ + /// 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 size_t GetTraceData(lldb::user_id_t uid, lldb::tid_t thread, + Error &error, void *buf, size_t buf_size, + size_t offset = 0) { + error.SetErrorString("Not implemented"); + return 0; + } + + //------------------------------------------------------------------ + /// Similar API as above except it aims to provide any extra data + /// useful for decoding the actual trace data. + //------------------------------------------------------------------ + virtual size_t GetTraceMetaData(lldb::user_id_t uid, lldb::tid_t thread, + Error &error, void *buf, size_t buf_size, + size_t offset = 0) { + error.SetErrorString("Not implemented"); + return 0; + } + + //------------------------------------------------------------------ + /// 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 void GetTraceConfig(lldb::user_id_t uid, lldb::tid_t threadid, + Error &error, TraceOptions &config) { + error.SetErrorString("Not implemented"); + } + protected: lldb::pid_t m_pid; 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_QTrace_start(StringExtractorGDBRemote &packet); + + PacketResult Handle_qTrace_read(StringExtractorGDBRemote &packet); + + PacketResult Handle_QTrace_stop(StringExtractorGDBRemote &packet); + + PacketResult Handle_qTrace_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_QTrace_Start, + &GDBRemoteCommunicationServerLLGS::Handle_QTrace_start); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qTraceBufferRead, + &GDBRemoteCommunicationServerLLGS::Handle_qTrace_read); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qTraceMetaRead, + &GDBRemoteCommunicationServerLLGS::Handle_qTrace_read); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QTrace_Stop, + &GDBRemoteCommunicationServerLLGS::Handle_QTrace_stop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qTraceConfigRead, + &GDBRemoteCommunicationServerLLGS::Handle_qTrace_conf_read); + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, [this](StringExtractorGDBRemote packet, Error &error, bool &interrupt, bool &quit) { @@ -1083,6 +1099,335 @@ } GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QTrace_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 (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + if (packet.GetStringRef().find("QTrace:1:type:") != 0) + return SendIllFormedResponse(packet, "QTrace:1: Ill formed packet "); + + packet.SetFilePos(strlen("QTrace:1:type:")); + + const uint64_t fail_value = std::numeric_limits::max(); + uint64_t type = packet.GetU64(fail_value, 16); + lldb::TraceType tracetype = lldb::TraceType::eTraceTypeNone; + + if (type == lldb::TraceType::eTraceTypeProcessorTrace && + packet.GetChar('\0') == ';') + tracetype = lldb::TraceType::eTraceTypeProcessorTrace; + else { + if (log) + log->Printf( + "GDBRemoteCommunicationServerLLGS::%s IllFormed type - %" PRIx32, + __FUNCTION__, tracetype); + return SendIllFormedResponse(packet, "QTrace:1: Ill formed packet "); + } + + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + uint64_t buffersize = std::numeric_limits::max(); + TraceOptions options; + uint64_t metabuffersize = options.getMetaDataBufferSize(); + + StructuredData::DictionarySP custom_params(new StructuredData::Dictionary()); + llvm::StringRef name, value; + + while (packet.GetNameColonValue(name, value)) { + StringExtractor value_extractor(value); + uint64_t extracted_value = value_extractor.GetU64(fail_value, 16); + + if (name.equals("threadid")) + tid = extracted_value; + + else if (name.equals("buffersize")) + buffersize = extracted_value; + + else if (name.equals("metabuffersize")) + metabuffersize = extracted_value; + + else { + StructuredData::ObjectSP key_object = StructuredData::ParseJSON(value.data()); + custom_params->AddItem(name, key_object); + } + } + + if (buffersize == std::numeric_limits::max() || + packet.GetBytesLeft() != 0) { + + if (log) + log->Printf( + "GDBRemoteCommunicationServerLLGS::%s IllFormed thread %" PRIx64 + " , buf size - %" PRIx64 ", meta size - %" PRIx64 ", type - %" PRIx32, + __FUNCTION__, tid, buffersize, metabuffersize, tracetype); + return SendIllFormedResponse(packet, "QTrace:1: Ill formed packet "); + } + + options.setMetaDataBufferSize(metabuffersize); + options.setTraceBufferSize(buffersize); + options.setType(tracetype); + options.setTraceParams(custom_params); + + Error error; + lldb::user_id_t uid = LLDB_INVALID_UID; + if (tid != LLDB_INVALID_THREAD_ID) + uid = m_debugged_process_sp->StartTracing(tid, options, error); + else + uid = m_debugged_process_sp->StartTracingAllThreads(options, error); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + StreamGDBRemote response; + response.Printf("%" PRIx64, uid); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qTrace_conf_read( + 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.GetStringRef().find("qTrace:conf:read:userid:") != 0) + return SendIllFormedResponse(packet, "qTrace: Ill formed packet "); + packet.SetFilePos(strlen("qTrace:conf:read:userid:")); + + lldb::user_id_t uid = LLDB_INVALID_UID; + const uint64_t fail_value = LLDB_INVALID_UID; + + uid = packet.GetU64(fail_value, 16); + if (uid == fail_value || packet.GetChar('\0') != ';') { + if (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s IllFormed type", + __FUNCTION__); + return SendIllFormedResponse(packet, "qTrace: Ill formed packet "); + } + + lldb::tid_t threadid = LLDB_INVALID_THREAD_ID; + bool error_encountered = false; + + llvm::StringRef name, value; + while (packet.GetNameColonValue(name, value)) { + + StringExtractor value_extractor(value); + uint64_t extracted_value; + + if (name.equals("threadid")) + threadid = extracted_value = value_extractor.GetU64(fail_value, 16); + else { + error_encountered = true; + break; + } + + } + + if (error_encountered || packet.GetBytesLeft() != 0) + return SendIllFormedResponse(packet, "qTrace: Ill formed packet "); + + if (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s called, userid -%" PRIx64, + __FUNCTION__, uid); + + TraceOptions options; + StreamGDBRemote response; + + Error error; + m_debugged_process_sp->GetTraceConfig(uid, threadid, error, 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) { + custom_params->ForEach([&response](ConstString key, + StructuredData::Object *object) -> bool { + if (object != nullptr) { + response.Printf("%s:", key.AsCString()); + object->Dump(response, false); + response.PutChar(';'); + return true; + } + return false; + }); + } + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QTrace_stop( + 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 (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + if (packet.GetStringRef().find("QTrace:0:userid:") != 0) + return SendIllFormedResponse(packet, "QTrace:0: Ill formed packet "); + packet.SetFilePos(strlen("QTrace:0:userid:")); + + lldb::user_id_t uid = LLDB_INVALID_UID; + const uint64_t fail_value = LLDB_INVALID_UID; + + uid = packet.GetU64(fail_value, 16); + if (uid == fail_value || packet.GetChar('\0') != ';') { + if (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s IllFormed type", + __FUNCTION__); + return SendIllFormedResponse(packet, "QTrace:0: Ill formed packet "); + } + + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + bool error_encountered = false; + + llvm::StringRef name, value; + while (packet.GetNameColonValue(name, value)) { + StringExtractor value_extractor(value); + uint64_t extracted_value; + + if (name.equals("threadid")) + tid = extracted_value = value_extractor.GetU64(fail_value, 16); + + else { + error_encountered = true; + break; + } + + } + + if (error_encountered || packet.GetBytesLeft() != 0) + return SendIllFormedResponse(packet, "QTrace:0: Ill formed packet "); + + Error error; + error = m_debugged_process_sp->StopTracing(uid, tid); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + StreamGDBRemote response; + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qTrace_read( + 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); + + enum PacketType { MetaData, BufferData }; + PacketType tracetype = MetaData; + + if (packet.GetStringRef().find("qTrace:buffer:read:userid:") == 0) { + tracetype = BufferData; + packet.SetFilePos(strlen("qTrace:buffer:read:userid:")); + } else if (packet.GetStringRef().find("qTrace:meta:read:userid:") == 0) { + tracetype = MetaData; + packet.SetFilePos(strlen("qTrace:meta:read:userid:")); + } else { + return SendIllFormedResponse(packet, "qTrace: Ill formed packet "); + } + + lldb::user_id_t uid = LLDB_INVALID_UID; + const uint64_t fail_value = LLDB_INVALID_UID; + + uid = packet.GetU64(fail_value, 16); + if (uid == fail_value || packet.GetChar('\0') != ';') { + if (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s IllFormed type", + __FUNCTION__); + return SendIllFormedResponse(packet, "qTrace: Ill formed packet "); + } + + 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)) { + StringExtractor value_extractor(value); + uint64_t extracted_value; + + if (name.equals("buffersize")) + byte_count = extracted_value = value_extractor.GetU64(fail_value, 16); + + else if (name.equals("threadid")) + tid = extracted_value = value_extractor.GetU64(fail_value, 16); + + else if (name.equals("offset")) + offset = extracted_value = value_extractor.GetU64(fail_value, 16); + + else { + error_encountered = true; + break; + } + + } + + if (error_encountered || packet.GetBytesLeft() != 0 || + byte_count == std::numeric_limits::max() || + offset == std::numeric_limits::max()) + return SendIllFormedResponse(packet, "qTrace: Ill formed packet "); + + if (log) + log->Printf("GDBRemoteCommunicationServerLLGS::%s called, %" PRIx64 + " userid -%" PRIx64, + __FUNCTION__, byte_count, uid); + + // Allocate the response buffer. + std::vector buf(byte_count, '\0'); + if (buf.empty()) + return SendErrorResponse(0x78); + + size_t filled_data = 0; + StreamGDBRemote response; + Error error; + if (tracetype == BufferData) + filled_data = m_debugged_process_sp->GetTraceData( + uid, tid, error, buf.data(), byte_count, offset); + else if (tracetype == MetaData) + filled_data = m_debugged_process_sp->GetTraceMetaData( + uid, tid, error, buf.data(), byte_count, offset); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + if (filled_data > byte_count) + return SendErrorResponse(0x08); + + for (size_t i = 0; i < filled_data; ++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,27 @@ Error DisableWatchpoint(Watchpoint *wp, bool notify = true) override; + virtual lldb::user_id_t StartTrace(lldb::TraceOptionsSP &options, + Error &error) override; + + virtual void StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error) override; + + 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) override; + + 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) override; + + size_t GetTraceData(StreamString &packet, lldb::user_id_t uid, + lldb::tid_t thread_id, Error &error, void *buf, + size_t size, size_t offset = 0); + + virtual void GetTraceConfig(lldb::user_id_t uid, Error &error, + lldb::TraceOptionsSP &options) override; + Error GetWatchpointSupportInfo(uint32_t &num) override; Error GetWatchpointSupportInfo(uint32_t &num, bool &after) override; Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1236,6 +1236,198 @@ return error; } +lldb::user_id_t ProcessGDBRemote::StartTrace(lldb::TraceOptionsSP &options, + Error &error) { + + StreamString packet; + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + lldb::user_id_t ret_uid = LLDB_INVALID_UID; + + if (!options) { + error.SetErrorString("Null Trace Options"); + return ret_uid; + } + + packet.PutCString("QTrace:1:"); + 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(); + custom_params->ForEach( + [&packet](ConstString key, StructuredData::Object *object) -> bool { + if (object != nullptr) { + packet.Printf("%s:", key.AsCString()); + object->Dump(packet, false); + packet.PutCString(";"); + return true; + } + return false; + }); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (!response.IsNormalResponse()) { + error.SetError(response.GetError(), eErrorTypeGeneric); + if (log) + log->Printf("ProcessGDBRemote::%s Target does not support tracing", + __FUNCTION__); + } else { + ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID); + } + } else { + if (log) + log->Printf("ProcessGDBRemote::%s failed to send packet", __FUNCTION__); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return ret_uid; +} + +void ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + StringExtractorGDBRemote response; + + StreamString packet; + packet.PutCString("QTrace:0:"); + packet.Printf("userid:%" 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); + if (log) + log->Printf("ProcessGDBRemote::%s stop trace failed", __FUNCTION__); + } + } else { + if (log) + log->Printf("ProcessGDBRemote::%s failed to send packet", __FUNCTION__); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } +} + +size_t ProcessGDBRemote::GetTraceData(StreamString &packet, lldb::user_id_t uid, + lldb::tid_t thread_id, Error &error, + void *buf, size_t size, size_t offset) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf("ProcessGDBRemote::%s size is %" PRIx64, __FUNCTION__, size); + + packet.Printf("userid:%" PRIx64 ";", uid); + packet.Printf("offset:%" PRIx64 ";", offset); + packet.Printf("buffersize:%" PRIx64 ";", 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 buf_size = std::min(size, response.GetBytesLeft()); + return response.GetHexBytesAvail(llvm::MutableArrayRef( + reinterpret_cast(buf), buf_size)); + } else { + error.SetError(response.GetError(), eErrorTypeGeneric); + if (log) + log->Printf("ProcessGDBRemote::%s failed", __FUNCTION__); + } + } else { + if (log) + log->Printf("ProcessGDBRemote::%s Failed to send the packet", + __FUNCTION__); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return 0; +} + +size_t ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error, void *buf, size_t size, + size_t offset) { + + StreamString packet; + packet.PutCString("qTrace:buffer:read:"); + return GetTraceData(packet, uid, thread_id, error, buf, size, offset); +} + +size_t ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + Error &error, void *buf, size_t size, + size_t offset) { + StreamString packet; + packet.PutCString("qTrace:meta:read:"); + return GetTraceData(packet, uid, thread_id, error, buf, size, offset); +} + +void ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid, Error &error, + lldb::TraceOptionsSP &options) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + StringExtractorGDBRemote response; + + StreamString packet; + packet.PutCString("qTrace:conf:read:"); + packet.Printf("userid:%" PRIx64 ";", uid); + + if (options->getThreadID() != LLDB_INVALID_THREAD_ID) + packet.Printf("threadid:%" PRIx64 ";", options->getThreadID()); + + StructuredData::DictionarySP custom_params(new StructuredData::Dictionary()); + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name, value; + const uint64_t fail_value = std::numeric_limits::max(); + while (response.GetNameColonValue(name, value)) { + StringExtractor value_extractor(value); + uint64_t extracted_value = value_extractor.GetU64(fail_value, 16); + + if (name.equals("buffersize")) { + options->setTraceBufferSize(extracted_value); + } else if (name.equals("metabuffersize")) { + options->setMetaDataBufferSize(extracted_value); + } else if (name.equals("type")) { + if (extracted_value == lldb::TraceType::eTraceTypeProcessorTrace) + options->setType(lldb::TraceType::eTraceTypeProcessorTrace); + else { + error.SetErrorString("Invalid Configuration obtained"); + break; + } + } else { + StructuredData::ObjectSP key_object = + StructuredData::ParseJSON(value.data()); + custom_params->AddItem(name, key_object); + } + } + + options->setTraceParams(custom_params); + } else { + error.SetError(response.GetError(), eErrorTypeGeneric); + if (log) + log->Printf("ProcessGDBRemote::%s get trace config failed", + __FUNCTION__); + } + } else { + if (log) + log->Printf("ProcessGDBRemote::%s failed to send packet", __FUNCTION__); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } +} + void ProcessGDBRemote::DidExit() { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); 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_QTrace_Start, + eServerPacketType_qTraceBufferRead, + eServerPacketType_qTraceMetaRead, + eServerPacketType_QTrace_Stop, + eServerPacketType_qTraceConfigRead, }; ServerPacketType GetServerPacketType() const; Index: source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- source/Utility/StringExtractorGDBRemote.cpp +++ source/Utility/StringExtractorGDBRemote.cpp @@ -136,6 +136,10 @@ case 'T': if (PACKET_MATCHES("QThreadSuffixSupported")) return eServerPacketType_QThreadSuffixSupported; + if (PACKET_STARTS_WITH("QTrace:1:")) + return eServerPacketType_QTrace_Start; + if (PACKET_STARTS_WITH("QTrace:0:")) + return eServerPacketType_QTrace_Stop; break; } break; @@ -253,6 +257,12 @@ return eServerPacketType_qThreadExtraInfo; if (PACKET_STARTS_WITH("qThreadStopInfo")) return eServerPacketType_qThreadStopInfo; + if (PACKET_STARTS_WITH("qTrace:buffer:read:")) + return eServerPacketType_qTraceBufferRead; + if (PACKET_STARTS_WITH("qTrace:conf:read:")) + return eServerPacketType_qTraceConfigRead; + if (PACKET_STARTS_WITH("qTrace:meta:read:")) + return eServerPacketType_qTraceMetaRead; break; case 'U':