]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
btrace, python: Enable ptwrite filter registration.
authorFelix Willgerodt <felix.willgerodt@intel.com>
Tue, 15 May 2018 13:42:24 +0000 (15:42 +0200)
committerFelix Willgerodt <felix.willgerodt@intel.com>
Wed, 14 Aug 2024 09:20:57 +0000 (11:20 +0200)
By default GDB will be printing the hex payload of the ptwrite package as
auxiliary information.  To customize this, the user can register a ptwrite
filter function in python, that takes the payload and the PC as arguments and
returns a string which will be printed instead.  Registering the filter
function is done using a factory pattern to make per-thread filtering easier.

Approved-By: Markus Metzger <markus.t.metzger@intel.com>
18 files changed:
gdb/btrace.c
gdb/btrace.h
gdb/config.in
gdb/configure
gdb/data-directory/Makefile.in
gdb/extension-priv.h
gdb/extension.c
gdb/extension.h
gdb/guile/guile.c
gdb/python/lib/gdb/ptwrite.py [new file with mode: 0644]
gdb/python/py-record-btrace.c
gdb/python/python-internal.h
gdb/python/python.c
gdbserver/config.in
gdbserver/configure
gdbsupport/common.m4
gdbsupport/config.in
gdbsupport/configure

index d6188c84ad49d1af70077b2591218f37fb4389af..d493fbec65a6065e64a8c8ca9c9b82f40df054d3 100644 (file)
@@ -32,6 +32,7 @@
 #include "gdbsupport/rsp-low.h"
 #include "cli/cli-cmds.h"
 #include "cli/cli-utils.h"
+#include "extension.h"
 #include "gdbarch.h"
 
 /* For maintenance commands.  */
