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.
|
||||
"""
|
||||
...
|
||||
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(
|
||||
self,
|
||||
name: str,
|
||||
@ -499,16 +542,20 @@ class Program:
|
||||
self, fn: Callable[[TypeKind, str, Optional[str]], Optional[Type]]
|
||||
) -> 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
|
||||
until the type is found. So, more recently added callbacks take
|
||||
precedence.
|
||||
.. deprecated:: 0.0.27
|
||||
Use :meth:`register_type_finder()` instead.
|
||||
|
||||
:param fn: Callable taking a :class:`TypeKind`, name, and filename:
|
||||
``(kind, name, filename)``. The filename should be matched with
|
||||
:func:`filename_matches()`. This should return a :class:`Type`
|
||||
or ``None`` if not found.
|
||||
The differences from :meth:`register_type_finder()` are:
|
||||
|
||||
1. *fn* is not passed *prog*.
|
||||
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(
|
||||
|
@ -82,7 +82,7 @@ program "memory":
|
||||
print(drgn.Object(prog, 'struct btrfs_super_block', address=65536))
|
||||
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
|
||||
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.
|
||||
if (!dbinfo->dwfl)
|
||||
abort();
|
||||
drgn_program_add_type_finder_impl(prog, &dbinfo->type_finder,
|
||||
drgn_debug_info_find_type, dbinfo);
|
||||
const struct drgn_type_finder_ops type_finder_ops = {
|
||||
.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_debug_info_find_object,
|
||||
dbinfo);
|
||||
|
108
libdrgn/drgn.h
108
libdrgn/drgn.h
@ -587,41 +587,91 @@ enum {
|
||||
DRGN_HANDLER_REGISTER_DONT_ENABLE = SIZE_MAX - 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for finding a type.
|
||||
*
|
||||
* @param[in] kinds Kinds of types to find, as a bitmask of bits shifted by @ref
|
||||
* drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 << DRGN_TYPE_CLASS)`
|
||||
* means to find a structure or class type.
|
||||
* @param[in] name Name of type (or tag, for structs, unions, and enums). This
|
||||
* is @em not null-terminated.
|
||||
* @param[in] name_len Length of @p name.
|
||||
* @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_add_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.
|
||||
*/
|
||||
typedef struct drgn_error *
|
||||
(*drgn_type_find_fn)(uint64_t kinds, const char *name, size_t name_len,
|
||||
const char *filename, void *arg,
|
||||
struct drgn_qualified_type *ret);
|
||||
/** Type finder callback table. */
|
||||
struct drgn_type_finder_ops {
|
||||
/**
|
||||
* Callback to destroy the type finder.
|
||||
*
|
||||
* This may be @c NULL.
|
||||
*
|
||||
* @param[in] arg Argument passed to @ref
|
||||
* drgn_program_register_type_finder().
|
||||
*/
|
||||
void (*destroy)(void *arg);
|
||||
/**
|
||||
* Callback for finding a type.
|
||||
*
|
||||
* @param[in] kinds Kinds of types to find, as a bitmask of bits shifted
|
||||
* by @ref drgn_type_kind. E.g., `(1 << DRGN_TYPE_STRUCT) | (1 <<
|
||||
* DRGN_TYPE_CLASS)` means to find a structure or class type.
|
||||
* @param[in] name Name of type (or tag, for structs, unions, and
|
||||
* enums). This is @em not null-terminated.
|
||||
* @param[in] name_len Length of @p name.
|
||||
* @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.
|
||||
*
|
||||
* Callbacks are called in reverse order of the order they were added until the
|
||||
* type is found. So, more recently added callbacks take precedence.
|
||||
*
|
||||
* @param[in] fn The callback.
|
||||
* @param[in] arg Argument to pass to @p fn.
|
||||
* @return @c NULL on success, non-@c NULL on error.
|
||||
* @param[in] name Finder name. This is copied.
|
||||
* @param[in] ops Callback table. This is copied.
|
||||
* @param[in] arg Argument to pass to callbacks.
|
||||
* @param[in] enable_index Insert the finder into the list of enabled finders at
|
||||
* the given index. If @ref DRGN_HANDLER_REGISTER_ENABLE_LAST or greater than
|
||||
* 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 *
|
||||
drgn_program_add_type_finder(struct drgn_program *prog, drgn_type_find_fn fn,
|
||||
void *arg);
|
||||
drgn_program_register_type_finder(struct drgn_program *prog, const char *name,
|
||||
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(). */
|
||||
enum drgn_find_object_flags {
|
||||
|
@ -283,6 +283,7 @@ drgn_program_enabled_##which##_finders(struct drgn_program *prog, \
|
||||
count_ret); \
|
||||
}
|
||||
|
||||
DRGN_PROGRAM_FINDER(type)
|
||||
DRGN_PROGRAM_FINDER(symbol)
|
||||
#undef DRGN_PROGRAM_FINDER
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "type.h"
|
||||
#include "vector.h"
|
||||
|
||||
struct drgn_type_finder;
|
||||
struct drgn_symbol_finder;
|
||||
|
||||
/**
|
||||
@ -82,7 +83,7 @@ struct drgn_program {
|
||||
* Types.
|
||||
*/
|
||||
/** Callbacks for finding types. */
|
||||
struct drgn_type_finder *type_finders;
|
||||
struct drgn_handler_list type_finders;
|
||||
/** Void type for each language. */
|
||||
struct drgn_type void_types[DRGN_NUM_LANGUAGES];
|
||||
/** Cache of primitive types. */
|
||||
@ -383,6 +384,13 @@ drgn_program_find_symbol_by_address_internal(struct drgn_program *prog,
|
||||
uint64_t address,
|
||||
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 *
|
||||
drgn_program_register_symbol_finder_impl(struct drgn_program *prog,
|
||||
struct drgn_symbol_finder *finder,
|
||||
|
@ -385,6 +385,29 @@ static PyObject *Program_add_memory_segment(Program *self, PyObject *args,
|
||||
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,
|
||||
size_t name_len, const char *filename,
|
||||
void *arg,
|
||||
@ -392,6 +415,33 @@ static struct drgn_error *py_type_find_fn(uint64_t kinds, const char *name,
|
||||
{
|
||||
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 =
|
||||
PyUnicode_FromStringAndSize(name, name_len);
|
||||
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();
|
||||
if (type_obj == Py_None)
|
||||
continue;
|
||||
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;
|
||||
return py_type_find_fn_common(type_obj, arg, ret);
|
||||
}
|
||||
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,
|
||||
const char *filename,
|
||||
enum drgn_find_object_flags flags,
|
||||
@ -560,10 +565,11 @@ py_symbol_find_fn(const char *name, uint64_t addr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define symbol_finder_arg(self, fn) \
|
||||
#define type_finder_arg(self, fn) \
|
||||
_cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); \
|
||||
if (!arg) \
|
||||
return NULL;
|
||||
#define symbol_finder_arg type_finder_arg
|
||||
|
||||
#define DEFINE_PROGRAM_FINDER_METHODS(which) \
|
||||
static PyObject *Program_register_##which##_finder(Program *self, \
|
||||
@ -705,8 +711,62 @@ static PyObject *Program_enabled_##which##_finders(Program *self) \
|
||||
return_ptr(res); \
|
||||
}
|
||||
|
||||
DEFINE_PROGRAM_FINDER_METHODS(type)
|
||||
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,
|
||||
PyObject *kwds)
|
||||
{
|
||||
@ -1396,6 +1456,7 @@ static int Program_set_language(Program *self, PyObject *value, void *arg)
|
||||
static PyMethodDef Program_methods[] = {
|
||||
{"add_memory_segment", (PyCFunction)Program_add_memory_segment,
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_Program_add_memory_segment_DOC},
|
||||
PROGRAM_FINDER_METHOD_DEFS(type),
|
||||
PROGRAM_FINDER_METHOD_DEFS(symbol),
|
||||
{"add_type_finder", (PyCFunction)Program_add_type_finder,
|
||||
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);
|
||||
drgn_dedupe_type_set_deinit(&prog->dedupe_types);
|
||||
|
||||
struct drgn_type_finder *finder = prog->type_finders;
|
||||
while (finder) {
|
||||
struct drgn_type_finder *next = finder->next;
|
||||
if (finder->free)
|
||||
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);
|
||||
drgn_handler_list_deinit(struct drgn_type_finder, finder,
|
||||
&prog->type_finders,
|
||||
if (finder->ops.destroy)
|
||||
finder->ops.destroy(finder->arg);
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
struct drgn_qualified_type *ret)
|
||||
{
|
||||
struct drgn_type_finder *finder = prog->type_finders;
|
||||
while (finder) {
|
||||
drgn_handler_list_for_each_enabled(struct drgn_type_finder, finder,
|
||||
&prog->type_finders) {
|
||||
struct drgn_error *err =
|
||||
finder->fn(kinds, name, name_len, filename, finder->arg,
|
||||
ret);
|
||||
finder->ops.find(kinds, name, name_len, filename,
|
||||
finder->arg, ret);
|
||||
if (!err) {
|
||||
if (drgn_type_program(ret->type) != prog) {
|
||||
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)
|
||||
return err;
|
||||
finder = finder->next;
|
||||
}
|
||||
return &drgn_not_found;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "drgn.h"
|
||||
#include "handler.h"
|
||||
#include "hash_table.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. */
|
||||
struct drgn_type_finder {
|
||||
/** The callback. */
|
||||
drgn_type_find_fn fn;
|
||||
/** Argument to pass to @ref drgn_type_finder::fn. */
|
||||
struct drgn_handler handler;
|
||||
struct drgn_type_finder_ops ops;
|
||||
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 *);
|
||||
|
||||
/** <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_find_type(kind, name, filename):
|
||||
def mock_find_type(prog, kinds, name, filename):
|
||||
if filename:
|
||||
return None
|
||||
for type in types:
|
||||
if type.kind == kind:
|
||||
if type.kind in kinds:
|
||||
try:
|
||||
type_name = type.name
|
||||
except AttributeError:
|
||||
@ -105,7 +105,7 @@ def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=N
|
||||
if segments is not None:
|
||||
add_mock_memory_segments(prog, segments)
|
||||
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:
|
||||
prog.add_object_finder(mock_object_find)
|
||||
return prog
|
||||
|
@ -404,14 +404,73 @@ class TestMemory(TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestTypes(MockProgramTestCase):
|
||||
def test_invalid_finder(self):
|
||||
self.assertRaises(TypeError, self.prog.add_type_finder, "foo")
|
||||
class TestTypeFinder(TestCase):
|
||||
def test_register(self):
|
||||
prog = Program(MOCK_PLATFORM)
|
||||
|
||||
self.prog.add_type_finder(lambda kind, name, filename: "foo")
|
||||
self.assertRaises(TypeError, self.prog.type, "int")
|
||||
# We don't test every corner case because the symbol finder tests cover
|
||||
# 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):
|
||||
if kind == TypeKind.TYPEDEF and name == "foo":
|
||||
prog = Program()
|
||||
@ -419,23 +478,41 @@ class TestTypes(MockProgramTestCase):
|
||||
else:
|
||||
return None
|
||||
|
||||
self.prog.add_type_finder(finder)
|
||||
prog = Program(MOCK_PLATFORM)
|
||||
prog.add_type_finder(finder)
|
||||
self.assertRaisesRegex(
|
||||
ValueError,
|
||||
"type find callback returned type from wrong program",
|
||||
self.prog.type,
|
||||
prog.type,
|
||||
"foo",
|
||||
)
|
||||
|
||||
def test_wrong_kind(self):
|
||||
self.prog.add_type_finder(lambda kind, name, filename: self.prog.void_type())
|
||||
self.assertRaises(TypeError, self.prog.type, "int")
|
||||
def test_register_wrong_kind(self):
|
||||
prog = Program(MOCK_PLATFORM)
|
||||
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):
|
||||
self.assertRaises(LookupError, self.prog.type, "struct foo")
|
||||
self.prog.add_type_finder(lambda kind, name, filename: None)
|
||||
self.assertRaises(LookupError, self.prog.type, "struct foo")
|
||||
def test_add_wrong_kind(self):
|
||||
prog = Program(MOCK_PLATFORM)
|
||||
prog.add_type_finder(lambda kind, name, filename: prog.void_type())
|
||||
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):
|
||||
self.assertIdentical(
|
||||
self.prog.type(self.prog.pointer_type(self.prog.void_type())),
|
||||
|
Loading…
Reference in New Issue
Block a user