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:
Omar Sandoval 2024-05-31 11:44:32 -07:00
parent 9b73b44908
commit 5c9797a633
11 changed files with 366 additions and 157 deletions

View File

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

View File

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

View File

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

View File

@ -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, dont 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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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())),