@@ -1316,6 +1317,9 @@ ftrace_add_pt (struct btrace_thread_info *btinfo,
   uint64_t offset;
   int status;
 
+  /* Register the ptwrite filter.  */
+  apply_ext_lang_ptwrite_filter (btinfo);
+
   for (;;)
     {
       struct pt_insn insn;
index 0f9317302dda04c56ab07deda9d45938784f0884..34f8d2be5ce4bbcb696ee288b560be8f63299da0 100644 (file)
@@ -35,6 +35,7 @@
 #endif
 
 #include <vector>
+#include <string>
 
 struct thread_info;
 struct btrace_function;
@@ -352,6 +353,16 @@ struct btrace_thread_info
      displaying or stepping through the execution history.  */
   std::vector<std::string> aux_data;
 
+  /* Function pointer to the ptwrite callback.  Returns the string returned
+     by the ptwrite filter function.  */
+  std::optional<std::string> (*ptw_callback_fun) (const uint64_t payload,
+                                                 const uint64_t ip,
+                                                 const void *ptw_context)
+                                                   = nullptr;
+
+  /* Context for the ptw_callback_fun.  */
+  void *ptw_context = nullptr;
+
   /* The function level offset.  When added to each function's LEVEL,
      this normalizes the function levels such that the smallest level
      becomes zero.  */
index 0c144c8918bd009ab435a74b30d2392f4a43af52..57be033802e33194a9bf4fe871d7a0aa3371c9db 100644 (file)
 /* Define to 1 if `pl_tdname' is a member of `struct ptrace_lwpinfo'. */
 #undef HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME
 
+/* Define to 1 if `variant.ptwrite' is a member of `struct pt_event'. */
+#undef HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE
+
 /* Define to 1 if `enabled' is a member of `struct pt_insn'. */
 #undef HAVE_STRUCT_PT_INSN_ENABLED
 
index 673dd6d90f68b764454d28db7dc347b22ee5f5f0..53eaad4f0e28fbbf642427c97857126abc4162eb 100755 (executable)
@@ -21510,6 +21510,17 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+fi
+
+      ac_fn_c_check_member "$LINENO" "struct pt_event" "variant.ptwrite" "ac_cv_member_struct_pt_event_variant_ptwrite" "#include <intel-pt.h>
+"
+if test "x$ac_cv_member_struct_pt_event_variant_ptwrite" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE 1
+_ACEOF
+
+
 fi
 
       LIBS=$save_LIBS
index f529656ca054a6acac2c9dafcdfde61de78c42b7..f9472f49ee9654ff9a791d76534cabfc62ecc591 100644 (file)
@@ -79,6 +79,7 @@ PYTHON_FILE_LIST = \
        gdb/missing_debug.py \
        gdb/printing.py \
        gdb/prompt.py \
+       gdb/ptwrite.py \
        gdb/styling.py \
        gdb/types.py \
        gdb/unwinder.py \
index 1365988b40ddb850b7e9dcd684fc4ae7f19416c3..653fd51e2f11ec0053a2d3ebf55b58d3053e45f6 100644 (file)
@@ -188,6 +188,11 @@ 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 filter to the current thread.  */
+  void (*apply_ptwrite_filter)
+    (const struct extension_language_defn *extlang,
+     struct btrace_thread_info *btinfo);
+
   /* Update values held by the extension language when OBJFILE is discarded.
      New global types must be created for every such value, which must then be
      updated to use the new types.
index 99e7190d80b67a7996869d9d61181d7a79f86ce0..c488fc774941e09d3edbc7b00bf627a8998798bf 100644 (file)
@@ -562,6 +562,19 @@ apply_ext_lang_frame_filter (const frame_info_ptr &frame,
   return EXT_LANG_BT_NO_FILTERS;
 }
 
+/* Used for registering the ptwrite filter to the current thread.  */
+
+void
+apply_ext_lang_ptwrite_filter (btrace_thread_info *btinfo)
+{
+  for (const struct extension_language_defn *extlang : extension_languages)
+    {
+      if (extlang->ops != nullptr
+         && extlang->ops->apply_ptwrite_filter != nullptr)
+       extlang->ops->apply_ptwrite_filter (extlang, btinfo);
+    }
+}
+
 /* Update values held by the extension language when OBJFILE is discarded.
    New global types must be created for every such value, which must then be
    updated to use the new types.
index 258a77dbfccb9a421626c27cff828faab78afdce..5b0830b667510275fa04cae95462e832f3dde878 100644 (file)
@@ -303,6 +303,9 @@ extern enum ext_lang_bt_status apply_ext_lang_frame_filter
    enum ext_lang_frame_args args_type,
    struct ui_out *out, int frame_low, int frame_high);
 
+extern void apply_ext_lang_ptwrite_filter
+  (struct btrace_thread_info *btinfo);
+
 extern void preserve_ext_lang_values (struct objfile *, htab_t copied_types);
 
 extern const struct extension_language_defn *get_breakpoint_cond_ext_lang
index dbbb96edfa8518853ad1afe3b10c18b2df57227f..432093b6aea28acc9113b011784ee5278a8cca70 100644 (file)
@@ -124,6 +124,7 @@ static const struct extension_language_ops guile_extension_ops =
   gdbscm_apply_val_pretty_printer,
 
   NULL, /* gdbscm_apply_frame_filter, */
+  NULL, /* gdbscm_load_ptwrite_filter, */
 
   gdbscm_preserve_values,
 
diff --git a/gdb/python/lib/gdb/ptwrite.py b/gdb/python/lib/gdb/ptwrite.py
new file mode 100644 (file)
index 0000000..3be65fe
--- /dev/null
@@ -0,0 +1,77 @@
+# Ptwrite utilities.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Utilities for working with ptwrite filters."""
+
+import gdb
+
+# _ptwrite_filter contains the per thread copies of the filter function.
+# The keys are tuples of inferior id and thread id.
+# The filter functions are created for each thread by calling the
+# _ptwrite_filter_factory.
+_ptwrite_filter = {}
+_ptwrite_filter_factory = None
+
+
+def _ptwrite_exit_handler(event):
+    """Exit handler to prune _ptwrite_filter on thread exit."""
+    _ptwrite_filter.pop(event.inferior_thread.ptid, None)
+
+
+gdb.events.thread_exited.connect(_ptwrite_exit_handler)
+
+
+def _clear_traces():
+    """Helper function to clear the trace of all threads."""
+    current_thread = gdb.selected_thread()
+
+    for inferior in gdb.inferiors():
+        for thread in inferior.threads():
+            thread.switch()
+            recording = gdb.current_recording()
+            if recording is not None:
+                recording.clear()
+
+    current_thread.switch()
+
+
+def register_filter_factory(filter_factory_):
+    """Register the ptwrite filter factory."""
+    if filter_factory_ is not None and not callable(filter_factory_):
+        raise TypeError("The filter factory must be callable or 'None'.")
+
+    # Clear the traces of all threads of all inferiors to force
+    # re-decoding with the new filter.
+    _clear_traces()
+
+    _ptwrite_filter.clear()
+    global _ptwrite_filter_factory
+    _ptwrite_filter_factory = filter_factory_
+
+
+def get_filter():
+    """Returns the filter of the current thread."""
+    thread = gdb.selected_thread()
+    key = thread.ptid
+
+    # Create a new filter for new threads.
+    if key not in _ptwrite_filter:
+        if _ptwrite_filter_factory is not None:
+            _ptwrite_filter[key] = _ptwrite_filter_factory(thread)
+        else:
+            return None
+
+    return _ptwrite_filter[key]
index 49b6c741dcf74cc793d746e5ffadd823faf4fa79..68b474cc1b4db00d354243ed8ddd37dd086879b8 100644 (file)
@@ -808,6 +808,109 @@ recpy_bt_function_call_history (PyObject *self, void *closure)
   return btpy_list_new (tinfo, first, last, 1, &recpy_func_type);
 }
 
+/* Helper function that calls PTW_FILTER with PAYLOAD and IP as arguments.
+   Returns the string that will be printed, if there is a filter to call.  */
+static std::optional<std::string>
+recpy_call_filter (const uint64_t payload, const uint64_t ip,
+                  const void *ptw_filter)
+{
+  std::optional<std::string> result;
+
+  gdb_assert (ptw_filter != nullptr);
+  if ((PyObject *) ptw_filter == Py_None)
+    return result;
+
+  gdbpy_enter enter_py;
+
+  gdbpy_ref<> py_payload = gdb_py_object_from_ulongest (payload);
+
+  gdbpy_ref<> py_ip;
+  if (ip == 0)
+    py_ip = gdbpy_ref<>::new_reference (Py_None);
+  else
+    py_ip = gdb_py_object_from_ulongest (ip);
+
+  gdbpy_ref<> py_result (PyObject_CallFunctionObjArgs ((PyObject *) ptw_filter,
+                                                       py_payload.get (),
+                                                       py_ip.get (),
+                                                       nullptr));
+
+  if (py_result == nullptr)
+    {
+      gdbpy_print_stack ();
+      gdbpy_error (_("Couldn't call the ptwrite filter."));
+    }
+
+  /* Py_None is valid and results in no output.  */
+  if (py_result == Py_None)
+    {
+      result = "";
+      return result;
+    }
+
+  gdb::unique_xmalloc_ptr<char> user_string
+    = gdbpy_obj_to_string (py_result.get ());
+
+  if (user_string == nullptr)
+    {
+      gdbpy_print_stack ();
+      gdbpy_error (_("The ptwrite filter didn't return a string."));
+    }
+  else
+    result = user_string.get ();
+
+  return result;
+}
+
+/* Helper function returning the current ptwrite filter.  */
+
+static PyObject *
+get_ptwrite_filter ()
+{
+  gdbpy_ref<> module (PyImport_ImportModule ("gdb.ptwrite"));
+
+  if (PyErr_Occurred ())
+  {
+    gdbpy_print_stack ();
+    gdbpy_error (_("Couldn't import gdb.ptwrite."));
+  }
+
+  /* We need to keep the reference count.  */
+  gdbpy_ref<> ptw_filter (gdbpy_call_method (module.get (), "get_filter"));
+
+  if (PyErr_Occurred ())
+    {
+      gdbpy_print_stack ();
+      gdbpy_error (_("Couldn't get the ptwrite filter."));
+    }
+
+  return ptw_filter.get();
+}
+
+/* Used for registering any python ptwrite filter to the current thread.  A
+   pointer to this function is stored in the python extension interface.  */
+
+void
+gdbpy_load_ptwrite_filter (const struct extension_language_defn *extlang,
+                          struct btrace_thread_info *btinfo)
+{
+  gdb_assert (btinfo != nullptr);
+
+  gdbpy_enter enter_py;
+
+  btinfo->ptw_context = get_ptwrite_filter ();
+
+#if defined (HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE)
+  if (!btinfo->target->conf.pt.ptwrite && btinfo->ptw_context != Py_None)
+    warning (_("The target doesn't support decoding ptwrite events."));
+#else
+  if (btinfo->ptw_context != Py_None)
+    warning (_("Libipt doesn't support decoding ptwrite events."));
+#endif /* defined (HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE) */
+
+  btinfo->ptw_callback_fun = &recpy_call_filter;
+}
+
 /* Implementation of BtraceRecord.goto (self, BtraceInstruction) -> None.  */
 
 PyObject *
index 5db308821f266edba0c97a0458817a42ba8c9050..bf3ab67ea744dcc98dc5969c54518628de41a2a2 100644 (file)
@@ -464,6 +464,9 @@ extern enum ext_lang_rc gdbpy_apply_val_pretty_printer
    struct ui_file *stream, int recurse,
    const struct value_print_options *options,
    const struct language_defn *language);
