[PATCH v3 09/12] btrace, python: Enable ptwrite listener registration.

Willgerodt, Felix felix.willgerodt@intel.com
Fri May 6 11:26:33 GMT 2022


> -----Original Message-----
> From: Metzger, Markus T <markus.t.metzger@intel.com>
> Sent: Freitag, 13. August 2021 12:36
> To: Willgerodt, Felix <felix.willgerodt@intel.com>
> Cc: gdb-patches@sourceware.org
> Subject: RE: [PATCH v3 09/12] btrace, python: Enable ptwrite listener
> registration.
> 
> Thanks, Felix,
> 
> 
> >+  apply_ext_lang_ptwrite_listener (inferior_ptid);
> 
> Should this be called ptwrite_filter?
> 

No, that wasn't my intention. I can rename "listener" to "filter" if you
want, but that would affect the whole series and documentation.
Or are you just requesting the extension functions to be updated?

> 
> >+  /* PyObject pointer to the ptwrite listener function.  */
> >+  void *ptw_listener = nullptr;
> >+
> 
> The comment says that this is a PyObject.  Why is it declared void *?
>

Because I didn't want to include a python header for PyObject here.
Afaik, btrace.h should be kept free of those.

> 
> >diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
> >index 77f23e0f911..36da4211738 100644
> >--- a/gdb/extension-priv.h
> >+++ b/gdb/extension-priv.h
> >@@ -183,6 +183,10 @@ struct extension_language_ops
> >      enum ext_lang_frame_args args_type,
> >      struct ui_out *out, int frame_low, int frame_high);
> >
> >+  /* Used for registering the ptwrite listener to the current thread.  */
> >+  enum ext_lang_bt_status (*apply_ptwrite_listener)
> >+    (const struct extension_language_defn *, ptid_t inferior_ptid);
> 
> There is a parameter name for the second but not for the first parameter.
> Also, the name shadows a global variable.  Let's call it just 'ptid'.
> 

The first parameter never seems to be named in this file. Apart from one
occasion. That said, I think it might be only for formatting reasons, and we have
enough space here. So I added a name. I also changed it to ptid.

> 
> >+/* Used for registering the ptwrite listener to the current thread.  */
> >+
> >+enum ext_lang_bt_status
> 
> This enum is specifically for frame filters.  We'd need to either generalize it or
> add our own for ptwrite filters.
> 

Good catch. Since we are not checking it anyway and allow nullptr/None
as a valid listener I decided to make this return void.

