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:
Felix Willgerodt 2018-05-15 15:42:24 +02:00
parent 77a33bb024
commit 6be9971c93
18 changed files with 267 additions and 0 deletions

View 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;

View 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. */

View File

@ -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
View File

@ -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

View 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 \

View 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.

View 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.

View 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

View 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,

View 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]

View 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 *

View 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,

View 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,

View File

@ -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
View File

@ -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

View 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

View File

@ -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
View File

@ -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