diff options
Diffstat (limited to 'gdbserver')
-rw-r--r-- | gdbserver/gdbthread.h | 3 | ||||
-rw-r--r-- | gdbserver/server.cc | 130 | ||||
-rw-r--r-- | gdbserver/target.cc | 6 | ||||
-rw-r--r-- | gdbserver/target.h | 6 |
4 files changed, 145 insertions, 0 deletions
diff --git a/gdbserver/gdbthread.h b/gdbserver/gdbthread.h index 493e1db..a4dff0f 100644 --- a/gdbserver/gdbthread.h +++ b/gdbserver/gdbthread.h @@ -80,6 +80,9 @@ struct thread_info /* Branch trace target information for this thread. */ struct btrace_target_info *btrace = nullptr; + + /* Thread options GDB requested with QThreadOptions. */ + gdb_thread_options thread_options = 0; }; extern std::list<thread_info *> all_threads; diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 38f084b..c24a5c9 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -36,6 +36,7 @@ #include "dll.h" #include "hostio.h" #include <vector> +#include <unordered_map> #include "gdbsupport/common-inferior.h" #include "gdbsupport/job-control.h" #include "gdbsupport/environ.h" @@ -616,6 +617,17 @@ parse_store_memtags_request (char *request, CORE_ADDR *addr, size_t *len, return true; } +/* Parse thread options starting at *P and return them. On exit, + advance *P past the options. */ + +static gdb_thread_options +parse_gdb_thread_options (const char **p) +{ + ULONGEST options = 0; + *p = unpack_varlen_hex (*p, &options); + return (gdb_thread_option) options; +} + /* Handle all of the extended 'Q' packets. */ static void @@ -897,6 +909,114 @@ handle_general_set (char *own_buf) return; } + if (startswith (own_buf, "QThreadOptions;")) + { + const char *p = own_buf + strlen ("QThreadOptions"); + + gdb_thread_options supported_options = target_supported_thread_options (); + if (supported_options == 0) + { + /* Something went wrong -- we don't support any option, but + GDB sent the packet anyway. */ + write_enn (own_buf); + return; + } + + /* We could store the options directly in thread->thread_options + without this map, but that would mean that a QThreadOptions + packet with a wildcard like "QThreadOptions;0;3:TID" would + result in the debug logs showing: + + [options for TID are now 0x0] + [options for TID are now 0x3] + + It's nicer if we only print the final options for each TID, + and if we only print about it if the options changed compared + to the options that were previously set on the thread. */ + std::unordered_map<thread_info *, gdb_thread_options> set_options; + + while (*p != '\0') + { + if (p[0] != ';') + { + write_enn (own_buf); + return; + } + p++; + + /* Read the options. */ + + gdb_thread_options options = parse_gdb_thread_options (&p); + + if ((options & ~supported_options) != 0) + { + /* GDB asked for an unknown or unsupported option, so + error out. */ + std::string err + = string_printf ("E.Unknown thread options requested: %s\n", + to_string (options).c_str ()); + strcpy (own_buf, err.c_str ()); + return; + } + + ptid_t ptid; + + if (p[0] == ';' || p[0] == '\0') + ptid = minus_one_ptid; + else if (p[0] == ':') + { + const char *q; + + ptid = read_ptid (p + 1, &q); + + if (p == q) + { + write_enn (own_buf); + return; + } + p = q; + if (p[0] != ';' && p[0] != '\0') + { + write_enn (own_buf); + return; + } + } + else + { + write_enn (own_buf); + return; + } + + /* Convert PID.-1 => PID.0 for ptid.matches. */ + if (ptid.lwp () == -1) + ptid = ptid_t (ptid.pid ()); + + for_each_thread ([&] (thread_info *thread) + { + if (ptid_of (thread).matches (ptid)) + set_options[thread] = options; + }); + } + + for (const auto &iter : set_options) + { + thread_info *thread = iter.first; + gdb_thread_options options = iter.second; + + if (thread->thread_options != options) + { + threads_debug_printf ("[options for %s are now %s]\n", + target_pid_to_str (ptid_of (thread)).c_str (), + to_string (options).c_str ()); + + thread->thread_options = options; + } + } + + write_ok (own_buf); + return; + } + if (startswith (own_buf, "QStartupWithShell:")) { const char *value = own_buf + strlen ("QStartupWithShell:"); @@ -2348,6 +2468,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) cs.vCont_supported = 1; else if (feature == "QThreadEvents+") ; + else if (feature == "QThreadOptions+") + ; else if (feature == "no-resumed+") { /* GDB supports and wants TARGET_WAITKIND_NO_RESUMED @@ -2474,6 +2596,14 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) strcat (own_buf, ";vContSupported+"); + gdb_thread_options supported_options = target_supported_thread_options (); + if (supported_options != 0) + { + char *end_buf = own_buf + strlen (own_buf); + sprintf (end_buf, ";QThreadOptions=%s", + phex_nz (supported_options, sizeof (supported_options))); + } + strcat (own_buf, ";QThreadEvents+"); strcat (own_buf, ";no-resumed+"); diff --git a/gdbserver/target.cc b/gdbserver/target.cc index f8e592d..1c740bb 100644 --- a/gdbserver/target.cc +++ b/gdbserver/target.cc @@ -532,6 +532,12 @@ process_stratum_target::supports_vfork_events () return false; } +gdb_thread_options +process_stratum_target::supported_thread_options () +{ + return 0; +} + bool process_stratum_target::supports_exec_events () { diff --git a/gdbserver/target.h b/gdbserver/target.h index f13ee40..8893e0a 100644 --- a/gdbserver/target.h +++ b/gdbserver/target.h @@ -276,6 +276,9 @@ public: /* Returns true if vfork events are supported. */ virtual bool supports_vfork_events (); + /* Returns the set of supported thread options. */ + virtual gdb_thread_options supported_thread_options (); + /* Returns true if exec events are supported. */ virtual bool supports_exec_events (); @@ -531,6 +534,9 @@ int kill_inferior (process_info *proc); #define target_supports_vfork_events() \ the_target->supports_vfork_events () +#define target_supported_thread_options(options) \ + the_target->supported_thread_options (options) + #define target_supports_exec_events() \ the_target->supports_exec_events () |