mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 01:53:38 +08:00
btrace, python: Enable ptwrite filter registration.
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>
This commit is contained in:
parent
77a33bb024
commit
6be9971c93
@ -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;
|
||||
|
11
gdb/btrace.h
11
gdb/btrace.h
@ -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. */
|
||||
|
@ -481,6 +481,9 @@
|
||||
/* 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
|
||||
|
||||
|
11
gdb/configure
vendored
11
gdb/configure
vendored
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
||||
|
77
gdb/python/lib/gdb/ptwrite.py
Normal file
77
gdb/python/lib/gdb/ptwrite.py
Normal file
@ -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]
|
@ -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 *
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -312,6 +312,9 @@
|
||||
/* 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
|
||||
|
||||
|
11
gdbserver/configure
vendored
11
gdbserver/configure
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -256,6 +256,9 @@
|
||||
/* 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
|
||||
|
||||
|
11
gdbsupport/configure
vendored
11
gdbsupport/configure
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user