libdwfl_stacktrace [6/12]: Elf* caching via dwflst_process_tracker
Changes for v6:
- Minor fixes as requested.
Changes for v5:
- Bugfixes in dwflst_tracker_find_elf.c.
Changes for v4:
- Separate out libdwfl_stacktrace, as requested.
- dwfl_module_getdwarf.c now uses the dwflst_tracker_cache_elf()
interface instead of editing the elftab directly.
Changes for v3:
- Reworked elftab to incorporate dev/ino into the caching key
(to allow caching modules from different filesystems
e.g. a main filesystem and a container filesystem)
and guard more carefully against collisions.
- Add external API for implementing a custom
find_elf callback that reads/writes the cache.
Changes for v2:
- Add locking for elftab. This is needed in addition to the
intrinsic locking in dynamicsizehash_concurrent to avoid
having cache_elf expose an incomplete dwfltracker_elf_info*
entry to other threads while its data is being populated /
replaced.
- Tidy dwfl_process_tracker_find_elf.c into the main find_elf
callback and two functions to consider (in future) making into
a public api for custom cached callbacks.
* * *
The Dwflst_Process_Tracker includes a dynamicsizehash cache which maps
file paths to Elf * (or rather, dwflst_tracker_elf_info * storing fd
and Elf *). We provide a dwflst_tracker_linux_proc_find_elf callback
which checks the cache for an already-loaded Elf * and, if missing,
populates the cache with the fd returned by dwfl_linux_proc_find_elf.
Later, open_elf updates the cache with the Elf * for that fd. The
commented asserts in dwflst_tracker_cache_elf still catch some cases
where a redundant Elf * is being created without checking the cache.
Since the Elf * outlasts the Dwfl that created it, we use the
(convenient, already-existing) reference count field in Elf * to
retain the data in the table. Then dwfl_end calling elf_end will
decrement the refcount cleanly, and dwflst_tracker_end will issue
another elf_end call.
* libdwfl_stacktrace/libdwfl_stacktrace.h
(dwflst_tracker_find_cached_elf): New function.
(dwflst_tracker_cache_elf): New function.
(dwflst_module_gettracker): New function, gives external users
a way to access Dwflst_Process_Tracker given a Dwfl_Module.
(dwflst_tracker_linux_proc_find_elf): New function, serves as a
cached version of the dwfl_linux_proc_find_elf callback.
* libdwfl_stacktrace/libdwfl_stacktraceP.h (dwflst_tracker_elf_info):
New struct typedef.
(struct Dwflst_Process_Tracker): Add dynamicsizehash table of
dwflst_tracker_elf_info structs + associated rwlock.
(INTDECLs): Add INTDECL for dwflst_tracker_find_cached_elf,
dwflst_tracker_cache_elf, dwflst_module_gettracker.
* libdwfl_stacktrace/dwflst_tracker_elftab.c: New file, instantiates
lib/dynamicsizehash_concurrent.c to store dwflst_tracker_elf_info
structs.
* libdwfl_stacktrace/dwflst_tracker_elftab.h: New file, ditto.
* libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c: New file.
* libdwfl_stacktrace/dwflst_process_tracker.c (dwflst_tracker_begin):
Init elftab.
(dwflst_tracker_end): Clean up elftab. Lock and iterate the hash to
free tracker->elftab.table items.
* libdwfl_stacktrace/dwflst_tracker_find_elf.c: New file, implements a
find_elf callback that wraps dwfl_linux_proc_find_elf with
additional caching logic, and an API to access the
Dwflst_Process_Tracker Elf cache when implementing a custom find_elf
callback.
* libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): Add
dwflst_tracker_find_elf.c, dwflst_tracker_elftab.c,
libdwfl_stacktrace_next_prime.c.
(noinst_HEADERS): Add dwflst_tracker_elftab.h.
* libdw/libdw.map: Add dwflst_tracker_find_cached_elf,
dwflst_tracker_cache_elf,
dwflst_module_gettracker,
dwflst_tracker_linux_proc_find_elf.
* libdwfl/Makefile.am (AM_CPPFLAGS): Include headers from
../libdwfl_stacktrace.
* libdwfl/dwfl_module_getdwarf.c (open_elf): Cache file->elf in
Dwflst_Process_Tracker. Must be done here as
dwfl_linux_proc_find_elf opens an fd but does not yet create the
Elf *.