[PATCH v4 34/44] gdb, gdbserver: library notifications
Markus Metzger <[email protected]>
| Newsgroups | gmane.comp.gdb.patches |
|---|---|
| Message-ID | <[email protected]> |
Support asynchronous library notifications for non-stop remote targets.
Libraries for GPU targets are loaded onto the device by the host process.
The IntelGT remote target models devices as separate inferiors. This
allows debugging only the device code of a process or only a particular
device.
To learn about changes in loaded device libraries, the target receives
events from the Level-Zero debug API. Such events do not bind to a stop
on the device (there may not even be any threads dispatched).
To ensure that GDB had the chance to insert breakpoint into newly loaded
libraries, the host thread that loaded a library will be blocked inside a
system call until the library load is acknowledged by GDB.
Add a new notification
%Library:<pid>
to asynchronously indicate changes in loaded libraries. This is similar
to the 'library' stop reason.
In the traditional flow, the thread that loaded or unloaded a library
would hit a magic breakpoint and gdbserver would report the stop to GDB
with the library stop reason. GDB would then query the list of libraries
of the current process, insert breakpoints that bind into newly loaded
libraries, and, when it is done, resume the thread.
In this new flow, gdbserver would receive a library event and notify GDB.
GDB would then query the list of libraries for the process indicated by
the notification, insert breakpoints that bind into newly loaded
libraries, and, when it is done, acknowledge the notification.
Since libraries are changed from the outside, new libraries may be added
and gdbserver may receive new library load events. To ensure that we
acknowledge a library only when it has been processed by GDB, we hide
newly added libraries until we send a %Library notification. When GDB
acknowledges the notification, we know which libraries GDB has seen.
A remote target needs to choose whether it wants to use library stop
reasons or library notifications.
---
gdb/NEWS | 4 ++
gdb/doc/gdb.texinfo | 8 ++++
gdb/remote-notif.c | 1 +
gdb/remote-notif.h | 2 +
gdb/remote.c | 92 +++++++++++++++++++++++++++++++++++++++
gdbserver/dll.cc | 46 ++++++++++++++++++++
gdbserver/dll.h | 21 +++++++++
gdbserver/notif.cc | 4 ++
gdbserver/notif.h | 4 ++
gdbserver/remote-utils.cc | 3 +-
gdbserver/server.cc | 56 +++++++++++++++++++++++-
gdbserver/server.h | 3 ++
gdbserver/target.cc | 18 ++++++++
gdbserver/target.h | 34 +++++++++++++++
14 files changed, 294 insertions(+), 2 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 879f8e8865d..e6ed63128ec 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -18,6 +18,10 @@ qXfer:libraries:read's response
The qXfer:libraries:read query supports reporting in-memory libraries.
Older GDB will silently ignore them.
+%Library
+ This notification informs about asynchronous changes in the loaded
+ libraries of a process.
+
*** Changes in GDB 18
* Support for the Common Trace Format (CTF) has been removed. GDB now
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4cc755a0999..b36b7d01b23 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -47592,6 +47592,14 @@ for information on how these notifications are acknowledged by
@value{GDBN}.
@tab Report an asynchronous stop event in non-stop mode.
+@item Library
+@tab vLibrary
+@tab @var{pid}. The process for which libraries have changed.
+@tab Report asynchronous changes in the loaded libraries.
+@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
+list of loaded libraries and acknowledge the notification when the
+changes have been processed.
+
@end multitable
@node Remote Non-Stop
diff --git a/gdb/remote-notif.c b/gdb/remote-notif.c
index 4884761f3ad..7fd9ad80a3d 100644
--- a/gdb/remote-notif.c
+++ b/gdb/remote-notif.c
@@ -48,6 +48,7 @@ bool notif_debug = false;
static const notif_client *const notifs[] =
{
¬if_client_stop,
+ ¬if_client_library,
};
static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST);
diff --git a/gdb/remote-notif.h b/gdb/remote-notif.h
index cec7db8025b..85b49eee245 100644
--- a/gdb/remote-notif.h
+++ b/gdb/remote-notif.h
@@ -41,6 +41,7 @@ using notif_event_up = std::unique_ptr<notif_event>;
enum REMOTE_NOTIF_ID
{
REMOTE_NOTIF_STOP = 0,
+ REMOTE_NOTIF_LIBRARY,
REMOTE_NOTIF_LAST,
};
@@ -128,6 +129,7 @@ void remote_notif_process (struct remote_notif_state *state,
remote_notif_state *remote_notif_state_allocate (remote_target *remote);
extern const notif_client notif_client_stop;
+extern const notif_client notif_client_library;
extern bool notif_debug;
diff --git a/gdb/remote.c b/gdb/remote.c
index 10bceb2c798..20447fb5480 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -108,7 +108,9 @@ struct protocol_feature;
struct packet_reg;
struct stop_reply;
+struct library_reply;
using stop_reply_up = std::unique_ptr<stop_reply>;
+using library_reply_up = std::unique_ptr<library_reply>;
/* Generic configuration support for packets the stub optionally
supports. Allows the user to specify the use of the packet as well
@@ -1641,6 +1643,17 @@ struct stop_reply : public notif_event
int core;
};
+struct library_reply : public notif_event
+{
+ /* The identifier of the process about this event. */
+ int pid;
+
+ /* The remote state this event is associated with. When the remote
+ connection, represented by a remote_state object, is closed,
+ all the associated library_reply events should be released. */
+ struct remote_state *rs;
+};
+
/* Return TARGET as a remote_target if it is one, else nullptr. */
static remote_target *
@@ -5312,6 +5325,16 @@ as_stop_reply_up (notif_event_up event)
return stop_reply_up (stop_reply);
}
+/* Transfer ownership of the library_reply owned by EVENT to a
+ library_reply_up object. */
+
+static library_reply_up
+as_library_reply_up (notif_event_up event)
+{
+ library_reply *reply = static_cast<library_reply *> (event.release ());
+ return library_reply_up (reply);
+}
+
/* Read, decode, and return a hex-encoded string from *PTR. Update *PTR to
point at the first character past the end of the string that was read
in. */
@@ -8161,6 +8184,75 @@ const notif_client notif_client_stop =
REMOTE_NOTIF_STOP,
};
+static void
+remote_notif_library_parse (remote_target *remote,
+ const notif_client *self, const char *buf,
+ notif_event *event)
+{
+ library_reply *reply = (library_reply *) event;
+
+ ULONGEST pid;
+ const char *endp = unpack_varlen_hex (buf, &pid);
+ if (endp == buf)
+ error (_("No process id in Library notification: %s."), buf);
+ if (endp[0] != 0)
+ error (_("Trailing junk in Library notification: %s."), buf);
+
+ reply->pid = (int) pid;
+ if (((ULONGEST) reply->pid) != pid)
+ error (_("Bad Library notification process id: %s."), buf);
+}
+
+static void
+remote_notif_library_ack (remote_target *remote,
+ const notif_client *self, const char *buf,
+ notif_event_up event)
+{
+ library_reply_up reply = as_library_reply_up (std::move (event));
+
+ scoped_restore_current_thread restore_thread;
+ inferior *process = find_inferior_pid (remote, reply->pid);
+ if (process == nullptr)
+ {
+ process = remote->remote_add_inferior (false, reply->pid, -1, 1);
+ process->needs_setup = 1;
+ }
+
+ switch_to_inferior_no_thread (process);
+ if (process->needs_setup)
+ setup_inferior (0);
+
+ handle_solib_event ();
+
+ putpkt (remote, self->ack_command);
+}
+
+static int
+remote_notif_library_can_get_pending_events (remote_target *remote,
+ const notif_client *self)
+{
+ return 1;
+}
+
+static notif_event_up
+remote_notif_library_alloc_reply ()
+{
+ return notif_event_up (new library_reply ());
+}
+
+/* A client of notification Library. */
+
+const notif_client notif_client_library =
+{
+ "Library",
+ "vLibrary",
+ remote_notif_library_parse,
+ remote_notif_library_ack,
+ remote_notif_library_can_get_pending_events,
+ remote_notif_library_alloc_reply,
+ REMOTE_NOTIF_LIBRARY,
+};
+
/* If CONTEXT contains any fork/vfork/clone child threads that have
not been reported yet, remove them from the CONTEXT list. If such
a thread exists it is because we are stopped at a fork/vfork/clone
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index 32d6dfb21a7..c2accb1ab7e 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -154,3 +154,49 @@ unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
return false;
});
}
+
+/* See dll.h. */
+
+void
+notify_dlls (process_info *proc)
+{
+ if (proc == nullptr)
+ return;
+
+ for (dll_info &dll : proc->all_dlls)
+ dll.hidden = false;
+}
+
+/* Acknowledge DLL in PROC. */
+
+static void
+ack_dll (process_info *proc, const dll_info &dll)
+{
+ switch (dll.location)
+ {
+ case dll_info::on_disk:
+ target_ack_library (proc, dll.name.c_str ());
+ return;
+
+ case dll_info::in_memory:
+ target_ack_in_memory_library (proc, dll.begin, dll.end);
+ return;
+ }
+
+ gdb_assert_not_reached ("unknown dll location: %x", dll.location);
+}
+
+/* See dll.h. */
+
+void
+ack_dlls (process_info *proc)
+{
+ if (proc == nullptr)
+ return;
+
+ for (dll_info &dll : proc->all_dlls)
+ {
+ if (!dll.hidden)
+ ack_dll (proc, dll);
+ }
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index ab8ffdc1d8c..6a54ce3ece3 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -54,6 +54,21 @@ struct dll_info
/* The base address at which the library is loaded. */
CORE_ADDR base_addr;
+
+ /* Whether to tell GDB about this library.
+
+ We use this when library notifications are used to track which
+ libraries belong to the current library event, so when GDB
+ acknowledges the event, we know which libraries GDB has acknowledged.
+
+ New libraries that were added in the meantime need to wait for the
+ next library event.
+
+ For targets that do not use library notifications, this will be
+ ignored and GDB will always get the full list of libraries. This
+ means that library annotations in stop replies cannot be mixed with
+ library notifications. */
+ bool hidden = true;
};
extern void loaded_dll (const char *name, CORE_ADDR base_addr);
@@ -67,4 +82,10 @@ extern void unloaded_dll (process_info *proc, const char *name,
extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
CORE_ADDR base_addr);
+/* Clear the hidden flag for all libraries in PROC. */
+extern void notify_dlls (process_info *proc);
+
+/* Acknowledge all non-hidden libraries. */
+extern void ack_dlls (process_info *proc);
+
#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/notif.cc b/gdbserver/notif.cc
index 1745a96a051..c5649173eb6 100644
--- a/gdbserver/notif.cc
+++ b/gdbserver/notif.cc
@@ -52,6 +52,7 @@
static struct notif_server *notifs[] =
{
¬if_stop,
+ ¬if_library,
};
/* Write another event or an OK, if there are no more left, to
@@ -104,6 +105,9 @@ handle_notif_ack (char *own_buf, int packet_len)
remote_debug_printf ("%s: acking %d", np->ack_name,
(int) np->queue.size ());
+ if (np->ack != nullptr)
+ np->ack (head);
+
delete head;
}
diff --git a/gdbserver/notif.h b/gdbserver/notif.h
index 18b507dce5b..49805b0048f 100644
--- a/gdbserver/notif.h
+++ b/gdbserver/notif.h
@@ -54,10 +54,14 @@ struct notif_server
/* Write event EVENT to OWN_BUF. */
void (*write) (struct notif_event *event, char *own_buf);
+
+ /* EVENT has been acknowledged by GDB. */
+ void (*ack) (struct notif_event *event);
};
using notif_server_p = struct notif_server *;
extern struct notif_server notif_stop;
+extern struct notif_server notif_library;
int handle_notif_ack (char *own_buf, int packet_len);
void notif_write_event (struct notif_server *notif, char *own_buf);
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index ffde4956776..ffd7d3b0fd5 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1239,7 +1239,8 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
}
}
- if (current_process ()->dlls_changed)
+ if (current_process ()->dlls_changed
+ && !target_uses_library_notifications ())
{
strcpy (buf, "library:;");
buf += strlen (buf);
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 67ef448780f..880c077286d 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -272,9 +272,60 @@ in_queued_stop_replies (ptid_t ptid)
struct notif_server notif_stop =
{
- "vStopped", "Stop", {}, vstop_notif_reply,
+ "vStopped", "Stop", {}, vstop_notif_reply, nullptr,
};
+/* Library notifications. */
+
+struct vlibrary_notif : public notif_event
+{
+ vlibrary_notif (ptid_t::pid_type pid_) : pid (pid_)
+ {}
+
+ /* The process that got the event. */
+ int pid;
+};
+
+static void
+vlibrary_notif_reply (struct notif_event *event, char *own_buf)
+{
+ gdb_assert (target_uses_library_notifications ());
+
+ vlibrary_notif *notif = (vlibrary_notif *) event;
+ int pid = notif->pid;
+
+ process_info *process = find_process_pid (pid);
+ notify_dlls (process);
+
+ if (pid < 0)
+ sprintf (own_buf, "-%x", pid);
+ else
+ sprintf (own_buf, "%x", pid);
+}
+
+static void
+vlibrary_notif_ack (struct notif_event *event)
+{
+ vlibrary_notif *notif = (vlibrary_notif *) event;
+ process_info *process = find_process_pid (notif->pid);
+ ack_dlls (process);
+}
+
+struct notif_server notif_library =
+{
+ "vLibrary", "Library", {}, vlibrary_notif_reply, vlibrary_notif_ack,
+};
+
+void
+push_notif_library (process_info *process)
+{
+ if (!process->dlls_changed)
+ return;
+
+ notif_push (¬if_library, new vlibrary_notif (process->pid));
+ process->dlls_changed = false;
+}
+
static int
target_running (void)
{
@@ -1906,6 +1957,9 @@ handle_qxfer_features (const char *annex,
static std::string
print_qxfer_libraries_entry (const dll_info &dll)
{
+ if (dll.hidden && target_uses_library_notifications ())
+ return "";
+
switch (dll.location)
{
case dll_info::in_memory:
diff --git a/gdbserver/server.h b/gdbserver/server.h
index bbba3650de7..23ac219138e 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -92,6 +92,9 @@ extern void discard_queued_stop_replies (ptid_t ptid);
the vStopped notifications queue. */
extern int in_queued_stop_replies (ptid_t ptid);
+/* Push a notification to GDB for library changes of PROCESS. */
+void push_notif_library (process_info *process);
+
#include "remote-utils.h"
#include "utils.h"
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 5f0d9000254..3c569c8c66c 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -875,3 +875,21 @@ process_stratum_target::get_ipa_tdesc_idx ()
{
return 0;
}
+
+bool
+process_stratum_target::uses_library_notifications ()
+{
+ return false;
+}
+
+void
+process_stratum_target::ack_library (process_info *process, const char *name)
+{
+}
+
+void
+process_stratum_target::ack_in_memory_library (process_info *process,
+ CORE_ADDR begin,
+ CORE_ADDR end)
+{
+}
diff --git a/gdbserver/target.h b/gdbserver/target.h
index e38570dabb2..685bdb8d113 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -535,6 +535,21 @@ class process_stratum_target
Returns true if successful and false otherwise. */
virtual bool store_memtags (CORE_ADDR address, size_t len,
const gdb::byte_vector &tags, int type);
+
+ /* Whether the target uses library notifications instead of library
+ clauses in stop replies. */
+ virtual bool uses_library_notifications ();
+
+ /* Acknowledge a library reported by name.
+
+ This if only relevant if using library notifications. */
+ virtual void ack_library (process_info *process, const char *name);
+
+ /* Acknowledge an in-memory library reported by address.
+
+ This if only relevant if using library notifications. */
+ virtual void ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end);
};
extern process_stratum_target *the_target;
@@ -732,6 +747,25 @@ target_thread_pending_child (thread_info *thread, target_waitkind *kind)
return the_target->thread_pending_child (thread, kind);
}
+static inline bool
+target_uses_library_notifications ()
+{
+ return the_target->uses_library_notifications ();
+}
+
+static inline void
+target_ack_library (process_info *process, const char *name)
+{
+ the_target->ack_library (process, name);
+}
+
+static inline void
+target_ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end)
+{
+ the_target->ack_in_memory_library (process, begin, end);
+}
+
/* Read LEN bytes from MEMADDR in the buffer MYADDR. Return 0 if the read
is successful, otherwise, return a non-zero error code. */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.