+extern void gdbpy_load_ptwrite_filter
+  (const struct extension_language_defn *extlang,
+   struct btrace_thread_info *btinfo);
 extern enum ext_lang_bt_status gdbpy_apply_frame_filter
   (const struct extension_language_defn *,
    const frame_info_ptr &frame, frame_filter_flags flags,
index d86a031ee95dc56a7625d54ccd9f105dc78caedd..043c221b0d8ce2ec773b719baa71a134216ee5a2 100644 (file)
@@ -159,6 +159,8 @@ static const struct extension_language_ops python_extension_ops =
 
   gdbpy_apply_frame_filter,
 
+  gdbpy_load_ptwrite_filter,
+
   gdbpy_preserve_values,
 
   gdbpy_breakpoint_has_cond,
index 47e1e722fe0aa1ebbbdc91d0adae5bf3ebee1eb2..65f9ff6e6470369ce8487e0ea3bdb186f323f7b1 100644 (file)
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if `variant.ptwrite' is a member of `struct pt_event'. */
+#undef HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE
+
 /* Define to 1 if `enabled' is a member of `struct pt_insn'. */
 #undef HAVE_STRUCT_PT_INSN_ENABLED
 
index 8d38b957e4159f67bf41897c57abb9c88e94ce2c..09cb3c5bf434b564da794a636a0ff7148cbb6695 100755 (executable)
@@ -10217,6 +10217,17 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+fi
+
+      ac_fn_c_check_member "$LINENO" "struct pt_event" "variant.ptwrite" "ac_cv_member_struct_pt_event_variant_ptwrite" "#include <intel-pt.h>
+"
+if test "x$ac_cv_member_struct_pt_event_variant_ptwrite" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE 1
+_ACEOF
+
+
 fi
 
       LIBS=$save_LIBS
index 6c317ff2450387fe43c070fe983db241acaa38cd..d89c3aed0fe1e76a7fb11b59855e437ef97353f0 100644 (file)
@@ -200,6 +200,8 @@ AC_DEFUN([GDB_AC_COMMON], [
       AC_CHECK_FUNCS(pt_insn_event)
       AC_CHECK_MEMBERS([struct pt_insn.enabled, struct pt_insn.resynced], [], [],
                       [#include <intel-pt.h>])
+      AC_CHECK_MEMBERS([struct pt_event.variant.ptwrite], [], [],
+                      [#include <intel-pt.h>])
       LIBS=$save_LIBS
     fi
   fi
index 832c92b2cb102c0b467bc2479a9378ee588f774c..8467072752a6648c68091eefa830e68bb18f1255 100644 (file)
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if `variant.ptwrite' is a member of `struct pt_event'. */
+#undef HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE
+
 /* Define to 1 if `enabled' is a member of `struct pt_insn'. */
 #undef HAVE_STRUCT_PT_INSN_ENABLED
 
index 54e32bbe2cdd2deb931e9a75d49ed7842d7fca2d..02408472771029493366e89ef305028cc65b791a 100755 (executable)
@@ -12991,6 +12991,17 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+fi
+
+      ac_fn_c_check_member "$LINENO" "struct pt_event" "variant.ptwrite" "ac_cv_member_struct_pt_event_variant_ptwrite" "#include <intel-pt.h>
+"
+if test "x$ac_cv_member_struct_pt_event_variant_ptwrite" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE 1
+_ACEOF
+
+
 fi
 
       LIBS=$save_LIBS