mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 17:23:06 +00:00
libdrgn: linux_kernel: optimize reading module list
An upcoming fix requires us to always use the module list from the core dump rather than /proc/modules. However, with the existing code, this would cause a major startup time regression for the live kernel, mainly because reading from /proc/kcore is stupidly slow. We currently do 3 + strlen(module->name) reads for every module. We can reduce this to 1 read per module by reading the entire struct module at once. The size of struct module is ~700-900 bytes depending on the kernel configuration, which is still much faster to read than only reading what we need. In some benchmarks that I did with DRGN_USE_PROC_AND_SYS_MODULES=0, this reduced the time spent in the kernel module iterator from ~2.5ms per module to ~0.4ms per module. Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
parent
a2db11ebae
commit
94036f6daf
@ -351,8 +351,15 @@ struct kernel_module_iterator {
|
|||||||
size_t name_capacity;
|
size_t name_capacity;
|
||||||
/* If not using /proc/modules. */
|
/* If not using /proc/modules. */
|
||||||
struct {
|
struct {
|
||||||
|
/* `struct module` type. */
|
||||||
struct drgn_qualified_type module_type;
|
struct drgn_qualified_type module_type;
|
||||||
struct drgn_object mod, node, tmp1, tmp2, tmp3;
|
/* Current `struct module` (not a pointer). */
|
||||||
|
struct drgn_object mod;
|
||||||
|
/* `struct list_head *` in next module to return. */
|
||||||
|
struct drgn_object node;
|
||||||
|
/* Temporary objects reused for various purposes. */
|
||||||
|
struct drgn_object tmp1, tmp2, tmp3;
|
||||||
|
/* Address of `struct list_head modules`. */
|
||||||
uint64_t head;
|
uint64_t head;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -408,15 +415,18 @@ kernel_module_iterator_init(struct kernel_module_iterator *it,
|
|||||||
&it->node);
|
&it->node);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
err = drgn_object_address_of(&it->node, &it->node);
|
if (it->node.kind != DRGN_OBJECT_REFERENCE) {
|
||||||
|
err = drgn_error_create(DRGN_ERROR_OTHER,
|
||||||
|
"can't get address of modules list");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
it->head = it->node.address;
|
||||||
|
err = drgn_object_member(&it->node, &it->node, "next");
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
err = drgn_object_read(&it->node, &it->node);
|
err = drgn_object_read(&it->node, &it->node);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
err = drgn_object_read_unsigned(&it->node, &it->head);
|
|
||||||
if (err)
|
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -471,12 +481,6 @@ kernel_module_iterator_next(struct kernel_module_iterator *it)
|
|||||||
|
|
||||||
struct drgn_error *err;
|
struct drgn_error *err;
|
||||||
|
|
||||||
err = drgn_object_member_dereference(&it->node, &it->node, "next");
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
err = drgn_object_read(&it->node, &it->node);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
uint64_t addr;
|
uint64_t addr;
|
||||||
err = drgn_object_read_unsigned(&it->node, &addr);
|
err = drgn_object_read_unsigned(&it->node, &addr);
|
||||||
if (err)
|
if (err)
|
||||||
@ -488,10 +492,25 @@ kernel_module_iterator_next(struct kernel_module_iterator *it)
|
|||||||
"list");
|
"list");
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
err = drgn_object_dereference(&it->mod, &it->mod);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
// We need several fields from the `struct module`. Especially for
|
||||||
|
// /proc/kcore, it is faster to read the entire structure (which is <1kB
|
||||||
|
// as of Linux 6.0) from the core dump all at once than it is to read
|
||||||
|
// each field individually.
|
||||||
|
err = drgn_object_read(&it->mod, &it->mod);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = drgn_object_member(&it->node, &it->mod, "list");
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = drgn_object_member(&it->node, &it->node, "next");
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
// Set tmp1 to the module base address and tmp2 to the size.
|
// Set tmp1 to the module base address and tmp2 to the size.
|
||||||
err = drgn_object_member_dereference(&it->tmp1, &it->mod,
|
err = drgn_object_member(&it->tmp1, &it->mod, "core_layout");
|
||||||
"core_layout");
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
// Since Linux kernel commit 7523e4dc5057 ("module: use a
|
// Since Linux kernel commit 7523e4dc5057 ("module: use a
|
||||||
// structure to encapsulate layout.") (in v4.5), the base and
|
// structure to encapsulate layout.") (in v4.5), the base and
|
||||||
@ -507,12 +526,10 @@ kernel_module_iterator_next(struct kernel_module_iterator *it)
|
|||||||
// Before that, they are directly in the `struct module`.
|
// Before that, they are directly in the `struct module`.
|
||||||
drgn_error_destroy(err);
|
drgn_error_destroy(err);
|
||||||
|
|
||||||
err = drgn_object_member_dereference(&it->tmp2, &it->mod,
|
err = drgn_object_member(&it->tmp2, &it->mod, "core_size");
|
||||||
"core_size");
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
err = drgn_object_member_dereference(&it->tmp1, &it->mod,
|
err = drgn_object_member(&it->tmp1, &it->mod, "module_core");
|
||||||
"module_core");
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
} else {
|
} else {
|
||||||
@ -526,7 +543,7 @@ kernel_module_iterator_next(struct kernel_module_iterator *it)
|
|||||||
return err;
|
return err;
|
||||||
it->end += it->start;
|
it->end += it->start;
|
||||||
|
|
||||||
err = drgn_object_member_dereference(&it->tmp2, &it->mod, "name");
|
err = drgn_object_member(&it->tmp2, &it->mod, "name");
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
char *name;
|
char *name;
|
||||||
@ -675,8 +692,7 @@ kernel_module_iterator_gnu_build_id(struct kernel_module_iterator *it,
|
|||||||
|
|
||||||
struct drgn_error *err;
|
struct drgn_error *err;
|
||||||
struct drgn_program *prog = drgn_object_program(&it->mod);
|
struct drgn_program *prog = drgn_object_program(&it->mod);
|
||||||
const bool bswap =
|
const bool bswap = drgn_platform_bswap(&prog->platform);
|
||||||
drgn_type_little_endian(it->mod.type) != HOST_LITTLE_ENDIAN;
|
|
||||||
|
|
||||||
struct drgn_object attrs, attr, tmp;
|
struct drgn_object attrs, attr, tmp;
|
||||||
drgn_object_init(&attrs, prog);
|
drgn_object_init(&attrs, prog);
|
||||||
@ -685,7 +701,7 @@ kernel_module_iterator_gnu_build_id(struct kernel_module_iterator *it,
|
|||||||
|
|
||||||
// n = mod->notes_attrs->notes
|
// n = mod->notes_attrs->notes
|
||||||
uint64_t n;
|
uint64_t n;
|
||||||
err = drgn_object_member_dereference(&attrs, &it->mod, "notes_attrs");
|
err = drgn_object_member(&attrs, &it->mod, "notes_attrs");
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
err = drgn_object_member_dereference(&tmp, &attrs, "notes");
|
err = drgn_object_member_dereference(&tmp, &attrs, "notes");
|
||||||
@ -790,8 +806,7 @@ kernel_module_section_iterator_init(struct kernel_module_section_iterator *it,
|
|||||||
it->i = 0;
|
it->i = 0;
|
||||||
it->name = NULL;
|
it->name = NULL;
|
||||||
/* it->nsections = mod->sect_attrs->nsections */
|
/* it->nsections = mod->sect_attrs->nsections */
|
||||||
err = drgn_object_member_dereference(&kmod_it->tmp1,
|
err = drgn_object_member(&kmod_it->tmp1, &kmod_it->mod,
|
||||||
&kmod_it->mod,
|
|
||||||
"sect_attrs");
|
"sect_attrs");
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
Loading…
Reference in New Issue
Block a user