diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index e0ac4f84c902..f5bb8b4c377a 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -145,6 +145,12 @@ public: const char * GetLaunchEventData () const; + bool + GetDetachOnError() const; + + void + SetDetachOnError(bool enable); + protected: friend class SBTarget; diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 94bc9581ee4e..69a2c4ef265e 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -866,6 +866,21 @@ public: return m_event_data.c_str(); } + void + SetDetachOnError (bool enable) + { + if (enable) + m_flags.Set(lldb::eLaunchFlagDetachOnError); + else + m_flags.Clear(lldb::eLaunchFlagDetachOnError); + } + + bool + GetDetachOnError () const + { + return m_flags.Test(lldb::eLaunchFlagDetachOnError); + } + protected: std::string m_working_dir; std::string m_plugin_name; @@ -882,9 +897,9 @@ protected: }; //---------------------------------------------------------------------- -// ProcessLaunchInfo +// ProcessAttachInfo // -// Describes any information that is required to launch a process. +// Describes any information that is required to attach to a process. //---------------------------------------------------------------------- class ProcessAttachInfo : public ProcessInstanceInfo @@ -896,7 +911,8 @@ public: m_resume_count (0), m_wait_for_launch (false), m_ignore_existing (true), - m_continue_once_attached (false) + m_continue_once_attached (false), + m_detach_on_error (true) { } @@ -906,12 +922,14 @@ public: m_resume_count (0), m_wait_for_launch (false), m_ignore_existing (true), - m_continue_once_attached (false) + m_continue_once_attached (false), + m_detach_on_error(true) { ProcessInfo::operator= (launch_info); SetProcessPluginName (launch_info.GetProcessPluginName()); SetResumeCount (launch_info.GetResumeCount()); SetHijackListener(launch_info.GetHijackListener()); + m_detach_on_error = launch_info.GetDetachOnError(); } bool @@ -1014,7 +1032,18 @@ public: m_hijack_listener_sp = listener_sp; } - + bool + GetDetachOnError () const + { + return m_detach_on_error; + } + + void + SetDetachOnError (bool enable) + { + m_detach_on_error = enable; + } + protected: lldb::ListenerSP m_hijack_listener_sp; std::string m_plugin_name; @@ -1022,6 +1051,7 @@ protected: bool m_wait_for_launch; bool m_ignore_existing; bool m_continue_once_attached; // Supports the use-case scenario of immediately continuing the process once attached. + bool m_detach_on_error; // If we are debugging remotely, instruct the stub to detach rather than killing the target on error. }; class ProcessLaunchCommandOptions : public Options diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 25e0eaa7786a..3393bb670338 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -81,6 +81,12 @@ public: void SetDisableASLR (bool b); + bool + GetDetachOnError () const; + + void + SetDetachOnError (bool b); + bool GetDisableSTDIO () const; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 8596f6de6d8d..33e34dfb2add 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -39,16 +39,18 @@ namespace lldb { typedef enum LaunchFlags { eLaunchFlagNone = 0u, - eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling process into a new process - eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to allow the process to be debugged - eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point instead of auto-continuing when launching or attaching at entry point - eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization - eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app) - eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host - eLaunchFlagLaunchInShell= (1u << 6), ///< Launch the process inside a shell to get shell expansion + eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling process into a new process + eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to allow the process to be debugged + eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point instead of auto-continuing when launching or attaching at entry point + eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization + eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app) + eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host + eLaunchFlagLaunchInShell= (1u << 6), ///< Launch the process inside a shell to get shell expansion eLaunchFlagLaunchInSeparateProcessGroup = (1u << 7), ///< Launch the process in a separate process group - eLaunchFlagsDontSetExitStatus = (1u << 8) ///< If you are going to hand the process off (e.g. to debugserver) - ///< set this flag so lldb & the handee don't race to set its exit status. + eLaunchFlagDontSetExitStatus = (1u << 8), ///< If you are going to hand the process off (e.g. to debugserver) + ///< set this flag so lldb & the handee don't race to set its exit status. + eLaunchFlagDetachOnError = (1u << 9) ///< If set, then the client stub should detach rather than killing the debugee + ///< if it loses connection with lldb. } LaunchFlags; //---------------------------------------------------------------------- diff --git a/lldb/scripts/Python/interface/SBTarget.i b/lldb/scripts/Python/interface/SBTarget.i index 1f697bad587b..bbf1f74faa5d 100644 --- a/lldb/scripts/Python/interface/SBTarget.i +++ b/lldb/scripts/Python/interface/SBTarget.i @@ -107,6 +107,12 @@ public: const char * GetLaunchEventData () const; + bool + GetDetachOnError() const; + + void + SetDetachOnError(bool enable); + }; class SBAttachInfo diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 900ccaf297f6..5826ee676d2a 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -292,6 +292,18 @@ SBLaunchInfo::GetLaunchEventData () const return m_opaque_sp->GetLaunchEventData (); } +void +SBLaunchInfo::SetDetachOnError (bool enable) +{ + m_opaque_sp->SetDetachOnError (enable); +} + +bool +SBLaunchInfo::GetDetachOnError () const +{ + return m_opaque_sp->GetDetachOnError (); +} + SBAttachInfo::SBAttachInfo () : m_opaque_sp (new ProcessAttachInfo()) { diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 92735ecee19c..4952ed5f6cb0 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -208,6 +208,9 @@ protected: if (target->GetDisableASLR()) m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + if (target->GetDetachOnError()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDetachOnError); + if (target->GetDisableSTDIO()) m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm index f4932ef37f3d..375f9e4fc1e1 100644 --- a/lldb/source/Host/macosx/Host.mm +++ b/lldb/source/Host/macosx/Host.mm @@ -1669,7 +1669,7 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info) const bool monitor_signals = false; Host::MonitorChildProcessCallback callback = nullptr; - if (!launch_info.GetFlags().Test(lldb::eLaunchFlagsDontSetExitStatus)) + if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) callback = Process::SetProcessExitStatus; StartMonitoringChildProcess (callback, diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp index 3ff28e12ec19..29d8bc832cda 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -788,7 +788,7 @@ PlatformDarwin::DebugProcess (ProcessLaunchInfo &launch_info, // We are going to hand this process off to debugserver which will be in charge of setting the exit status. // We still need to reap it from lldb but if we let the monitor thread also set the exit status, we set up a // race between debugserver & us for who will find out about the debugged process's death. - launch_info.GetFlags().Set(eLaunchFlagsDontSetExitStatus); + launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus); process_sp = Platform::DebugProcess (launch_info, debugger, target, listener, error); } else diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index aa1b6eee8aa0..6641998c0ba8 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -353,6 +353,7 @@ PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info) m_gdb_client.SetSTDOUT ("/dev/null"); m_gdb_client.SetSTDERR ("/dev/null"); m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)); + m_gdb_client.SetDetachOnError (launch_info.GetFlags().Test (eLaunchFlagDetachOnError)); const char *working_dir = launch_info.GetWorkingDirectory(); if (working_dir && working_dir[0]) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index f7beac2100e2..c3c2c6738725 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2256,6 +2256,25 @@ GDBRemoteCommunicationClient::SetDisableASLR (bool enable) return -1; } +int +GDBRemoteCommunicationClient::SetDetachOnError (bool enable) +{ + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDetachOnError:%i", enable ? 1 : 0); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + return -1; +} + + bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 1166a6f90254..556465d153dc 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -205,13 +205,25 @@ public: /// be launched with the 'A' packet. /// /// @param[in] enable - /// A boolean value indicating wether to disable ASLR or not. + /// A boolean value indicating whether to disable ASLR or not. /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetDisableASLR (bool enable); + + //------------------------------------------------------------------ + /// Sets the DetachOnError flag to \a enable for the process controlled by the stub. + /// + /// @param[in] enable + /// A boolean value indicating whether to detach on error or not. + /// + /// @return + /// Zero if the for success, or an error code for failure. + //------------------------------------------------------------------ + int + SetDetachOnError (bool enable); //------------------------------------------------------------------ /// Sets the working directory to \a path for a process that will diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 2fadf3359f56..21f9eda2b874 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -202,6 +202,10 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, packet_result = Handle_QSetDisableASLR (packet); break; + case StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError: + packet_result = Handle_QSetDetachOnError (packet); + break; + case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN: packet_result = Handle_QSetSTDIN (packet); break; @@ -1139,6 +1143,17 @@ GDBRemoteCommunicationServer::Handle_QSetDisableASLR (StringExtractorGDBRemote & return SendOKResponse (); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::Handle_QSetDetachOnError (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetDetachOnError:")); + if (packet.GetU32(0)) + m_process_launch_info.GetFlags().Set (eLaunchFlagDetachOnError); + else + m_process_launch_info.GetFlags().Clear (eLaunchFlagDetachOnError); + return SendOKResponse (); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index 913c6b673cfb..4bc0857b858f 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -264,6 +264,9 @@ protected: PacketResult Handle_QSetDisableASLR (StringExtractorGDBRemote &packet); + PacketResult + Handle_QSetDetachOnError (StringExtractorGDBRemote &packet); + PacketResult Handle_QSetWorkingDir (StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 7adf9165a3ef..521e35422c7e 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -799,6 +799,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) m_gdb_comm.SetSTDERR (stderr_path); m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR); + m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket (m_target.GetArchitecture().GetArchitectureName()); @@ -1071,6 +1072,8 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const Process if (error.Success()) { + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); + char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID (attach_pid); @@ -1108,6 +1111,8 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro { StreamString packet; + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); + if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 35b41121c128..9deafa9c6f1a 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -2642,6 +2642,7 @@ g_properties[] = { "input-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for reading its standard input." }, { "output-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for writing its standard output." }, { "error-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for writing its standard error." }, + { "detach-on-error" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "debugserver will detach (rather than killing) a process if it loses connection with lldb." }, { "disable-aslr" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Disable Address Space Layout Randomization (ASLR)" }, { "disable-stdio" , OptionValue::eTypeBoolean , false, false , NULL, NULL, "Disable stdin/stdout for process (e.g. for a GUI application)" }, { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsHeaders , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " @@ -2688,6 +2689,7 @@ enum ePropertyInputPath, ePropertyOutputPath, ePropertyErrorPath, + ePropertyDetachOnError, ePropertyDisableASLR, ePropertyDisableSTDIO, ePropertyInlineStrategy, @@ -2870,6 +2872,20 @@ TargetProperties::SetDisableASLR (bool b) m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); } +bool +TargetProperties::GetDetachOnError () const +{ + const uint32_t idx = ePropertyDetachOnError; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +void +TargetProperties::SetDetachOnError (bool b) +{ + const uint32_t idx = ePropertyDetachOnError; + m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); +} + bool TargetProperties::GetDisableSTDIO () const { diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index eccb81318131..c8633d7ce1f8 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -92,6 +92,7 @@ StringExtractorGDBRemote::GetServerPacketType () const if (PACKET_MATCHES ("QStartNoAckMode")) return eServerPacketType_QStartNoAckMode; if (PACKET_STARTS_WITH ("QSaveRegisterState")) return eServerPacketType_QSaveRegisterState; if (PACKET_STARTS_WITH ("QSetDisableASLR:")) return eServerPacketType_QSetDisableASLR; + if (PACKET_STARTS_WITH ("QSetDetachOnError:")) return eServerPacketType_QSetDetachOnError; if (PACKET_STARTS_WITH ("QSetSTDIN:")) return eServerPacketType_QSetSTDIN; if (PACKET_STARTS_WITH ("QSetSTDOUT:")) return eServerPacketType_QSetSTDOUT; if (PACKET_STARTS_WITH ("QSetSTDERR:")) return eServerPacketType_QSetSTDERR; diff --git a/lldb/source/Utility/StringExtractorGDBRemote.h b/lldb/source/Utility/StringExtractorGDBRemote.h index f8af3ca41a79..e73dd97e0203 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.h +++ b/lldb/source/Utility/StringExtractorGDBRemote.h @@ -62,6 +62,7 @@ public: eServerPacketType_QEnvironment, eServerPacketType_QLaunchArch, eServerPacketType_QSetDisableASLR, + eServerPacketType_QSetDetachOnError, eServerPacketType_QSetSTDIN, eServerPacketType_QSetSTDOUT, eServerPacketType_QSetSTDERR, diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index e8fb114d2ba2..f2b1171acdad 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -203,6 +203,7 @@ RNBRemote::CreatePacketTable () t.push_back (Packet (set_enable_profiling, &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, "QSetEnableAsyncProfiling", "Enable or disable the profiling of current target.")); t.push_back (Packet (watchpoint_support_info, &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, "qWatchpointSupportInfo", "Return the number of supported hardware watchpoints")); t.push_back (Packet (set_process_event, &RNBRemote::HandlePacket_QSetProcessEvent, NULL, "QSetProcessEvent:", "Set a process event, to be passed to the process, can be set before the process is started, or after.")); + t.push_back (Packet (set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the process it is controlling if it loses connection to lldb.")); t.push_back (Packet (speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:", "Test the maximum speed at which packet can be sent/received.")); } @@ -2017,6 +2018,24 @@ RNBRemote::HandlePacket_QSyncThreadState (const char *p) return SendPacket ("E61"); } +rnb_err_t +RNBRemote::HandlePacket_QSetDetachOnError (const char *p) +{ + p += sizeof ("QSetDetachOnError:") - 1; + bool should_detach = true; + switch (*p) + { + case '0': should_detach = false; break; + case '1': should_detach = true; break; + default: + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid value for QSetDetachOnError - should be 0 or 1"); + break; + } + + m_ctx.SetDetachOnError(should_detach); + return SendPacket ("OK"); +} + rnb_err_t RNBRemote::HandlePacket_QListThreadsInStopReply (const char *p) { diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h index fb2ec1278c3e..1622ba1e5921 100644 --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -126,6 +126,7 @@ public: save_register_state, // '_g' restore_register_state, // '_G' speed_test, // 'qSpeedTest:' + set_detach_on_error, // 'QSetDetachOnError:' unknown_type } PacketEnum; @@ -234,6 +235,7 @@ public: rnb_err_t HandlePacket_WatchpointSupportInfo (const char *p); rnb_err_t HandlePacket_qSpeedTest (const char *p); rnb_err_t HandlePacket_stop_process (const char *p); + rnb_err_t HandlePacket_QSetDetachOnError (const char *p); rnb_err_t SendStopReplyPacketForThread (nub_thread_t tid); rnb_err_t SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer);