From 2d8aeacb307db8be04dc5f712c650ccc2b632f84 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Tue, 4 Jun 2024 16:58:25 -0700 Subject: [PATCH] Allow naming and configuring order of object finders This one doesn't need any changes to the callback signature, just the new interface. We also keep add_object_finder() for compatibility. Signed-off-by: Omar Sandoval --- _drgn.pyi | 59 ++++++++++++++++++++---- docs/advanced_usage.rst | 2 +- libdrgn/Makefile.am | 2 - libdrgn/debug_info.c | 9 ++-- libdrgn/debug_info.h | 6 +-- libdrgn/drgn.h | 98 ++++++++++++++++++++++++++++++---------- libdrgn/linux_kernel.c | 6 ++- libdrgn/object.h | 6 +++ libdrgn/object_index.c | 94 -------------------------------------- libdrgn/object_index.h | 85 ---------------------------------- libdrgn/program.c | 66 +++++++++++++++++++-------- libdrgn/program.h | 16 ++++--- libdrgn/python/program.c | 19 ++++++-- tests/__init__.py | 2 +- tests/test_program.py | 84 +++++++++++++++++++++++++++++----- 15 files changed, 290 insertions(+), 264 deletions(-) delete mode 100644 libdrgn/object_index.c delete mode 100644 libdrgn/object_index.h diff --git a/_drgn.pyi b/_drgn.pyi index 21020c08a..22e96b02b 100644 --- a/_drgn.pyi +++ b/_drgn.pyi @@ -480,6 +480,49 @@ class Program: def enabled_type_finders(self) -> List[str]: """Return the names of enabled type finders, in order.""" ... + def register_object_finder( + self, + name: str, + fn: Callable[[Program, str, FindObjectFlags, Optional[str]], Optional[Object]], + *, + enable_index: Optional[int] = None, + ) -> None: + """ + Register a callback for finding objects in the program. + + This does not enable the finder unless *enable_index* is given. + + :param name: Finder name. + :param fn: Callable taking the program, name, :class:`FindObjectFlags`, + and filename: ``(prog, name, flags, filename)``. The filename + should be matched with :func:`filename_matches()`. This should + return an :class:`Object` or ``None`` if not found. + :param enable_index: Insert the finder into the list of enabled object + 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_object_finders(self) -> Set[str]: + """Return the names of all registered object finders.""" + ... + def set_enabled_object_finders(self, names: Sequence[str]) -> None: + """ + Set the list of enabled object finders. + + Finders are called in the same order as the list until an object 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_object_finders(self) -> List[str]: + """Return the names of enabled object finders, in order.""" + ... def register_symbol_finder( self, name: str, @@ -563,16 +606,16 @@ class Program: fn: Callable[[Program, str, FindObjectFlags, Optional[str]], Optional[Object]], ) -> None: """ - Register a callback for finding objects in the program. + Deprecated method to register and enable a callback for finding objects + in the program. - Callbacks are called in reverse order of the order they were added - until the object is found. So, more recently added callbacks take - precedence. + .. deprecated:: 0.0.27 + Use :meth:`register_object_finder()` instead. - :param fn: Callable taking a program, name, :class:`FindObjectFlags`, - and filename: ``(prog, name, flags, filename)``. The filename - should be matched with :func:`filename_matches()`. This should - return an :class:`Object` or ``None`` if not found. + The differences from :meth:`register_object_finder()` are: + + 1. A name for the finder is generated from *fn*. + 2. The finder is always enabled before any existing finders. """ ... def set_core_dump(self, path: Union[Path, int]) -> None: diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index fc5b5c877..2bfad0f8c 100644 --- a/docs/advanced_usage.rst +++ b/docs/advanced_usage.rst @@ -83,7 +83,7 @@ program "memory": run_interactive(prog, banner_func=lambda _: "BTRFS debugger") :meth:`drgn.Program.register_type_finder()` and -:meth:`drgn.Program.add_object_finder()` are the equivalent methods for +:meth:`drgn.Program.register_object_finder()` are the equivalent methods for plugging in types and objects. Environment Variables diff --git a/libdrgn/Makefile.am b/libdrgn/Makefile.am index 689b2fec0..5da34e3bf 100644 --- a/libdrgn/Makefile.am +++ b/libdrgn/Makefile.am @@ -88,8 +88,6 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \ nstring.h \ object.c \ object.h \ - object_index.c \ - object_index.h \ openmp.c \ openmp.h \ orc.h \ diff --git a/libdrgn/debug_info.c b/libdrgn/debug_info.c index 34a36dd2e..d7cdcfd70 100644 --- a/libdrgn/debug_info.c +++ b/libdrgn/debug_info.c @@ -2187,9 +2187,12 @@ void drgn_debug_info_init(struct drgn_debug_info *dbinfo, 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); + const struct drgn_object_finder_ops object_finder_ops = { + .find = drgn_debug_info_find_object, + }; + drgn_program_register_object_finder_impl(prog, &dbinfo->object_finder, + "dwarf", &object_finder_ops, + dbinfo, 0); const struct drgn_symbol_finder_ops symbol_finder_ops = { .find = elf_symbols_search, }; diff --git a/libdrgn/debug_info.h b/libdrgn/debug_info.h index 0b689106b..b64a327bf 100644 --- a/libdrgn/debug_info.h +++ b/libdrgn/debug_info.h @@ -20,7 +20,7 @@ #include "drgn.h" #include "dwarf_info.h" #include "hash_table.h" -#include "object_index.h" +#include "object.h" #include "orc_info.h" #include "string_builder.h" #include "symbol.h" @@ -253,13 +253,13 @@ struct drgn_error * drgn_debug_info_main_language(struct drgn_debug_info *dbinfo, const struct drgn_language **ret); -/** @ref drgn_type_find_fn() that uses debugging information. */ +/** @ref drgn_type_finder_ops::find() that uses debugging information. */ struct drgn_error *drgn_debug_info_find_type(uint64_t kinds, const char *name, size_t name_len, const char *filename, void *arg, struct drgn_qualified_type *ret); -/** @ref drgn_object_find_fn() that uses debugging information. */ +/** @ref drgn_object_finder_ops::find() that uses debugging information. */ struct drgn_error * drgn_debug_info_find_object(const char *name, size_t name_len, const char *filename, diff --git a/libdrgn/drgn.h b/libdrgn/drgn.h index 97c5b421d..6096469fc 100644 --- a/libdrgn/drgn.h +++ b/libdrgn/drgn.h @@ -685,38 +685,90 @@ enum drgn_find_object_flags { DRGN_FIND_OBJECT_ANY = (1 << 3) - 1, }; +/** Object finder callback table. */ +struct drgn_object_finder_ops { + /** + * Callback to destroy the object finder. + * + * This may be @c NULL. + * + * @param[in] arg Argument passed to @ref + * drgn_program_register_object_finder(). + */ + void (*destroy)(void *arg); + /** + * Callback for finding an object. + * + * @param[in] name Name of object. This is @em not null-terminated. + * @param[in] name_len Length of @p name. + * @param[in] filename Filename containing the object definition or @c + * NULL. This should be matched with @ref drgn_filename_matches(). + * @param[in] flags Flags indicating what kind of object to look for. + * @param[in] arg Argument passed to @ref + * drgn_program_register_object_finder(). + * @param[out] ret Returned object. This must only be modified on + * success. + * @return @c NULL on success, non-@c NULL on error. In particular, if + * the object is not found, this should return &@ref drgn_not_found; any + * other errors are considered fatal. + */ + struct drgn_error *(*find)(const char *name, size_t name_len, + const char *filename, + enum drgn_find_object_flags flags, + void *arg, struct drgn_object *ret); +}; + /** - * Callback for finding an object. + * Register an object finding callback. * - * @param[in] name Name of object. This is @em not null-terminated. - * @param[in] name_len Length of @p name. - * @param[in] filename Filename containing the object definition or @c NULL. - * This should be matched with @ref drgn_filename_matches(). - * @param[in] flags Flags indicating what kind of object to look for. - * @param[in] arg Argument passed to @ref drgn_program_add_object_finder(). - * @param[out] ret Returned object. This must only be modified on success. - * @return @c NULL on success, non-@c NULL on error. In particular, if the - * object is not found, this should return &@ref drgn_not_found; any other - * errors are considered fatal. + * @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. */ -typedef struct drgn_error * -(*drgn_object_find_fn)(const char *name, size_t name_len, const char *filename, - enum drgn_find_object_flags flags, void *arg, - struct drgn_object *ret); +struct drgn_error * +drgn_program_register_object_finder(struct drgn_program *prog, const char *name, + const struct drgn_object_finder_ops *ops, + void *arg, size_t enable_index); /** - * Register a object finding callback. + * Get the names of all registered object finders. * - * Callbacks are called in reverse order of the order they were added until the - * object is found. So, more recently added callbacks take precedence. + * The order of the names is arbitrary. * - * @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[out] names_ret Returned array of names. + * @param[out] count_ret Returned number of names in @p names_ret. */ struct drgn_error * -drgn_program_add_object_finder(struct drgn_program *prog, - drgn_object_find_fn fn, void *arg); +drgn_program_registered_object_finders(struct drgn_program *prog, + const char ***names_ret, + size_t *count_ret); + +/** + * Set the list of enabled object finders. + * + * Finders are called in the same order as the list until a object 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_object_finders(struct drgn_program *prog, + const char * const *names, + size_t count); + +/** + * Get the names of enabled object 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_object_finders(struct drgn_program *prog, + const char ***names_ret, size_t *count_ret); /** * Set a @ref drgn_program to a core dump. diff --git a/libdrgn/linux_kernel.c b/libdrgn/linux_kernel.c index 493905b7b..14a161e11 100644 --- a/libdrgn/linux_kernel.c +++ b/libdrgn/linux_kernel.c @@ -384,8 +384,10 @@ static struct drgn_error *linux_kernel_get_vmemmap(struct drgn_program *prog, struct drgn_error *drgn_program_finish_set_kernel(struct drgn_program *prog) { struct drgn_error *err; - err = drgn_program_add_object_finder(prog, linux_kernel_object_find, - prog); + const struct drgn_object_finder_ops ops = { + .find = linux_kernel_object_find, + }; + err = drgn_program_register_object_finder(prog, "linux", &ops, prog, 0); if (err) return err; if (!prog->lang) diff --git a/libdrgn/object.h b/libdrgn/object.h index 8fbac1d85..2b8e82747 100644 --- a/libdrgn/object.h +++ b/libdrgn/object.h @@ -34,6 +34,12 @@ * @{ */ +struct drgn_object_finder { + struct drgn_handler handler; + struct drgn_object_finder_ops ops; + void *arg; +}; + /** Allocate a zero-initialized @ref drgn_value. */ static inline bool drgn_value_zalloc(uint64_t size, union drgn_value *value_ret, char **buf_ret) diff --git a/libdrgn/object_index.c b/libdrgn/object_index.c deleted file mode 100644 index 5b49a0b84..000000000 --- a/libdrgn/object_index.c +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include -#include - -#include "object_index.h" - -void drgn_object_index_init(struct drgn_object_index *oindex) -{ - oindex->finders = NULL; -} - -void drgn_object_index_deinit(struct drgn_object_index *oindex) -{ - struct drgn_object_finder *finder = oindex->finders; - while (finder) { - struct drgn_object_finder *next = finder->next; - if (finder->free) - free(finder); - finder = next; - } -} - -struct drgn_error * -drgn_object_index_add_finder(struct drgn_object_index *oindex, - struct drgn_object_finder *finder, - drgn_object_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 = oindex->finders; - oindex->finders = finder; - return NULL; -} - -struct drgn_error *drgn_object_index_find(struct drgn_object_index *oindex, - const char *name, - const char *filename, - enum drgn_find_object_flags flags, - struct drgn_object *ret) -{ - struct drgn_error *err; - size_t name_len; - struct drgn_object_finder *finder; - const char *kind_str; - - if ((flags & ~DRGN_FIND_OBJECT_ANY) || !flags) { - return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, - "invalid find object flags"); - } - - name_len = strlen(name); - finder = oindex->finders; - while (finder) { - err = finder->fn(name, name_len, filename, flags, finder->arg, - ret); - if (err != &drgn_not_found) - return err; - finder = finder->next; - } - - switch (flags) { - case DRGN_FIND_OBJECT_CONSTANT: - kind_str = "constant "; - break; - case DRGN_FIND_OBJECT_FUNCTION: - kind_str = "function "; - break; - case DRGN_FIND_OBJECT_VARIABLE: - kind_str = "variable "; - break; - default: - kind_str = ""; - break; - } - if (filename) { - return drgn_error_format(DRGN_ERROR_LOOKUP, - "could not find %s'%s' in '%s'", - kind_str, name, filename); - } else { - return drgn_error_format(DRGN_ERROR_LOOKUP, - "could not find %s'%s'", kind_str, - name); - } -} diff --git a/libdrgn/object_index.h b/libdrgn/object_index.h deleted file mode 100644 index f3d843821..000000000 --- a/libdrgn/object_index.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. -// SPDX-License-Identifier: LGPL-2.1-or-later - -/** - * @file - * - * Object lookup. - * - * See @ref ObjectIndex. - */ - -#ifndef DRGN_OBJECT_INDEX_H -#define DRGN_OBJECT_INDEX_H - -#include "drgn.h" - -/** - * @ingroup Internals - * - * @defgroup ObjectIndex Object index - * - * Object lookup. - * - * @ref drgn_object_index provides a common interface for finding objects (e.g., - * variables, constants, and functions) in a program. - * - * @{ - */ - -/** Registered callback in a @ref drgn_object_index. */ -struct drgn_object_finder { - /** The callback. */ - drgn_object_find_fn fn; - /** Argument to pass to @ref drgn_object_finder::fn. */ - void *arg; - /** Next callback to try. */ - struct drgn_object_finder *next; - /** Whether this structure needs to be freed. */ - bool free; -}; - -/** - * Object index. - * - * A object index is used to find objects (variables, constants, and functions) - * by name. The objects are found using callbacks which are registered with @ref - * drgn_object_index_add_finder(). @ref drgn_object_index_find() searches for an - * object. - */ -struct drgn_object_index { - /** Callbacks for finding objects. */ - struct drgn_object_finder *finders; -}; - -/** Initialize a @ref drgn_object_index. */ -void drgn_object_index_init(struct drgn_object_index *oindex); - -/** Deinitialize a @ref drgn_object_index. */ -void drgn_object_index_deinit(struct drgn_object_index *oindex); - -struct drgn_error * -drgn_object_index_add_finder(struct drgn_object_index *oindex, - struct drgn_object_finder *finder, - drgn_object_find_fn fn, void *arg); - -/** - * Find an object in a @ref drgn_object_index. - * - * @param[in] oindex Object index. - * @param[in] name Name of the object. - * @param[in] filename Exact filename containing the object definition, or @c - * NULL for any definition. - * @param[in] flags Bitmask of @ref drgn_find_object_flags. - * @param[out] ret Returned object. - * @return @c NULL on success, non-@c NULL on error. - */ -struct drgn_error *drgn_object_index_find(struct drgn_object_index *oindex, - const char *name, - const char *filename, - enum drgn_find_object_flags flags, - struct drgn_object *ret); - -/** @} */ - -#endif /* DRGN_OBJECT_INDEX_H */ diff --git a/libdrgn/program.c b/libdrgn/program.c index 11e411143..513be636f 100644 --- a/libdrgn/program.c +++ b/libdrgn/program.c @@ -28,7 +28,6 @@ #include "linux_kernel.h" #include "memory_reader.h" #include "minmax.h" -#include "object_index.h" #include "program.h" #include "symbol.h" #include "util.h" @@ -100,7 +99,6 @@ void drgn_program_init(struct drgn_program *prog, memset(prog, 0, sizeof(*prog)); drgn_memory_reader_init(&prog->reader); drgn_program_init_types(prog); - drgn_object_index_init(&prog->oindex); drgn_debug_info_init(&prog->dbinfo, prog); prog->core_fd = -1; if (platform) @@ -139,7 +137,11 @@ void drgn_program_deinit(struct drgn_program *prog) if (finder->ops.destroy) finder->ops.destroy(finder->arg); ); - drgn_object_index_deinit(&prog->oindex); + drgn_handler_list_deinit(struct drgn_object_finder, finder, + &prog->object_finders, + if (finder->ops.destroy) + finder->ops.destroy(finder->arg); + ); drgn_program_deinit_types(prog); drgn_memory_reader_deinit(&prog->reader); @@ -196,21 +198,6 @@ drgn_program_add_memory_segment(struct drgn_program *prog, uint64_t address, physical); } -struct drgn_error * -drgn_program_add_object_finder_impl(struct drgn_program *prog, - struct drgn_object_finder *finder, - drgn_object_find_fn fn, void *arg) -{ - return drgn_object_index_add_finder(&prog->oindex, finder, fn, arg); -} - -LIBDRGN_PUBLIC struct drgn_error * -drgn_program_add_object_finder(struct drgn_program *prog, - drgn_object_find_fn fn, void *arg) -{ - return drgn_program_add_object_finder_impl(prog, NULL, fn, arg); -} - #define DRGN_PROGRAM_FINDER(which) \ struct drgn_error * \ drgn_program_register_##which##_finder_impl(struct drgn_program *prog, \ @@ -284,6 +271,7 @@ drgn_program_enabled_##which##_finders(struct drgn_program *prog, \ } DRGN_PROGRAM_FINDER(type) +DRGN_PROGRAM_FINDER(object) DRGN_PROGRAM_FINDER(symbol) #undef DRGN_PROGRAM_FINDER @@ -1845,12 +1833,50 @@ drgn_program_find_object(struct drgn_program *prog, const char *name, enum drgn_find_object_flags flags, struct drgn_object *ret) { + struct drgn_error *err; + + if ((flags & ~DRGN_FIND_OBJECT_ANY) || !flags) { + return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, + "invalid find object flags"); + } if (ret && drgn_object_program(ret) != prog) { return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, "object is from wrong program"); } - return drgn_object_index_find(&prog->oindex, name, filename, flags, - ret); + + size_t name_len = strlen(name); + drgn_handler_list_for_each_enabled(struct drgn_object_finder, finder, + &prog->object_finders) { + err = finder->ops.find(name, name_len, filename, flags, + finder->arg, ret); + if (err != &drgn_not_found) + return err; + } + + const char *kind_str; + switch (flags) { + case DRGN_FIND_OBJECT_CONSTANT: + kind_str = "constant "; + break; + case DRGN_FIND_OBJECT_FUNCTION: + kind_str = "function "; + break; + case DRGN_FIND_OBJECT_VARIABLE: + kind_str = "variable "; + break; + default: + kind_str = ""; + break; + } + if (filename) { + return drgn_error_format(DRGN_ERROR_LOOKUP, + "could not find %s'%s' in '%s'", + kind_str, name, filename); + } else { + return drgn_error_format(DRGN_ERROR_LOOKUP, + "could not find %s'%s'", kind_str, + name); + } } struct drgn_error *drgn_error_symbol_not_found(uint64_t address) diff --git a/libdrgn/program.h b/libdrgn/program.h index e8687b683..a9bdb2bbc 100644 --- a/libdrgn/program.h +++ b/libdrgn/program.h @@ -25,7 +25,6 @@ #include "hash_table.h" #include "language.h" #include "memory_reader.h" -#include "object_index.h" #include "platform.h" #include "pp.h" #include "symbol.h" @@ -33,6 +32,7 @@ #include "vector.h" struct drgn_type_finder; +struct drgn_object_finder; struct drgn_symbol_finder; /** @@ -112,7 +112,7 @@ struct drgn_program { /* * Debugging information. */ - struct drgn_object_index oindex; + struct drgn_handler_list object_finders; struct drgn_debug_info dbinfo; struct drgn_handler_list symbol_finders; @@ -253,11 +253,6 @@ struct drgn_error *drgn_program_init_kernel(struct drgn_program *prog); */ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid); -struct drgn_error * -drgn_program_add_object_finder_impl(struct drgn_program *prog, - struct drgn_object_finder *finder, - drgn_object_find_fn fn, void *arg); - static inline struct drgn_error * drgn_program_is_little_endian(struct drgn_program *prog, bool *ret) { @@ -391,6 +386,13 @@ drgn_program_register_type_finder_impl(struct drgn_program *prog, const struct drgn_type_finder_ops *ops, void *arg, size_t enable_index); +struct drgn_error * +drgn_program_register_object_finder_impl(struct drgn_program *prog, + struct drgn_object_finder *finder, + const char *name, + const struct drgn_object_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, diff --git a/libdrgn/python/program.c b/libdrgn/python/program.c index 000bbfbdb..dfb577de7 100644 --- a/libdrgn/python/program.c +++ b/libdrgn/python/program.c @@ -569,6 +569,7 @@ py_symbol_find_fn(const char *name, uint64_t addr, _cleanup_pydecref_ PyObject *arg = Py_BuildValue("OO", self, fn); \ if (!arg) \ return NULL; +#define object_finder_arg(self, fn) PyObject *arg = fn; #define symbol_finder_arg type_finder_arg #define DEFINE_PROGRAM_FINDER_METHODS(which) \ @@ -712,6 +713,7 @@ static PyObject *Program_enabled_##which##_finders(Program *self) \ } DEFINE_PROGRAM_FINDER_METHODS(type) +DEFINE_PROGRAM_FINDER_METHODS(object) DEFINE_PROGRAM_FINDER_METHODS(symbol) static PyObject *deprecated_finder_name_obj(PyObject *fn) @@ -783,13 +785,23 @@ static PyObject *Program_add_object_finder(Program *self, PyObject *args, return NULL; } - if (Program_hold_object(self, fn)) + _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; - err = drgn_program_add_object_finder(&self->prog, py_object_find_fn, - fn); + if (!Program_hold_reserve(self, 1)) + return NULL; + const struct drgn_object_finder_ops ops = { + .find = py_object_find_fn, + }; + err = drgn_program_register_object_finder(&self->prog, name, &ops, fn, + 0); if (err) return set_drgn_error(err); + Program_hold_object(self, fn); Py_RETURN_NONE; } @@ -1457,6 +1469,7 @@ 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(object), PROGRAM_FINDER_METHOD_DEFS(symbol), {"add_type_finder", (PyCFunction)Program_add_type_finder, METH_VARARGS | METH_KEYWORDS, drgn_Program_add_type_finder_DOC}, diff --git a/tests/__init__.py b/tests/__init__.py index c29a51085..e3d4fd6ea 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -107,7 +107,7 @@ def mock_object_find(prog, name, flags, filename): if types is not None: prog.register_type_finder("mock", mock_find_type, enable_index=0) if objects is not None: - prog.add_object_finder(mock_object_find) + prog.register_object_finder("mock", mock_object_find, enable_index=0) return prog diff --git a/tests/test_program.py b/tests/test_program.py index 7f5306129..39d3dc1e3 100644 --- a/tests/test_program.py +++ b/tests/test_program.py @@ -512,6 +512,78 @@ def test_add_not_found(self): self.assertRaises(LookupError, prog.type, "struct foo") +class TestObjectFinder(TestCase): + def test_register(self): + prog = Program(MOCK_PLATFORM) + + # We don't test every corner case because the symbol finder tests cover + # the shared part. + self.assertEqual(prog.registered_object_finders(), {"dwarf"}) + self.assertEqual(prog.enabled_object_finders(), ["dwarf"]) + + prog.register_object_finder( + "foo", lambda prog, name, flags, filename: None, enable_index=-1 + ) + self.assertEqual(prog.registered_object_finders(), {"dwarf", "foo"}) + self.assertEqual(prog.enabled_object_finders(), ["dwarf", "foo"]) + + prog.set_enabled_object_finders(["foo"]) + self.assertEqual(prog.registered_object_finders(), {"dwarf", "foo"}) + self.assertEqual(prog.enabled_object_finders(), ["foo"]) + + def test_add_object_finder(self): + prog = Program(MOCK_PLATFORM) + + def dummy(prog, name, flags, filename): + return Object(prog, "int", 1) + + prog.add_object_finder(dummy) + self.assertTrue( + any("dummy" in name for name in prog.registered_object_finders()) + ) + self.assertIn("dummy", prog.enabled_object_finders()[0]) + self.assertIdentical(prog.object("foo"), Object(prog, "int", 1)) + + def test_register_invalid(self): + prog = Program(MOCK_PLATFORM) + self.assertRaises(TypeError, prog.register_object_finder, "foo", "foo") + prog.register_object_finder( + "foo", lambda prog, name, flags, filename: "foo", enable_index=0 + ) + self.assertRaises(TypeError, prog.object, "foo") + + def test_add_invalid(self): + prog = Program(MOCK_PLATFORM) + self.assertRaises(TypeError, prog.add_object_finder, "foo") + prog.add_object_finder(lambda prog, name, flags, filename: "foo") + self.assertRaises(TypeError, prog.object, "foo") + + def test_wrong_program(self): + prog = Program(MOCK_PLATFORM) + prog.register_object_finder( + "foo", + lambda prog, name, flags, filename: Object( + Program(MOCK_PLATFORM), "int", 1 + ), + enable_index=0, + ) + self.assertRaisesRegex( + ValueError, + "different program", + prog.object, + "foo", + ) + + def test_not_found(self): + prog = Program(MOCK_PLATFORM) + self.assertRaises(LookupError, prog.object, "foo") + prog.register_object_finder( + "foo", lambda prog, name, flags, filename: None, enable_index=0 + ) + self.assertRaises(LookupError, prog.object, "foo") + self.assertFalse("foo" in prog) + + class TestTypes(MockProgramTestCase): def test_already_type(self): self.assertIdentical( @@ -855,18 +927,6 @@ def test_array_of_pointers_to_array(self): class TestObjects(MockProgramTestCase): - def test_invalid_finder(self): - self.assertRaises(TypeError, self.prog.add_object_finder, "foo") - - self.prog.add_object_finder(lambda prog, name, flags, filename: "foo") - self.assertRaises(TypeError, self.prog.object, "foo") - - def test_not_found(self): - self.assertRaises(LookupError, self.prog.object, "foo") - self.prog.add_object_finder(lambda prog, name, flags, filename: None) - self.assertRaises(LookupError, self.prog.object, "foo") - self.assertFalse("foo" in self.prog) - def test_constant(self): self.objects.append( MockObject("PAGE_SIZE", self.prog.int_type("int", 4, True), value=4096)