mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 01:03:07 +00:00
Allow naming and configuring order of type finders
Like for symbol finders, we want extra flexibility around configuring type finders. The type finder callback signature also has a couple of warts: it doesn't take the program since it was added before types needed to be constructed from a program, and it is called separately for each type kind since originally type lookups were for only one kind. While we're adding a new interface, let's fix these warts: pass the program and a set of type kinds. However, we need to keep the old add_type_finder() interface for backwards compatibility. Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
parent
9b73b44908
commit
5c9797a633
63
_drgn.pyi
63
_drgn.pyi
@ -437,6 +437,49 @@ class Program:
|
|||||||
another :ref:`buffer <python:binaryseq>` type.
|
another :ref:`buffer <python:binaryseq>` type.
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
def register_type_finder(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
fn: Callable[[Program, TypeKindSet, str, Optional[str]], Optional[Type]],
|
||||||
|
*,
|
||||||
|
enable_index: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Register a callback for finding types in the program.
|
||||||
|
|
||||||
|
This does not enable the finder unless *enable_index* is given.
|
||||||
|
|
||||||
|
:param name: Finder name.
|
||||||
|
:param fn: Callable taking the program, a :class:`TypeKindSet`, name,
|
||||||
|
and filename: ``(prog, kinds, name, filename)``. The filename
|
||||||
|
should be matched with :func:`filename_matches()`. This should
|
||||||
|
return a :class:`Type` or ``None`` if not found.
|
||||||
|
:param enable_index: Insert the finder into the list of enabled type
|
||||||
|
finders at the given index. If -1 or greater than the number of
|
||||||
|
enabled finders, insert it at the end. If ``None`` or not given,
|
||||||
|
don't enable the finder.
|
||||||
|
:raises ValueError: if there is already a finder with the given name
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
def registered_type_finders(self) -> Set[str]:
|
||||||
|
"""Return the names of all registered type finders."""
|
||||||
|
...
|
||||||
|
def set_enabled_type_finders(self, names: Sequence[str]) -> None:
|
||||||
|
"""
|
||||||
|
Set the list of enabled type finders.
|
||||||
|
|
||||||
|
Finders are called in the same order as the list until a type is found.
|
||||||
|
|
||||||
|
Finders that are not in the list are not called.
|
||||||
|
|
||||||
|
:param names: Names of finders to enable, in order.
|
||||||
|
:raises ValueError: if no finder has a given name or the same name is
|
||||||
|
given more than once
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
def enabled_type_finders(self) -> List[str]:
|
||||||
|
"""Return the names of enabled type finders, in order."""
|
||||||
|
...
|
||||||
def register_symbol_finder(
|
def register_symbol_finder(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
@ -499,16 +542,20 @@ class Program:
|
|||||||
self, fn: Callable[[TypeKind, str, Optional[str]], Optional[Type]]
|
self, fn: Callable[[TypeKind, str, Optional[str]], Optional[Type]]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Register a callback for finding types in the program.
|
Deprecated method to register and enable a callback for finding types
|
||||||
|
in the program.
|
||||||
|
|
||||||
Callbacks are called in reverse order of the order they were added
|
.. deprecated:: 0.0.27
|
||||||
until the type is found. So, more recently added callbacks take
|
Use :meth:`register_type_finder()` instead.
|
||||||
precedence.
|
|
||||||
|
|
||||||
:param fn: Callable taking a :class:`TypeKind`, name, and filename:
|
The differences from :meth:`register_type_finder()` are:
|
||||||
``(kind, name, filename)``. The filename should be matched with
|
|
||||||
:func:`filename_matches()`. This should return a :class:`Type`
|
1. *fn* is not passed *prog*.
|
||||||
or ``None`` if not found.
|
2. *fn* is passed a :class:`TypeKind` instead of a
|
||||||
|
:class:`TypeKindSet`. If multiple kinds are being searched for, *fn*
|
||||||
|
will be called multiple times.
|
||||||
|
3. A name for the finder is generated from *fn*.
|
||||||
|
4. The finder is always enabled before any existing finders.
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
def add_object_finder(
|
def add_object_finder(
|
||||||
|
@ -82,7 +82,7 @@ program "memory":
|
|||||||
print(drgn.Object(prog, 'struct btrfs_super_block', address=65536))
|
print(drgn.Object(prog, 'struct btrfs_super_block', address=65536))
|
||||||
run_interactive(prog, banner_func=lambda _: "BTRFS debugger")
|
run_interactive(prog, banner_func=lambda _: "BTRFS debugger")
|
||||||
|
|
||||||
:meth:`drgn.Program.add_type_finder()` and
|
:meth:`drgn.Program.register_type_finder()` and
|
||||||
:meth:`drgn.Program.add_object_finder()` are the equivalent methods for
|
:meth:`drgn.Program.add_object_finder()` are the equivalent methods for
|
||||||
plugging in types and objects.
|
plugging in types and objects.
|
||||||
|
|
||||||
|
@ -2181,8 +2181,12 @@ void drgn_debug_info_init(struct drgn_debug_info *dbinfo,
|
|||||||
// unlikely to fail anwyays, so don't bother propagating an error up.
|
// unlikely to fail anwyays, so don't bother propagating an error up.
|
||||||
if (!dbinfo->dwfl)
|
if (!dbinfo->dwfl)
|
||||||
abort();
|
abort();
|
||||||
drgn_program_add_type_finder_impl(prog, &dbinfo->type_finder,
|
const struct drgn_type_finder_ops type_finder_ops = {
|
||||||
drgn_debug_info_find_type, dbinfo);
|
.find = drgn_debug_info_find_type,
|
||||||
|
};
|
||||||
|
drgn_program_register_type_finder_impl(prog, &dbinfo->type_finder,
|
||||||
|
"dwarf", &type_finder_ops,
|
||||||
|
dbinfo, 0);
|
||||||
drgn_program_add_object_finder_impl(prog, &dbinfo->object_finder,
|
drgn_program_add_object_finder_impl(prog, &dbinfo->object_finder,
|
||||||
drgn_debug_info_find_object,
|
drgn_debug_info_find_object,
|
||||||
dbinfo);
|
dbinfo);
|
||||||
|
108
libdrgn/drgn.h
108
libdrgn/drgn.h
@ -587,41 +587,91 @@ enum {
|
|||||||
DRGN_HANDLER_REGISTER_DONT_ENABLE = SIZE_MAX - 1,
|
DRGN_HANDLER_REGISTER_DONT_ENABLE = SIZE_MAX - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/** Type finder callback table. */
|
||||||
* Callback for finding a type.
|
struct drgn_type_finder_ops {
|
||||||
*
|
/**
|
||||||
* @param[in] kinds Kinds of types to find, as a bitmask of bits shifted by @ref
|
* Callback to destroy the type finder.
|
||||||
* drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 << DRGN_TYPE_CLASS)`
|
*
|
||||||
* means to find a structure or class type.
|
* This may be @c NULL.
|
||||||
* @param[in] name Name of type (or tag, for structs, unions, and enums). This
|
*
|
||||||
* is @em not null-terminated.
|
* @param[in] arg Argument passed to @ref
|
||||||
* @param[in] name_len Length of @p name.
|
* drgn_program_register_type_finder().
|
||||||
* @param[in] filename Filename containing the type definition or @c NULL. This
|
*/
|
||||||
* should be matched with @ref drgn_filename_matches().
|
void (*destroy)(void *arg);
|
||||||
* @param[in] arg Argument passed to @ref drgn_program_add_type_finder().
|
/**
|
||||||
* @param[out] ret Returned type.
|
* Callback for finding a type.
|
||||||
* @return @c NULL on success, non-@c NULL on error. In particular, if the type
|
*
|
||||||
* is not found, this should return &@ref drgn_not_found; any other errors are
|
* @param[in] kinds Kinds of types to find, as a bitmask of bits shifted
|
||||||
* considered fatal.
|
* by @ref drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 <<
|
||||||
*/
|
* DRGN_TYPE_CLASS)` means to find a structure or class type.
|
||||||
typedef struct drgn_error *
|
* @param[in] name Name of type (or tag, for structs, unions, and
|
||||||
(*drgn_type_find_fn)(uint64_t kinds, const char *name, size_t name_len,
|
* enums). This is @em not null-terminated.
|
||||||
const char *filename, void *arg,
|
* @param[in] name_len Length of @p name.
|
||||||
struct drgn_qualified_type *ret);
|
* @param[in] filename Filename containing the type definition or @c
|
||||||
|
* NULL. This should be matched with @ref drgn_filename_matches().
|
||||||
|
* @param[in] arg Argument passed to @ref
|
||||||
|
* drgn_program_register_type_finder().
|
||||||
|
* @param[out] ret Returned type.
|
||||||
|
* @return @c NULL on success, non-@c NULL on error. In particular, if
|
||||||
|
* the type is not found, this should return &@ref drgn_not_found; any
|
||||||
|
* other errors are considered fatal.
|
||||||
|
*/
|
||||||
|
struct drgn_error *(*find)(uint64_t kinds, const char *name,
|
||||||
|
size_t name_len, const char *filename,
|
||||||
|
void *arg, struct drgn_qualified_type *ret);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a type finding callback.
|
* Register a type finding callback.
|
||||||
*
|
*
|
||||||
* Callbacks are called in reverse order of the order they were added until the
|
* @param[in] name Finder name. This is copied.
|
||||||
* type is found. So, more recently added callbacks take precedence.
|
* @param[in] ops Callback table. This is copied.
|
||||||
*
|
* @param[in] arg Argument to pass to callbacks.
|
||||||
* @param[in] fn The callback.
|
* @param[in] enable_index Insert the finder into the list of enabled finders at
|
||||||
* @param[in] arg Argument to pass to @p fn.
|
* the given index. If @ref DRGN_HANDLER_REGISTER_ENABLE_LAST or greater than
|
||||||
* @return @c NULL on success, non-@c NULL on error.
|
* the number of enabled finders, insert it at the end. If @ref
|
||||||
|
* DRGN_HANDLER_REGISTER_DONT_ENABLE, don’t enable the finder.
|
||||||
*/
|
*/
|
||||||
struct drgn_error *
|
struct drgn_error *
|
||||||
drgn_program_add_type_finder(struct drgn_program *prog, drgn_type_find_fn fn,
|
drgn_program_register_type_finder(struct drgn_program *prog, const char *name,
|
||||||
void *arg);
|
const struct drgn_type_finder_ops *ops,
|
||||||
|
void *arg, size_t enable_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the names of all registered type finders.
|
||||||
|
*
|
||||||
|
* The order of the names is arbitrary.
|
||||||
|
*
|
||||||
|
* @param[out] names_ret Returned array of names.
|
||||||
|
* @param[out] count_ret Returned number of names in @p names_ret.
|
||||||
|
*/
|
||||||
|
struct drgn_error *
|
||||||
|
drgn_program_registered_type_finders(struct drgn_program *prog,
|
||||||
|
const char ***names_ret,
|
||||||
|
size_t *count_ret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the list of enabled type finders.
|
||||||
|
*
|
||||||
|
* Finders are called in the same order as the list until a type is found.
|
||||||
|
*
|
||||||
|
* @param[in] names Names of finders to enable, in order.
|
||||||
|
* @param[in] count Number of names in @p names.
|
||||||
|
*/
|
||||||
|
struct drgn_error *
|
||||||
|
drgn_program_set_enabled_type_finders(struct drgn_program *prog,
|
||||||
|
const char * const *names,
|
||||||
|
size_t count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the names of enabled type finders, in order.
|
||||||
|
*
|
||||||
|
* @param[out] names_ret Returned array of names.
|
||||||
|
* @param[out] count_ret Returned number of names in @p names_ret.
|
||||||
|
*/
|
||||||
|
struct drgn_error *drgn_program_enabled_type_finders(struct drgn_program *prog,
|
||||||
|
const char ***names_ret,
|
||||||
|
size_t *count_ret);
|
||||||
|
|
||||||
/** Flags for @ref drgn_program_find_object(). */
|
/** Flags for @ref drgn_program_find_object(). */
|
||||||
enum drgn_find_object_flags {
|
enum drgn_find_object_flags {
|
||||||
|
@ -283,6 +283,7 @@ drgn_program_enabled_##which##_finders(struct drgn_program *prog, \
|
|||||||
count_ret); \
|
count_ret); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DRGN_PROGRAM_FINDER(type)
|
||||||
DRGN_PROGRAM_FINDER(symbol)
|
DRGN_PROGRAM_FINDER(symbol)
|
||||||
#undef DRGN_PROGRAM_FINDER
|
#undef DRGN_PROGRAM_FINDER
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "type.h"
|
#include "type.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
struct drgn_type_finder;
|
||||||
struct drgn_symbol_finder;
|
struct drgn_symbol_finder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,7 +83,7 @@ struct drgn_program {
|
|||||||
* Types.
|
* Types.
|
||||||
*/
|
*/
|
||||||
/** Callbacks for finding types. */
|
/** Callbacks for finding types. */
|
||||||
struct drgn_type_finder *type_finders;
|
struct drgn_handler_list type_finders;
|
||||||
/** Void type for each language. */
|
/** Void type for each language. */
|
||||||
struct drgn_type void_types[DRGN_NUM_LANGUAGES];
|
struct drgn_type void_types[DRGN_NUM_LANGUAGES];
|
||||||
/** Cache of primitive types. */
|
/** Cache of primitive types. */
|
||||||
@ -383,6 +384,13 @@ drgn_program_find_symbol_by_address_internal(struct drgn_program *prog,
|
|||||||
uint64_t address,
|
uint64_t address,
|
||||||
struct drgn_symbol **ret);
|
struct drgn_symbol **ret);
|
||||||
|
|
||||||
|
struct drgn_error *
|
||||||
|
drgn_program_register_type_finder_impl(struct drgn_program *prog,
|
||||||
|
struct drgn_type_finder *finder,
|
||||||
|
const char *name,
|
||||||
|
const struct drgn_type_finder_ops *ops,
|
||||||
|
void *arg, size_t enable_index);
|
||||||
|
|
||||||
struct drgn_error *
|
struct drgn_error *
|
||||||
drgn_program_register_symbol_finder_impl(struct drgn_program *prog,
|
drgn_program_register_symbol_finder_impl(struct drgn_program *prog,
|
||||||
struct drgn_symbol_finder *finder,
|
struct drgn_symbol_finder *finder,
|
||||||
|
@ -385,6 +385,29 @@ static PyObject *Program_add_memory_segment(Program *self, PyObject *args,
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct drgn_error *
|
||||||
|
py_type_find_fn_common(PyObject *type_obj, void *arg,
|
||||||
|
struct drgn_qualified_type *ret)
|
||||||
|
{
|
||||||
|
if (!PyObject_TypeCheck(type_obj, &DrgnType_type)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"type find callback must return Type or None");
|
||||||
|
return drgn_error_from_python();
|
||||||
|
}
|
||||||
|
// This check is also done in libdrgn, but we need it here because if
|
||||||
|
// the type isn't from this program, then there's no guarantee that it
|
||||||
|
// will remain valid after we decrement its reference count.
|
||||||
|
if (DrgnType_prog((DrgnType *)type_obj)
|
||||||
|
!= (Program *)PyTuple_GET_ITEM(arg, 0)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"type find callback returned type from wrong program");
|
||||||
|
return drgn_error_from_python();
|
||||||
|
}
|
||||||
|
ret->type = ((DrgnType *)type_obj)->type;
|
||||||
|
ret->qualifiers = ((DrgnType *)type_obj)->qualifiers;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
|
static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
|
||||||
size_t name_len, const char *filename,
|
size_t name_len, const char *filename,
|
||||||
void *arg,
|
void *arg,
|
||||||
@ -392,6 +415,33 @@ static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
|
|||||||
{
|
{
|
||||||
PyGILState_guard();
|
PyGILState_guard();
|
||||||
|
|
||||||
|
_cleanup_pydecref_ PyObject *name_obj =
|
||||||
|
PyUnicode_FromStringAndSize(name, name_len);
|
||||||
|
if (!name_obj)
|
||||||
|
return drgn_error_from_python();
|
||||||
|
|
||||||
|
_cleanup_pydecref_ PyObject *kinds_obj = TypeKindSet_wrap(kinds);
|
||||||
|
if (!kinds_obj)
|
||||||
|
return drgn_error_from_python();
|
||||||
|
_cleanup_pydecref_ PyObject *type_obj =
|
||||||
|
PyObject_CallFunction(PyTuple_GET_ITEM(arg, 1), "OOOs",
|
||||||
|
PyTuple_GET_ITEM(arg, 0), kinds_obj,
|
||||||
|
name_obj, filename);
|
||||||
|
if (!type_obj)
|
||||||
|
return drgn_error_from_python();
|
||||||
|
if (type_obj == Py_None)
|
||||||
|
return &drgn_not_found;
|
||||||
|
return py_type_find_fn_common(type_obj, arg, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old version for add_type_finder().
|
||||||
|
static struct drgn_error *py_type_find_fn_old(uint64_t kinds, const char *name,
|
||||||
|
size_t name_len,
|
||||||
|
const char *filename, void *arg,
|
||||||
|
struct drgn_qualified_type *ret)
|
||||||
|
{
|
||||||
|
PyGILState_guard();
|
||||||
|
|
||||||
_cleanup_pydecref_ PyObject *name_obj =
|
_cleanup_pydecref_ PyObject *name_obj =
|
||||||
PyUnicode_FromStringAndSize(name, name_len);
|
PyUnicode_FromStringAndSize(name, name_len);
|
||||||
if (!name_obj)
|
if (!name_obj)
|
||||||
@ -411,56 +461,11 @@ static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
|
|||||||
return drgn_error_from_python();
|
return drgn_error_from_python();
|
||||||
if (type_obj == Py_None)
|
if (type_obj == Py_None)
|
||||||
continue;
|
continue;
|
||||||
if (!PyObject_TypeCheck(type_obj, &DrgnType_type)) {
|
return py_type_find_fn_common(type_obj, arg, ret);
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"type find callback must return Type or None");
|
|
||||||
return drgn_error_from_python();
|
|
||||||
}
|
|
||||||
// This check is also done in libdrgn, but we need it here
|
|
||||||
// because if the type isn't from this program, then there's no
|
|
||||||
// guarantee that it will remain valid after we decrement its
|
|
||||||
// reference count.
|
|
||||||
if (DrgnType_prog((DrgnType *)type_obj)
|
|
||||||
!= (Program *)PyTuple_GET_ITEM(arg, 0)) {
|
|
||||||
PyErr_SetString(PyExc_ValueError,
|
|
||||||
"type find callback returned type from wrong program");
|
|
||||||
return drgn_error_from_python();
|
|
||||||
}
|
|
||||||
ret->type = ((DrgnType *)type_obj)->type;
|
|
||||||
ret->qualifiers = ((DrgnType *)type_obj)->qualifiers;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
return &drgn_not_found;
|
return &drgn_not_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *Program_add_type_finder(Program *self, PyObject *args,
|
|
||||||
PyObject *kwds)
|
|
||||||
{
|
|
||||||
struct drgn_error *err;
|
|
||||||
|
|
||||||
static char *keywords[] = {"fn", NULL};
|
|
||||||
PyObject *fn;
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:add_type_finder",
|
|
||||||
keywords, &fn))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!PyCallable_Check(fn)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "fn must be callable");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn);
|
|
||||||
if (!arg)
|
|
||||||
return NULL;
|
|
||||||
if (Program_hold_object(self, arg))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = drgn_program_add_type_finder(&self->prog, py_type_find_fn, arg);
|
|
||||||
if (err)
|
|
||||||
return set_drgn_error(err);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct drgn_error *py_object_find_fn(const char *name, size_t name_len,
|
static struct drgn_error *py_object_find_fn(const char *name, size_t name_len,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
enum drgn_find_object_flags flags,
|
enum drgn_find_object_flags flags,
|
||||||
@ -560,10 +565,11 @@ py_symbol_find_fn(const char *name, uint64_t addr,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define symbol_finder_arg(self, fn) \
|
#define type_finder_arg(self, fn) \
|
||||||
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); \
|
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); \
|
||||||
if (!arg) \
|
if (!arg) \
|
||||||
return NULL;
|
return NULL;
|
||||||
|
#define symbol_finder_arg type_finder_arg
|
||||||
|
|
||||||
#define DEFINE_PROGRAM_FINDER_METHODS(which) \
|
#define DEFINE_PROGRAM_FINDER_METHODS(which) \
|
||||||
static PyObject *Program_register_##which##_finder(Program *self, \
|
static PyObject *Program_register_##which##_finder(Program *self, \
|
||||||
@ -705,8 +711,62 @@ static PyObject *Program_enabled_##which##_finders(Program *self) \
|
|||||||
return_ptr(res); \
|
return_ptr(res); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_PROGRAM_FINDER_METHODS(type)
|
||||||
DEFINE_PROGRAM_FINDER_METHODS(symbol)
|
DEFINE_PROGRAM_FINDER_METHODS(symbol)
|
||||||
|
|
||||||
|
static PyObject *deprecated_finder_name_obj(PyObject *fn)
|
||||||
|
{
|
||||||
|
_cleanup_pydecref_ PyObject *name_attr_obj =
|
||||||
|
PyObject_GetAttrString(fn, "__name__");
|
||||||
|
if (name_attr_obj) {
|
||||||
|
return PyUnicode_FromFormat("%S_%lu", name_attr_obj, random());
|
||||||
|
} else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return PyUnicode_FromFormat("%lu", random());
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *Program_add_type_finder(Program *self, PyObject *args,
|
||||||
|
PyObject *kwds)
|
||||||
|
{
|
||||||
|
struct drgn_error *err;
|
||||||
|
static char *keywords[] = {"fn", NULL};
|
||||||
|
PyObject *fn;
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:add_type_finder",
|
||||||
|
keywords, &fn))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!PyCallable_Check(fn)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "fn must be callable");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn);
|
||||||
|
if (!arg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
_cleanup_pydecref_ PyObject *name_obj = deprecated_finder_name_obj(fn);
|
||||||
|
if (!name_obj)
|
||||||
|
return NULL;
|
||||||
|
const char *name = PyUnicode_AsUTF8(name_obj);
|
||||||
|
if (!name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!Program_hold_reserve(self, 1))
|
||||||
|
return NULL;
|
||||||
|
const struct drgn_type_finder_ops ops = {
|
||||||
|
.find = py_type_find_fn_old,
|
||||||
|
};
|
||||||
|
err = drgn_program_register_type_finder(&self->prog, name, &ops, arg,
|
||||||
|
0);
|
||||||
|
if (err)
|
||||||
|
return set_drgn_error(err);
|
||||||
|
Program_hold_object(self, arg);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *Program_add_object_finder(Program *self, PyObject *args,
|
static PyObject *Program_add_object_finder(Program *self, PyObject *args,
|
||||||
PyObject *kwds)
|
PyObject *kwds)
|
||||||
{
|
{
|
||||||
@ -1396,6 +1456,7 @@ static int Program_set_language(Program *self, PyObject *value, void *arg)
|
|||||||
static PyMethodDef Program_methods[] = {
|
static PyMethodDef Program_methods[] = {
|
||||||
{"add_memory_segment", (PyCFunction)Program_add_memory_segment,
|
{"add_memory_segment", (PyCFunction)Program_add_memory_segment,
|
||||||
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_memory_segment_DOC},
|
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_memory_segment_DOC},
|
||||||
|
PROGRAM_FINDER_METHOD_DEFS(type),
|
||||||
PROGRAM_FINDER_METHOD_DEFS(symbol),
|
PROGRAM_FINDER_METHOD_DEFS(symbol),
|
||||||
{"add_type_finder", (PyCFunction)Program_add_type_finder,
|
{"add_type_finder", (PyCFunction)Program_add_type_finder,
|
||||||
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_type_finder_DOC},
|
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_type_finder_DOC},
|
||||||
|
@ -1310,40 +1310,11 @@ void drgn_program_deinit_types(struct drgn_program *prog)
|
|||||||
free(*it.entry);
|
free(*it.entry);
|
||||||
drgn_dedupe_type_set_deinit(&prog->dedupe_types);
|
drgn_dedupe_type_set_deinit(&prog->dedupe_types);
|
||||||
|
|
||||||
struct drgn_type_finder *finder = prog->type_finders;
|
drgn_handler_list_deinit(struct drgn_type_finder, finder,
|
||||||
while (finder) {
|
&prog->type_finders,
|
||||||
struct drgn_type_finder *next = finder->next;
|
if (finder->ops.destroy)
|
||||||
if (finder->free)
|
finder->ops.destroy(finder->arg);
|
||||||
free(finder);
|
);
|
||||||
finder = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct drgn_error *
|
|
||||||
drgn_program_add_type_finder_impl(struct drgn_program *prog,
|
|
||||||
struct drgn_type_finder *finder,
|
|
||||||
drgn_type_find_fn fn, void *arg)
|
|
||||||
{
|
|
||||||
if (finder) {
|
|
||||||
finder->free = false;
|
|
||||||
} else {
|
|
||||||
finder = malloc(sizeof(*finder));
|
|
||||||
if (!finder)
|
|
||||||
return &drgn_enomem;
|
|
||||||
finder->free = true;
|
|
||||||
}
|
|
||||||
finder->fn = fn;
|
|
||||||
finder->arg = arg;
|
|
||||||
finder->next = prog->type_finders;
|
|
||||||
prog->type_finders = finder;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
LIBDRGN_PUBLIC struct drgn_error *
|
|
||||||
drgn_program_add_type_finder(struct drgn_program *prog, drgn_type_find_fn fn,
|
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
return drgn_program_add_type_finder_impl(prog, NULL, fn, arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
|
struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
|
||||||
@ -1352,11 +1323,11 @@ struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
|
|||||||
const char *filename,
|
const char *filename,
|
||||||
struct drgn_qualified_type *ret)
|
struct drgn_qualified_type *ret)
|
||||||
{
|
{
|
||||||
struct drgn_type_finder *finder = prog->type_finders;
|
drgn_handler_list_for_each_enabled(struct drgn_type_finder, finder,
|
||||||
while (finder) {
|
&prog->type_finders) {
|
||||||
struct drgn_error *err =
|
struct drgn_error *err =
|
||||||
finder->fn(kinds, name, name_len, filename, finder->arg,
|
finder->ops.find(kinds, name, name_len, filename,
|
||||||
ret);
|
finder->arg, ret);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
if (drgn_type_program(ret->type) != prog) {
|
if (drgn_type_program(ret->type) != prog) {
|
||||||
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
|
||||||
@ -1370,7 +1341,6 @@ struct drgn_error *drgn_program_find_type_impl(struct drgn_program *prog,
|
|||||||
}
|
}
|
||||||
if (err != &drgn_not_found)
|
if (err != &drgn_not_found)
|
||||||
return err;
|
return err;
|
||||||
finder = finder->next;
|
|
||||||
}
|
}
|
||||||
return &drgn_not_found;
|
return &drgn_not_found;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "drgn.h"
|
#include "drgn.h"
|
||||||
|
#include "handler.h"
|
||||||
#include "hash_table.h"
|
#include "hash_table.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
@ -50,21 +51,11 @@ drgn_byte_order_from_little_endian(bool little_endian)
|
|||||||
|
|
||||||
/** Registered type finding callback in a @ref drgn_program. */
|
/** Registered type finding callback in a @ref drgn_program. */
|
||||||
struct drgn_type_finder {
|
struct drgn_type_finder {
|
||||||
/** The callback. */
|
struct drgn_handler handler;
|
||||||
drgn_type_find_fn fn;
|
struct drgn_type_finder_ops ops;
|
||||||
/** Argument to pass to @ref drgn_type_finder::fn. */
|
|
||||||
void *arg;
|
void *arg;
|
||||||
/** Next callback to try. */
|
|
||||||
struct drgn_type_finder *next;
|
|
||||||
/** Whether this structure needs to be freed. */
|
|
||||||
bool free;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct drgn_error *
|
|
||||||
drgn_program_add_type_finder_impl(struct drgn_program *prog,
|
|
||||||
struct drgn_type_finder *finder,
|
|
||||||
drgn_type_find_fn fn, void *arg);
|
|
||||||
|
|
||||||
DEFINE_HASH_SET_TYPE(drgn_dedupe_type_set, struct drgn_type *);
|
DEFINE_HASH_SET_TYPE(drgn_dedupe_type_set, struct drgn_type *);
|
||||||
|
|
||||||
/** <tt>(type, member name)</tt> pair. */
|
/** <tt>(type, member name)</tt> pair. */
|
||||||
|
@ -68,11 +68,11 @@ class MockObject(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=None):
|
def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=None):
|
||||||
def mock_find_type(kind, name, filename):
|
def mock_find_type(prog, kinds, name, filename):
|
||||||
if filename:
|
if filename:
|
||||||
return None
|
return None
|
||||||
for type in types:
|
for type in types:
|
||||||
if type.kind == kind:
|
if type.kind in kinds:
|
||||||
try:
|
try:
|
||||||
type_name = type.name
|
type_name = type.name
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -105,7 +105,7 @@ def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=N
|
|||||||
if segments is not None:
|
if segments is not None:
|
||||||
add_mock_memory_segments(prog, segments)
|
add_mock_memory_segments(prog, segments)
|
||||||
if types is not None:
|
if types is not None:
|
||||||
prog.add_type_finder(mock_find_type)
|
prog.register_type_finder("mock", mock_find_type, enable_index=0)
|
||||||
if objects is not None:
|
if objects is not None:
|
||||||
prog.add_object_finder(mock_object_find)
|
prog.add_object_finder(mock_object_find)
|
||||||
return prog
|
return prog
|
||||||
|
@ -404,14 +404,73 @@ class TestMemory(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestTypes(MockProgramTestCase):
|
class TestTypeFinder(TestCase):
|
||||||
def test_invalid_finder(self):
|
def test_register(self):
|
||||||
self.assertRaises(TypeError, self.prog.add_type_finder, "foo")
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
|
||||||
self.prog.add_type_finder(lambda kind, name, filename: "foo")
|
# We don't test every corner case because the symbol finder tests cover
|
||||||
self.assertRaises(TypeError, self.prog.type, "int")
|
# the shared part.
|
||||||
|
self.assertEqual(prog.registered_type_finders(), {"dwarf"})
|
||||||
|
self.assertEqual(prog.enabled_type_finders(), ["dwarf"])
|
||||||
|
|
||||||
def test_finder_different_program(self):
|
prog.register_type_finder(
|
||||||
|
"foo", lambda prog, kinds, name, filename: None, enable_index=-1
|
||||||
|
)
|
||||||
|
self.assertEqual(prog.registered_type_finders(), {"dwarf", "foo"})
|
||||||
|
self.assertEqual(prog.enabled_type_finders(), ["dwarf", "foo"])
|
||||||
|
|
||||||
|
prog.set_enabled_type_finders(["foo"])
|
||||||
|
self.assertEqual(prog.registered_type_finders(), {"dwarf", "foo"})
|
||||||
|
self.assertEqual(prog.enabled_type_finders(), ["foo"])
|
||||||
|
|
||||||
|
def test_add_type_finder(self):
|
||||||
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
|
||||||
|
def dummy(kind, name, filename):
|
||||||
|
if kind == TypeKind.TYPEDEF and name == "foo":
|
||||||
|
return prog.typedef_type("foo", prog.void_type())
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
prog.add_type_finder(dummy)
|
||||||
|
self.assertTrue(any("dummy" in name for name in prog.registered_type_finders()))
|
||||||
|
self.assertIn("dummy", prog.enabled_type_finders()[0])
|
||||||
|
self.assertIdentical(
|
||||||
|
prog.type("foo"), prog.typedef_type("foo", prog.void_type())
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_register_invalid(self):
|
||||||
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
self.assertRaises(TypeError, prog.register_type_finder, "foo", "foo")
|
||||||
|
prog.register_type_finder(
|
||||||
|
"foo", lambda prog, kinds, name, filename: "foo", enable_index=0
|
||||||
|
)
|
||||||
|
self.assertRaises(TypeError, prog.type, "int")
|
||||||
|
|
||||||
|
def test_add_invalid(self):
|
||||||
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
self.assertRaises(TypeError, prog.add_type_finder, "foo")
|
||||||
|
prog.add_type_finder(lambda kind, name, filename: "foo")
|
||||||
|
self.assertRaises(TypeError, prog.type, "int")
|
||||||
|
|
||||||
|
def test_register_wrong_program(self):
|
||||||
|
def finder(prog, kinds, name, filename):
|
||||||
|
if TypeKind.TYPEDEF in kinds and name == "foo":
|
||||||
|
prog = Program()
|
||||||
|
return prog.typedef_type("foo", prog.void_type())
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
prog.register_type_finder("foo", finder, enable_index=0)
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
ValueError,
|
||||||
|
"type find callback returned type from wrong program",
|
||||||
|
prog.type,
|
||||||
|
"foo",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_add_wrong_program(self):
|
||||||
def finder(kind, name, filename):
|
def finder(kind, name, filename):
|
||||||
if kind == TypeKind.TYPEDEF and name == "foo":
|
if kind == TypeKind.TYPEDEF and name == "foo":
|
||||||
prog = Program()
|
prog = Program()
|
||||||
@ -419,23 +478,41 @@ class TestTypes(MockProgramTestCase):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.prog.add_type_finder(finder)
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
prog.add_type_finder(finder)
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
ValueError,
|
ValueError,
|
||||||
"type find callback returned type from wrong program",
|
"type find callback returned type from wrong program",
|
||||||
self.prog.type,
|
prog.type,
|
||||||
"foo",
|
"foo",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_wrong_kind(self):
|
def test_register_wrong_kind(self):
|
||||||
self.prog.add_type_finder(lambda kind, name, filename: self.prog.void_type())
|
prog = Program(MOCK_PLATFORM)
|
||||||
self.assertRaises(TypeError, self.prog.type, "int")
|
prog.register_type_finder(
|
||||||
|
"foo", lambda prog, kinds, name, filename: prog.void_type(), enable_index=0
|
||||||
|
)
|
||||||
|
self.assertRaises(TypeError, prog.type, "int")
|
||||||
|
|
||||||
def test_not_found(self):
|
def test_add_wrong_kind(self):
|
||||||
self.assertRaises(LookupError, self.prog.type, "struct foo")
|
prog = Program(MOCK_PLATFORM)
|
||||||
self.prog.add_type_finder(lambda kind, name, filename: None)
|
prog.add_type_finder(lambda kind, name, filename: prog.void_type())
|
||||||
self.assertRaises(LookupError, self.prog.type, "struct foo")
|
self.assertRaises(TypeError, prog.type, "int")
|
||||||
|
|
||||||
|
def test_register_not_found(self):
|
||||||
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
prog.register_type_finder(
|
||||||
|
"foo", lambda prog, kinds, name, filename: None, enable_index=0
|
||||||
|
)
|
||||||
|
self.assertRaises(LookupError, prog.type, "struct foo")
|
||||||
|
|
||||||
|
def test_add_not_found(self):
|
||||||
|
prog = Program(MOCK_PLATFORM)
|
||||||
|
prog.add_type_finder(lambda kind, name, filename: None)
|
||||||
|
self.assertRaises(LookupError, prog.type, "struct foo")
|
||||||
|
|
||||||
|
|
||||||
|
class TestTypes(MockProgramTestCase):
|
||||||
def test_already_type(self):
|
def test_already_type(self):
|
||||||
self.assertIdentical(
|
self.assertIdentical(
|
||||||
self.prog.type(self.prog.pointer_type(self.prog.void_type())),
|
self.prog.type(self.prog.pointer_type(self.prog.void_type())),
|
||||||
|
Loading…
Reference in New Issue
Block a user