> 
> >+apply_ext_lang_ptwrite_listener (ptid_t inferior_ptid)
> >+{
> >+  for (const struct extension_language_defn *extlang :
> extension_languages)
> >+    {
> >+      enum ext_lang_bt_status status;
> >+
> >+      if (extlang->ops == nullptr
> >+	  || extlang->ops->apply_ptwrite_listener == NULL)
> >+	continue;
> >+      status = extlang->ops->apply_ptwrite_listener (extlang, inferior_ptid);
> >+
> >+      if (status != EXT_LANG_BT_NO_FILTERS)
> >+	return status;
> 
> I would have inserted the empty line after the 'continue' from the if
> statement since obtaining the status and checking it belong together.
>

Done
 
> IIUC, this installs the ptwrite filter.  The code that actually calls it on a
> PTWRITE event is not part of this patch.  I think the installation of the filter
> and calling it should be inside one patch to make it clear how this works.
> 

Done.

> 
> >+# Ptwrite utilities.
> >+# Copyright (C) 2020 Free Software Foundation, Inc.
> 
> Year.
> 

Done

> 
> >+def default_listener(payload, ip):
> >+    """Default listener that is active upon starting GDB."""
> >+    return "{:x}".format(payload)
> >+
> >+# This dict contains the per thread copies of the listener function and the
> >+# global template listener, from which the copies are created.
> >+_ptwrite_listener = {"global" : default_listener}
> >+
> >+
> >+def _update_listener_dict(thread_list):
> >+    """Helper function to update the listener dict.
> >+
> >+    Discards listener copies of threads that already exited and registers
> >+    copies of the listener for new threads."""
> >+    # thread_list[x].ptid returns the tuple (pid, lwp, tid)
> >+    lwp_list = [i.ptid[1] for i in thread_list]
> >+
> >+    # clean-up old listeners
> >+    for key in _ptwrite_listener.keys():
> >+      if key not in lwp_list and key != "global":
> >+        _ptwrite_listener.pop(key)
> >+
> >+    # Register listener for new threads
> >+    for key in lwp_list:
> >+        if key not in _ptwrite_listener.keys():
> >+            _ptwrite_listener[key] = deepcopy(_ptwrite_listener["global"])
> 
> So we can only update all at once?  In non-stop mode, I think we want to be
> able to update just the one for the current thread.
> 
> What's the purpose of this deepcopy?  For the default filter, this doesn't
> seem necessary.  For user-defined filters, this is initializing the per-thread
> filter from a template, correct?  Should 'global' be spelled 'template' in that
> case?
> 
> Should the template filter be part of the dict, at all?
> 

We discussed this offline last year. To summarize:
We deepcopy to allow every thread to keep it's own internal state. We also
discussed the architecture repeatedly. We decided to update every thread
if we update the listener. I had a version some years ago that allowed to
specify which threads to update, but we decided against that.

I didn't change anything.

> 
> >+def _clear_traces(thread_list):
> >+    """Helper function to clear the trace of all threads."""
> 
> The comment doesn’t match the code.  The function clears the recordings of
> threads in THREAD_LIST.
> 

I updated the comment.


> 
> >+    current_thread = gdb.selected_thread()
> >+
> >+    recording = gdb.current_recording()
> >+
> >+    if (recording is not None):
> >+        for thread in thread_list:
> >+            thread.switch()
> >+            recording.clear()
> >+
> >+        current_thread.switch()
> 
> Should there be a try around the above to ensure we switch back in case of
> exceptions?
>

I am not sure. I don't think clear() can really throw. If switch throws, it
might be because of the thread_list being wrong. But we got the list from
gdb itself just before this call. It might also be that there is a problem with
switch(), but in that case trying to switch again seems dangerous.
 
> 
> >+def register_listener(listener):
> >+    """Register the ptwrite listener function."""
> >+    if listener is not None and not callable(listener):
> >+        raise TypeError("Listener must be callable!")
> >+
> >+    thread_list = gdb.Inferior.threads(gdb.selected_inferior())
> >+    _clear_traces(thread_list)
> 
> Please add a comment as to why we need to clear the recordings for all
> threads?
> 

Done.

> 
> >+def get_listener():
> >+    """Returns the listeners of the current thread."""
> 
> The comment says 'listeners' but there can be only one, correct?
> 
> >+    thread_list = gdb.Inferior.threads(gdb.selected_inferior())
> >+    _update_listener_dict(thread_list)
> 
> Why do we need to update the filters for all threads?
> 

Because we are registering a new copy of the new listener for every
thread.  An earlier (internal) version that I had allowed to register
different listeners per thread, but we decided against that.
This is needed to enable per-thread internal states, even if the user
can only register one listener globally.

> 
> >+/* Helper function returning the current ptwrite listener.  Returns nullptr
> >+   in case of errors.  */
> >+
> >+PyObject *
> >+get_ptwrite_listener ()
> >+{
> >+  PyObject *module = PyImport_ImportModule ("gdb.ptwrite");
> >+
> >+  if (PyErr_Occurred ())
> >+  {
> >+    gdbpy_print_stack ();
> >+    return nullptr;
> >+  }
> >+
> >+  PyObject *default_ptw_listener = PyObject_CallMethod (module,
> >	"get_listener",
> >+							NULL);
> >+
> >+  gdb_Py_DECREF (module);
> >+
> >+  if (PyErr_Occurred ())
> >+    gdbpy_print_stack ();
> >+
> >+  return default_ptw_listener;
> 
> This is not necessarily the default listener, correct?
>

Correct, I renamed it.


> 
> >+/* Helper function to initialize the ptwrite listener.  Returns nullptr in
> >+   case of errors.  */
> >+
> >+PyObject *
> >+recpy_initialize_listener (ptid_t inferior_ptid)
> 
> The argument name shadows a global variable.  Let's just call it 'ptid'.
>

I rewrote this a bit so this is no longer an issue.
 
> 
> >+  process_stratum_target *proc_target = current_inferior ()-
> >process_target ();
> 
> Any chance we can get here without having a current inferior?
>

I rewrote this to pass btinfo from btrace.c. That made this function
redundant.
 
> 
> >+  struct thread_info * const tinfo = find_thread_ptid (proc_target,
> inferior_ptid);
> >+
> >+  tinfo->btrace.ptw_listener = get_ptwrite_listener ();
> 
> Let's check tinfo.  Or pass it in as parameter.
>

See above, I rewrote this. In the new version I pass and check btinfo directly.

> 
> >+  return (PyObject *) tinfo->btrace.ptw_listener;
> 
> It is called only once and that call ignores the result.
>

I rewrote this. Since we allow nullptr/None to silence the listener I decided
to make everything return void.
 
> 
> >+/* Used for registering the default ptwrite listener to the current thread.
> A
> >+   pointer to this function is stored in the python extension interface.  */
> >+
> >+enum ext_lang_bt_status
> >+gdbpy_load_ptwrite_listener (const struct extension_language_defn
> *extlang,
> >+			     ptid_t inferior_ptid)
> >+{
> >+  struct gdbarch *gdbarch = NULL;
> >+
> >+  if (!gdb_python_initialized)
> >+    return EXT_LANG_BT_NO_FILTERS;
> >+
> >+  try
> >+    {
> >+      gdbarch = target_gdbarch ();
> 
> Is this a precaution or did you run into cases where target_gdbarch() throws?
> Should the rest also be done inside a try block?
>

I blindly copied this from py-framefilter.c:gdbpy_apply_frame_filter() years ago.
But looking at git and some recent changes to enter_py I don't think any of this
is necessary anymore. I rewrote this.

 
> 
> Regards,
> Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


More information about the Gdb-patches mailing list