* [PATCH makedumpfile 1/9] Do not call extensions for tail pages
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 2/9] Honor CFLAGS in extension/Makefile Stephen Brennan
` (9 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
Currently, extensions are called for every page, including compound
tails. But as it is currently implemented, once a compound head is
excluded, makedumpfile will create an exclusion for the entire compound
page range. If an extension returns PG_INCLUDE for a tail page, the
bitmap is not amended, so the tail page may still be excluded. Thus, the
only time an extension's decision is respected is when it is processing
the head page. Similarly, if an extension return PG_EXCLUDE on an
included tail page, no exclusion will be created, since the
compound_head check will skip ahead.
We could fix this by overwriting the 2nd bitmap's bits for
a tail page when extensions return PG_INCLUDE or PG_EXCLUDE, thus
overriding the prior decision. However, that doesn't seem like the right
approach.
Even if an extension cares to partially include a compound page, it's
simpler to keep the decision making localized to the head page. This
makes it easier to account the pages which are included/excluded, and
ensures that we can persist any necessary state across cycles for cyclic
processing. The downside is that we may need to implement more specific
ways for the extension to include or exclude tail pages.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
makedumpfile.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/makedumpfile.c b/makedumpfile.c
index 46d9ac7..e882b84 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -6543,14 +6543,6 @@ __exclude_unnecessary_pages(unsigned long mem_map,
pfn_read_end = pfn + pfn_mm - 1;
}
- /*
- * Include pages that specified by user via
- * makedumpfile extensions
- */
- filter_pg = run_extension_callback(pfn, pcache);
- if (filter_pg == PG_INCLUDE)
- continue;
-
flags = ULONG(pcache + OFFSET(page.flags));
_count = UINT(pcache + OFFSET(page._refcount));
mapping = ULONG(pcache + OFFSET(page.mapping));
@@ -6637,14 +6629,22 @@ check_order:
* Excludable compound tail pages must have already been excluded by
* exclude_range(), don't need to check them here.
*/
- if (compound_head & 1) {
+ if (compound_head & 1)
continue;
- }
+
+ /*
+ * Include pages that specified by user via
+ * makedumpfile extensions
+ */
+ filter_pg = run_extension_callback(pfn, pcache);
+ if (filter_pg == PG_INCLUDE)
+ continue;
+
/*
* Exclude the free page managed by a buddy
* Use buddy identification of free pages whether cyclic or not.
*/
- else if ((info->dump_level & DL_EXCLUDE_FREE)
+ if ((info->dump_level & DL_EXCLUDE_FREE)
&& info->page_is_buddy
&& info->page_is_buddy(flags, _mapcount, private, _count)) {
if ((ARRAY_LENGTH(zone.free_area) != NOT_FOUND_STRUCTURE) &&
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 2/9] Honor CFLAGS in extension/Makefile
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 1/9] Do not call extensions for tail pages Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 3/9] Share page information with extension callbacks Stephen Brennan
` (8 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
Currently CFLAGS are specified manually, so anything already provided in
the environment or from the calling make process is ignored.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
extensions/Makefile | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/extensions/Makefile b/extensions/Makefile
index c112d56..25e0a07 100644
--- a/extensions/Makefile
+++ b/extensions/Makefile
@@ -3,8 +3,10 @@ CONTRIB_SO := sample.so erase_sample.so
all: $(CONTRIB_SO)
+CFLAGS += -fPIC -shared -Wl,-T,../makedumpfile.ld
+
$(CONTRIB_SO): %.so: %.c
- $(CC) -O2 -g -fPIC -shared -Wl,-T,../makedumpfile.ld -o $@ $^
+ $(CC) $(CFLAGS) -o $@ $^
clean:
rm -f $(CONTRIB_SO)
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 3/9] Share page information with extension callbacks
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 1/9] Do not call extensions for tail pages Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 2/9] Honor CFLAGS in extension/Makefile Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 4/9] Introduce a stat for pages retained by extension Stephen Brennan
` (7 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
In __exclude_unnecessary_pages(), we extract several fields related
to the page. Some of these, like compound_order and compound_dtor, have
logic specific to the kernel version.
Extensions can, of course, determine these values for themselves, but
it's extra work, and duplicates logic that may need to be updated
frequently with new kernel versions. What's more, if we put all the
values together in a single structure, helpers like isSlab() and others
can be implemented in terms of that structure and shared with the
extensions in order to further simplify their implementation.
With that in mind, group the per-page variables into a structure and
share them with extension callbacks. This breaks the extension API,
but since a release hasn't yet happened, it seems reasonable to do so.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
extension.c | 8 ++---
extension.h | 3 +-
makedumpfile.c | 83 +++++++++++++++++++++++++-------------------------
makedumpfile.h | 16 ++++++++++
4 files changed, 64 insertions(+), 46 deletions(-)
diff --git a/extension.c b/extension.c
index 5188c1f..9b29f0c 100644
--- a/extension.c
+++ b/extension.c
@@ -10,7 +10,7 @@
#include "kallsyms.h"
#include "btf_info.h"
-typedef int (*callback_fn)(unsigned long, const void *);
+typedef int (*callback_fn)(unsigned long, const void *, const struct pginfo *);
struct extension_handle_cb {
void *handle;
@@ -306,14 +306,14 @@ fail:
* 1) Include the page if anyone says PG_INCLUDE, and
* 2) Exclude the page if no one says PG_INCLUDE, but one or more say PG_EXCLUDE.
*/
-int run_extension_callback(unsigned long pfn, const void *pcache)
+int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *inf)
{
int result;
int ret = PG_UNDECID;
for (int i = 0; i < handle_cbs_len; i++) {
if (handle_cbs[i]->cb) {
- result = handle_cbs[i]->cb(pfn, pcache);
+ result = handle_cbs[i]->cb(pfn, pcache, inf);
if (result == PG_INCLUDE) {
ret = result;
goto out;
@@ -341,7 +341,7 @@ bool add_extension_opts(char *opt)
return false;
}
-int run_extension_callback(unsigned long pfn, const void *pcache)
+int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i)
{
return PG_UNDECID;
}
diff --git a/extension.h b/extension.h
index ba8d32a..22af9a6 100644
--- a/extension.h
+++ b/extension.h
@@ -2,12 +2,13 @@
#define _EXTENSION_H
#include <stdbool.h>
+struct pginfo;
enum {
PG_INCLUDE, // Exntesion will keep the page
PG_EXCLUDE, // Exntesion will discard the page
PG_UNDECID, // Exntesion makes no decision
};
-int run_extension_callback(unsigned long pfn, const void *pcache);
+int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i);
void init_extensions(void);
void cleanup_extensions(void);
bool add_extension_opts(char *opt);
diff --git a/makedumpfile.c b/makedumpfile.c
index e882b84..a4c9bbf 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -6466,12 +6466,13 @@ __exclude_unnecessary_pages(unsigned long mem_map,
mdf_pfn_t pfn_read_start, pfn_read_end;
unsigned char *page_cache;
unsigned char *pcache;
- unsigned int _count, _mapcount = 0, compound_order = 0;
+ struct pginfo i;
unsigned int order_offset, dtor_offset;
- unsigned long flags, mapping, private = 0;
- unsigned long compound_dtor, compound_head = 0;
int filter_pg;
+ i._mapcount = i.compound_order = 0;
+ i.private = i.compound_dtor = i.compound_head = 0;
+
/*
* If a multi-page exclusion is pending, do it first
*/
@@ -6543,21 +6544,21 @@ __exclude_unnecessary_pages(unsigned long mem_map,
pfn_read_end = pfn + pfn_mm - 1;
}
- flags = ULONG(pcache + OFFSET(page.flags));
- _count = UINT(pcache + OFFSET(page._refcount));
- mapping = ULONG(pcache + OFFSET(page.mapping));
+ i.flags = ULONG(pcache + OFFSET(page.flags));
+ i._count = UINT(pcache + OFFSET(page._refcount));
+ i.mapping = ULONG(pcache + OFFSET(page.mapping));
if (OFFSET(page._mapcount) != NOT_FOUND_STRUCTURE)
- _mapcount = UINT(pcache + OFFSET(page._mapcount));
+ i._mapcount = UINT(pcache + OFFSET(page._mapcount));
- compound_order = 0;
- compound_dtor = 0;
+ i.compound_order = 0;
+ i.compound_dtor = 0;
/*
* The last pfn of the mem_map cache must not be compound head
* page since all compound pages are aligned to its page order
* and PGMM_CACHED is a power of 2.
*/
- if ((index_pg < PGMM_CACHED - 1) && isCompoundHead(flags)) {
+ if ((index_pg < PGMM_CACHED - 1) && isCompoundHead(i.flags)) {
unsigned char *addr = pcache + SIZE(page);
/*
@@ -6567,10 +6568,10 @@ __exclude_unnecessary_pages(unsigned long mem_map,
if (NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE) != NOT_FOUND_NUMBER) {
unsigned long _flags_1 = ULONG(addr + OFFSET(page.flags));
- compound_order = _flags_1 & 0xff;
+ i.compound_order = _flags_1 & 0xff;
- if (_mapcount == (int)NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE))
- compound_dtor = IS_HUGETLB;
+ if (i._mapcount == (int)NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE))
+ i.compound_dtor = IS_HUGETLB;
goto check_order;
}
@@ -6582,19 +6583,19 @@ __exclude_unnecessary_pages(unsigned long mem_map,
if (NUMBER(PG_hugetlb) != NOT_FOUND_NUMBER) {
unsigned long _flags_1 = ULONG(addr + OFFSET(page.flags));
- compound_order = _flags_1 & 0xff;
+ i.compound_order = _flags_1 & 0xff;
if (_flags_1 & (1UL << NUMBER(PG_hugetlb)))
- compound_dtor = IS_HUGETLB;
+ i.compound_dtor = IS_HUGETLB;
goto check_order;
}
if (order_offset) {
if (info->kernel_version >= KERNEL_VERSION(4, 16, 0))
- compound_order = UCHAR(addr + order_offset);
+ i.compound_order = UCHAR(addr + order_offset);
else
- compound_order = USHORT(addr + order_offset);
+ i.compound_order = USHORT(addr + order_offset);
}
if (dtor_offset) {
@@ -6603,40 +6604,40 @@ __exclude_unnecessary_pages(unsigned long mem_map,
* to the ID of it since linux-4.4.
*/
if (info->kernel_version >= KERNEL_VERSION(4, 16, 0))
- compound_dtor = UCHAR(addr + dtor_offset);
+ i.compound_dtor = UCHAR(addr + dtor_offset);
else if (info->kernel_version >= KERNEL_VERSION(4, 4, 0))
- compound_dtor = USHORT(addr + dtor_offset);
+ i.compound_dtor = USHORT(addr + dtor_offset);
else
- compound_dtor = ULONG(addr + dtor_offset);
+ i.compound_dtor = ULONG(addr + dtor_offset);
}
check_order:
- if ((compound_order >= sizeof(unsigned long) * 8)
- || ((pfn & ((1UL << compound_order) - 1)) != 0)) {
+ if ((i.compound_order >= sizeof(unsigned long) * 8)
+ || ((pfn & ((1UL << i.compound_order) - 1)) != 0)) {
/* Invalid order */
- compound_order = 0;
+ i.compound_order = 0;
}
}
if (OFFSET(page.compound_head) != NOT_FOUND_STRUCTURE)
- compound_head = ULONG(pcache + OFFSET(page.compound_head));
+ i.compound_head = ULONG(pcache + OFFSET(page.compound_head));
if (OFFSET(page.private) != NOT_FOUND_STRUCTURE)
- private = ULONG(pcache + OFFSET(page.private));
+ i.private = ULONG(pcache + OFFSET(page.private));
- nr_pages = 1 << compound_order;
+ nr_pages = 1 << i.compound_order;
pfn_counter = NULL;
/*
* Excludable compound tail pages must have already been excluded by
* exclude_range(), don't need to check them here.
*/
- if (compound_head & 1)
+ if (i.compound_head & 1)
continue;
/*
* Include pages that specified by user via
* makedumpfile extensions
*/
- filter_pg = run_extension_callback(pfn, pcache);
+ filter_pg = run_extension_callback(pfn, pcache, &i);
if (filter_pg == PG_INCLUDE)
continue;
@@ -6646,14 +6647,14 @@ check_order:
*/
if ((info->dump_level & DL_EXCLUDE_FREE)
&& info->page_is_buddy
- && info->page_is_buddy(flags, _mapcount, private, _count)) {
+ && info->page_is_buddy(i.flags, i._mapcount, i.private, i._count)) {
if ((ARRAY_LENGTH(zone.free_area) != NOT_FOUND_STRUCTURE) &&
- (private >= ARRAY_LENGTH(zone.free_area))) {
+ (i.private >= ARRAY_LENGTH(zone.free_area))) {
MSG("WARNING: Invalid free page order: pfn=%llx, order=%lu, max order=%lu\n",
- pfn, private, ARRAY_LENGTH(zone.free_area) - 1);
+ pfn, i.private, ARRAY_LENGTH(zone.free_area) - 1);
continue;
}
- nr_pages = 1 << private;
+ nr_pages = 1 << i.private;
pfn_counter = &pfn_free;
}
/*
@@ -6663,7 +6664,7 @@ check_order:
* accepted immediately without being on the list.
*/
else if ((info->dump_level & DL_EXCLUDE_FREE)
- && isUnaccepted(_mapcount)) {
+ && isUnaccepted(i._mapcount)) {
nr_pages = 1 << (ARRAY_LENGTH(zone.free_area) - 1);
pfn_counter = &pfn_free;
}
@@ -6671,17 +6672,17 @@ check_order:
* Exclude the non-private cache page.
*/
else if ((info->dump_level & DL_EXCLUDE_CACHE)
- && is_cache_page(flags)
- && !isPrivate(flags) && !isAnon(mapping, flags, _mapcount)) {
+ && is_cache_page(i.flags)
+ && !isPrivate(i.flags) && !isAnon(i.mapping, i.flags, i._mapcount)) {
pfn_counter = &pfn_cache;
}
/*
* Exclude the cache page whether private or non-private.
*/
else if ((info->dump_level & DL_EXCLUDE_CACHE_PRI)
- && is_cache_page(flags)
- && !isAnon(mapping, flags, _mapcount)) {
- if (isPrivate(flags))
+ && is_cache_page(i.flags)
+ && !isAnon(i.mapping, i.flags, i._mapcount)) {
+ if (isPrivate(i.flags))
pfn_counter = &pfn_cache_private;
else
pfn_counter = &pfn_cache;
@@ -6692,19 +6693,19 @@ check_order:
* - hugetlbfs pages
*/
else if ((info->dump_level & DL_EXCLUDE_USER_DATA)
- && (isAnon(mapping, flags, _mapcount) || isHugetlb(compound_dtor))) {
+ && (isAnon(i.mapping, i.flags, i._mapcount) || isHugetlb(i.compound_dtor))) {
pfn_counter = &pfn_user;
}
/*
* Exclude the hwpoison page.
*/
- else if (isHWPOISON(flags)) {
+ else if (isHWPOISON(i.flags)) {
pfn_counter = &pfn_hwpoison;
}
/*
* Exclude pages that are logically offline.
*/
- else if (isOffline(flags, _mapcount)) {
+ else if (isOffline(i.flags, i._mapcount)) {
pfn_counter = &pfn_offline;
}
/*
diff --git a/makedumpfile.h b/makedumpfile.h
index 4f707c7..87f973d 100644
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -1507,6 +1507,22 @@ struct ppc64_vmemmap {
unsigned long virt;
};
+/* Per-page information determined during page filtering which may be useful
+ * to extensions making their decisions */
+struct pginfo {
+ unsigned long flags;
+ unsigned long mapping;
+ /* Present whenever OFFSET(page.private) != NOT_FOUND_STRUCTURE */
+ unsigned long private;
+ unsigned long compound_dtor;
+ /* Present whenever OFFSET(page.compound_head) != NOT_FOUND_STRUCTURE */
+ unsigned long compound_head;
+ unsigned int _count;
+ /* Present whenever OFFSET(page._mapcount) != NOT_FOUND_STRUCTURE */
+ unsigned int _mapcount;
+ unsigned int compound_order;
+};
+
struct DumpInfo {
int32_t kernel_version; /* version of first kernel*/
struct timeval timestamp;
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 4/9] Introduce a stat for pages retained by extension
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (2 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 3/9] Share page information with extension callbacks Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 5/9] Move page checks into makedumpfile.h Stephen Brennan
` (6 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
Extensions can mark pages to be excluded, but those pages may already be
excluded due to the dump level. We have a statistic to count pages
excluded by extensions. It counts only pages which were excluded because
no other criteria excluded them.
Extensions can mark pages to be retained, but there is no statistic to
count them. Adding a counter to the code as-is would not give us the
value that we care about. Just as above, pages marked for inclusion may
have been included anyway due to the dump-level configuration. The most
useful statistic is the one that tells us how many pages were included
by the extension, which would not have been included otherwise.
Introduce a statistic that counts this amount. To do so, we have to
skip the short-circuit evaluation when PG_INCLUDE is returned. This
seems like a worthwhile trade-off, since the dump-level checks are all
reasonably efficient.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
makedumpfile.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/makedumpfile.c b/makedumpfile.c
index a4c9bbf..cf6a38f 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -106,6 +106,7 @@ mdf_pfn_t pfn_elf_excluded;
mdf_pfn_t pfn_extension;
mdf_pfn_t num_dumped;
+mdf_pfn_t num_extension_retained;
int retcd = FAILED; /* return code */
@@ -6638,8 +6639,6 @@ check_order:
* makedumpfile extensions
*/
filter_pg = run_extension_callback(pfn, pcache, &i);
- if (filter_pg == PG_INCLUDE)
- continue;
/*
* Exclude the free page managed by a buddy
@@ -6722,6 +6721,13 @@ check_order:
else
continue;
+ if (filter_pg == PG_INCLUDE) {
+ /* Account pages which would have been excluded, but were
+ * retained by an extension. */
+ num_extension_retained += nr_pages;
+ continue;
+ }
+
/*
* Execute exclusion
*/
@@ -8265,6 +8271,7 @@ write_elf_pages_cyclic(struct cache_data *cd_header, struct cache_data *cd_page)
if (info->flag_cyclic) {
pfn_zero = pfn_cache = pfn_cache_private = 0;
pfn_user = pfn_free = pfn_hwpoison = pfn_offline = pfn_extension = 0;
+ num_extension_retained = 0;
pfn_memhole = info->max_mapnr;
}
@@ -9610,6 +9617,7 @@ write_kdump_pages_and_bitmap_cyclic(struct cache_data *cd_header, struct cache_d
*/
pfn_zero = pfn_cache = pfn_cache_private = 0;
pfn_user = pfn_free = pfn_hwpoison = pfn_offline = pfn_extension = 0;
+ num_extension_retained = 0;
pfn_memhole = info->max_mapnr;
/*
@@ -10575,6 +10583,7 @@ print_report(void)
REPORT_MSG(" Hwpoison pages : 0x%016llx\n", pfn_hwpoison);
REPORT_MSG(" Offline pages : 0x%016llx\n", pfn_offline);
REPORT_MSG(" Extension filter pages : 0x%016llx\n", pfn_extension);
+ REPORT_MSG(" Retained by extension : 0x%016llx\n", num_extension_retained);
REPORT_MSG(" Remaining pages : 0x%016llx\n",
pfn_original - pfn_excluded);
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 5/9] Move page checks into makedumpfile.h
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (3 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 4/9] Introduce a stat for pages retained by extension Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 6/9] Simplify arguments for page checks Stephen Brennan
` (5 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
Most checks are already macros in makedumpfile.h, but some are simple
helper functions in makedumpfile.c. They are not accessible to
extensions as a result.
It's helpful to keep as much of this logic available to extensions as
possible, so that they do not need to re-implement more than necessary.
Move isHugetlb, isSlab, isOffline, and is_cache_page to the header.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
makedumpfile.c | 57 --------------------------------------------------
makedumpfile.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/makedumpfile.c b/makedumpfile.c
index cf6a38f..3da68fa 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -266,63 +266,6 @@ is_in_same_page(unsigned long vaddr1, unsigned long vaddr2)
return FALSE;
}
-/* For Linux 6.6 and later */
-#define IS_HUGETLB ((unsigned long)-1)
-
-static inline int
-isHugetlb(unsigned long dtor)
-{
- return (dtor == IS_HUGETLB)
- || ((NUMBER(HUGETLB_PAGE_DTOR) != NOT_FOUND_NUMBER)
- && (NUMBER(HUGETLB_PAGE_DTOR) == dtor))
- || ((SYMBOL(free_huge_page) != NOT_FOUND_SYMBOL)
- && (SYMBOL(free_huge_page) == dtor));
-}
-
-static inline int
-isSlab(unsigned long flags, unsigned int _mapcount)
-{
- /* Linux 6.10 and later */
- if (NUMBER(PAGE_SLAB_MAPCOUNT_VALUE) != NOT_FOUND_NUMBER) {
- if (_mapcount == (int)NUMBER(PAGE_SLAB_MAPCOUNT_VALUE))
- return TRUE;
- }
-
- return flags & (1UL << NUMBER(PG_slab));
-}
-
-static int
-isOffline(unsigned long flags, unsigned int _mapcount)
-{
- if (NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE) == NOT_FOUND_NUMBER)
- return FALSE;
-
- if (isSlab(flags, _mapcount))
- return FALSE;
-
- if (_mapcount == (int)NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE))
- return TRUE;
-
- return FALSE;
-}
-
-static int
-is_cache_page(unsigned long flags)
-{
- if (isLRU(flags))
- return TRUE;
-
- /* PG_swapcache is valid only if:
- * a. PG_swapbacked bit is set, or
- * b. PG_swapbacked did not exist (kernels before 4.10-rc1).
- */
- if ((NUMBER(PG_swapbacked) == NOT_FOUND_NUMBER || isSwapBacked(flags))
- && isSwapCache(flags))
- return TRUE;
-
- return FALSE;
-}
-
static inline unsigned long
calculate_len_buf_out(long page_size)
{
diff --git a/makedumpfile.h b/makedumpfile.h
index 87f973d..3d64677 100644
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -2644,6 +2644,63 @@ is_zero_page(unsigned char *buf, long page_size)
return TRUE;
}
+/* For Linux 6.6 and later */
+#define IS_HUGETLB ((unsigned long)-1)
+
+static inline int
+isHugetlb(unsigned long dtor)
+{
+ return (dtor == IS_HUGETLB)
+ || ((NUMBER(HUGETLB_PAGE_DTOR) != NOT_FOUND_NUMBER)
+ && (NUMBER(HUGETLB_PAGE_DTOR) == dtor))
+ || ((SYMBOL(free_huge_page) != NOT_FOUND_SYMBOL)
+ && (SYMBOL(free_huge_page) == dtor));
+}
+
+static inline int
+isSlab(unsigned long flags, unsigned int _mapcount)
+{
+ /* Linux 6.10 and later */
+ if (NUMBER(PAGE_SLAB_MAPCOUNT_VALUE) != NOT_FOUND_NUMBER) {
+ if (_mapcount == (int)NUMBER(PAGE_SLAB_MAPCOUNT_VALUE))
+ return TRUE;
+ }
+
+ return flags & (1UL << NUMBER(PG_slab));
+}
+
+static inline int
+isOffline(unsigned long flags, unsigned int _mapcount)
+{
+ if (NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE) == NOT_FOUND_NUMBER)
+ return FALSE;
+
+ if (isSlab(flags, _mapcount))
+ return FALSE;
+
+ if (_mapcount == (int)NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE))
+ return TRUE;
+
+ return FALSE;
+}
+
+static inline int
+is_cache_page(unsigned long flags)
+{
+ if (isLRU(flags))
+ return TRUE;
+
+ /* PG_swapcache is valid only if:
+ * a. PG_swapbacked bit is set, or
+ * b. PG_swapbacked did not exist (kernels before 4.10-rc1).
+ */
+ if ((NUMBER(PG_swapbacked) == NOT_FOUND_NUMBER || isSwapBacked(flags))
+ && isSwapCache(flags))
+ return TRUE;
+
+ return FALSE;
+}
+
void write_vmcoreinfo_data(void);
int set_bit_on_1st_bitmap(mdf_pfn_t pfn, struct cycle *cycle);
int clear_bit_on_1st_bitmap(mdf_pfn_t pfn, struct cycle *cycle);
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 6/9] Simplify arguments for page checks
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (4 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 5/9] Move page checks into makedumpfile.h Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 7/9] Add PG_INCLUDE_HEAD extension return status Stephen Brennan
` (4 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
Many isSomething() helpers need to use several fields in order to make a
determination (e.g. flags, _mapcount, private, _count). All of the info
we extract about a page's identity is now grouped together into struct
pginfo, so we can instead pass a pointer to that struct directly. This
makes it easier to read and write the code, and it also sets an easy
calling convention that extensions can use.
For helpers that require a single field, e.g. flags, still pass it
directly. The benefits of replacing that value with a structure pointer
aren't so obvious to me.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
makedumpfile.c | 22 ++++++++++------------
makedumpfile.h | 21 +++++++++------------
2 files changed, 19 insertions(+), 24 deletions(-)
diff --git a/makedumpfile.c b/makedumpfile.c
index 3da68fa..f277360 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -6008,10 +6008,9 @@ exclude_free_page(struct cycle *cycle)
* For the kernel versions from v2.6.17 to v2.6.37.
*/
static int
-page_is_buddy_v2(unsigned long flags, unsigned int _mapcount,
- unsigned long private, unsigned int _count)
+page_is_buddy_v2(const struct pginfo *i)
{
- if (flags & (1UL << NUMBER(PG_buddy)))
+ if (i->flags & (1UL << NUMBER(PG_buddy)))
return TRUE;
return FALSE;
@@ -6021,13 +6020,12 @@ page_is_buddy_v2(unsigned long flags, unsigned int _mapcount,
* For v2.6.38 and later kernel versions.
*/
static int
-page_is_buddy_v3(unsigned long flags, unsigned int _mapcount,
- unsigned long private, unsigned int _count)
+page_is_buddy_v3(const struct pginfo *i)
{
- if (isSlab(flags, _mapcount))
+ if (isSlab(i))
return FALSE;
- if (_mapcount == (int)NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE))
+ if (i->_mapcount == (int)NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE))
return TRUE;
return FALSE;
@@ -6589,7 +6587,7 @@ check_order:
*/
if ((info->dump_level & DL_EXCLUDE_FREE)
&& info->page_is_buddy
- && info->page_is_buddy(i.flags, i._mapcount, i.private, i._count)) {
+ && info->page_is_buddy(&i)) {
if ((ARRAY_LENGTH(zone.free_area) != NOT_FOUND_STRUCTURE) &&
(i.private >= ARRAY_LENGTH(zone.free_area))) {
MSG("WARNING: Invalid free page order: pfn=%llx, order=%lu, max order=%lu\n",
@@ -6615,7 +6613,7 @@ check_order:
*/
else if ((info->dump_level & DL_EXCLUDE_CACHE)
&& is_cache_page(i.flags)
- && !isPrivate(i.flags) && !isAnon(i.mapping, i.flags, i._mapcount)) {
+ && !isPrivate(i.flags) && !isAnon(&i)) {
pfn_counter = &pfn_cache;
}
/*
@@ -6623,7 +6621,7 @@ check_order:
*/
else if ((info->dump_level & DL_EXCLUDE_CACHE_PRI)
&& is_cache_page(i.flags)
- && !isAnon(i.mapping, i.flags, i._mapcount)) {
+ && !isAnon(&i)) {
if (isPrivate(i.flags))
pfn_counter = &pfn_cache_private;
else
@@ -6635,7 +6633,7 @@ check_order:
* - hugetlbfs pages
*/
else if ((info->dump_level & DL_EXCLUDE_USER_DATA)
- && (isAnon(i.mapping, i.flags, i._mapcount) || isHugetlb(i.compound_dtor))) {
+ && (isAnon(&i) || isHugetlb(i.compound_dtor))) {
pfn_counter = &pfn_user;
}
/*
@@ -6647,7 +6645,7 @@ check_order:
/*
* Exclude pages that are logically offline.
*/
- else if (isOffline(i.flags, i._mapcount)) {
+ else if (isOffline(&i)) {
pfn_counter = &pfn_offline;
}
/*
diff --git a/makedumpfile.h b/makedumpfile.h
index 3d64677..cf9d22b 100644
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -161,8 +161,8 @@ test_bit(int nr, unsigned long addr)
#define isSwapBacked(flags) test_bit(NUMBER(PG_swapbacked), flags)
#define isHWPOISON(flags) (test_bit(NUMBER(PG_hwpoison), flags) \
&& (NUMBER(PG_hwpoison) != NOT_FOUND_NUMBER))
-#define isAnon(mapping, flags, _mapcount) \
- (((unsigned long)mapping & PAGE_MAPPING_ANON) != 0 && !isSlab(flags, _mapcount))
+#define isAnon(i) \
+ (((unsigned long)(i)->mapping & PAGE_MAPPING_ANON) != 0 && !isSlab(i))
#define isUnaccepted(_mapcount) (_mapcount == (int)NUMBER(PAGE_UNACCEPTED_MAPCOUNT_VALUE) \
&& (NUMBER(PAGE_UNACCEPTED_MAPCOUNT_VALUE) != NOT_FOUND_NUMBER))
@@ -1763,8 +1763,7 @@ struct DumpInfo {
/*
* for filtering free pages managed by buddy system:
*/
- int (*page_is_buddy)(unsigned long flags, unsigned int _mapcount,
- unsigned long private, unsigned int _count);
+ int (*page_is_buddy)(const struct pginfo *);
/*
* for cyclic_splitting mode, setup splitblock_size
*/
@@ -2657,28 +2656,26 @@ isHugetlb(unsigned long dtor)
&& (SYMBOL(free_huge_page) == dtor));
}
-static inline int
-isSlab(unsigned long flags, unsigned int _mapcount)
+static inline int isSlab(const struct pginfo *i)
{
/* Linux 6.10 and later */
if (NUMBER(PAGE_SLAB_MAPCOUNT_VALUE) != NOT_FOUND_NUMBER) {
- if (_mapcount == (int)NUMBER(PAGE_SLAB_MAPCOUNT_VALUE))
+ if (i->_mapcount == (int)NUMBER(PAGE_SLAB_MAPCOUNT_VALUE))
return TRUE;
}
- return flags & (1UL << NUMBER(PG_slab));
+ return i->flags & (1UL << NUMBER(PG_slab));
}
-static inline int
-isOffline(unsigned long flags, unsigned int _mapcount)
+static inline int isOffline(const struct pginfo *i)
{
if (NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE) == NOT_FOUND_NUMBER)
return FALSE;
- if (isSlab(flags, _mapcount))
+ if (isSlab(i))
return FALSE;
- if (_mapcount == (int)NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE))
+ if (i->_mapcount == (int)NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE))
return TRUE;
return FALSE;
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 7/9] Add PG_INCLUDE_HEAD extension return status
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (5 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 6/9] Simplify arguments for page checks Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-08-07 16:24 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 8/9] Add userstack extension Stephen Brennan
` (3 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
This allows an extension to request that only the head page of a
compound page be included in the dump.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
extension.c | 2 +-
extension.h | 7 ++++---
makedumpfile.c | 9 +++++++++
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/extension.c b/extension.c
index 9b29f0c..a7360d0 100644
--- a/extension.c
+++ b/extension.c
@@ -314,7 +314,7 @@ int run_extension_callback(unsigned long pfn, const void *pcache, const struct p
for (int i = 0; i < handle_cbs_len; i++) {
if (handle_cbs[i]->cb) {
result = handle_cbs[i]->cb(pfn, pcache, inf);
- if (result == PG_INCLUDE) {
+ if (result == PG_INCLUDE || result == PG_INCLUDE_HEAD) {
ret = result;
goto out;
} else if (result == PG_EXCLUDE) {
diff --git a/extension.h b/extension.h
index 22af9a6..3edfbeb 100644
--- a/extension.h
+++ b/extension.h
@@ -4,9 +4,10 @@
struct pginfo;
enum {
- PG_INCLUDE, // Exntesion will keep the page
- PG_EXCLUDE, // Exntesion will discard the page
- PG_UNDECID, // Exntesion makes no decision
+ PG_INCLUDE, // Extension will keep the full page
+ PG_INCLUDE_HEAD, // Extension will keep just the head page
+ PG_EXCLUDE, // Extension will discard the full page
+ PG_UNDECID, // Extension makes no decision
};
int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i);
void init_extensions(void);
diff --git a/makedumpfile.c b/makedumpfile.c
index f277360..69abb17 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -6667,6 +6667,15 @@ check_order:
* retained by an extension. */
num_extension_retained += nr_pages;
continue;
+ } else if (filter_pg == PG_INCLUDE_HEAD) {
+ num_extension_retained += 1;
+ if (nr_pages == 1)
+ continue;
+
+ /* FALL THROUGH and exclude tail pages */
+ pfn++;
+ mem_map += SIZE(page);
+ nr_pages--;
}
/*
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH makedumpfile 7/9] Add PG_INCLUDE_HEAD extension return status
2026-07-14 0:45 ` [PATCH makedumpfile 7/9] Add PG_INCLUDE_HEAD extension return status Stephen Brennan
@ 2026-08-07 16:24 ` Stephen Brennan
0 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-08-07 16:24 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao
Stephen Brennan <stephen.s.brennan@oracle.com> writes:
> This allows an extension to request that only the head page of a
> compound page be included in the dump.
Hello Kazu,
Here's a more complete message which you can use to replace what's here:
---
Prior to patch "Do not call extensions for tail pages", extensions were
called for every page (including tail and head pages), yet their
decisions on tail pages were not respected when they conflicted with
makedumpfile's decision on the head page. Since that patch, we only call
extensions for head pages, and we use their decision for the entirety of
the compound page, which is at least consistent.
However, some extensions may want more fine-grained control. One such
policy may be to include only the head page, excluding tail pages. For
example, this is useful for an extension which includes just the first
page of ELF file headers within the page cache. Since page cache data
frequently uses larger folios, including just the head can avoid
unnecessary overhead.
So, add a new return status PG_INCLUDE_HEAD, which behaves as described,
including just the head of a compound page.
---
Thank you,
Stephen
> Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
> ---
> extension.c | 2 +-
> extension.h | 7 ++++---
> makedumpfile.c | 9 +++++++++
> 3 files changed, 14 insertions(+), 4 deletions(-)
>
> diff --git a/extension.c b/extension.c
> index 9b29f0c..a7360d0 100644
> --- a/extension.c
> +++ b/extension.c
> @@ -314,7 +314,7 @@ int run_extension_callback(unsigned long pfn, const void *pcache, const struct p
> for (int i = 0; i < handle_cbs_len; i++) {
> if (handle_cbs[i]->cb) {
> result = handle_cbs[i]->cb(pfn, pcache, inf);
> - if (result == PG_INCLUDE) {
> + if (result == PG_INCLUDE || result == PG_INCLUDE_HEAD) {
> ret = result;
> goto out;
> } else if (result == PG_EXCLUDE) {
> diff --git a/extension.h b/extension.h
> index 22af9a6..3edfbeb 100644
> --- a/extension.h
> +++ b/extension.h
> @@ -4,9 +4,10 @@
>
> struct pginfo;
> enum {
> - PG_INCLUDE, // Exntesion will keep the page
> - PG_EXCLUDE, // Exntesion will discard the page
> - PG_UNDECID, // Exntesion makes no decision
> + PG_INCLUDE, // Extension will keep the full page
> + PG_INCLUDE_HEAD, // Extension will keep just the head page
> + PG_EXCLUDE, // Extension will discard the full page
> + PG_UNDECID, // Extension makes no decision
> };
> int run_extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i);
> void init_extensions(void);
> diff --git a/makedumpfile.c b/makedumpfile.c
> index f277360..69abb17 100644
> --- a/makedumpfile.c
> +++ b/makedumpfile.c
> @@ -6667,6 +6667,15 @@ check_order:
> * retained by an extension. */
> num_extension_retained += nr_pages;
> continue;
> + } else if (filter_pg == PG_INCLUDE_HEAD) {
> + num_extension_retained += 1;
> + if (nr_pages == 1)
> + continue;
> +
> + /* FALL THROUGH and exclude tail pages */
> + pfn++;
> + mem_map += SIZE(page);
> + nr_pages--;
> }
>
> /*
> --
> 2.47.3
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH makedumpfile 8/9] Add userstack extension
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (6 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 7/9] Add PG_INCLUDE_HEAD extension return status Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-07-14 0:45 ` [PATCH makedumpfile 9/9] Add elfheader extension Stephen Brennan
` (2 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
Some kernel crashes are triggered by userspace. It can be helpful to
debug the user processes to understand why this occurred, but including
all userspace memory in is usually not feasible. The minimal useful
information is a stack trace, which can be pieced together from the top
of the userspace stack, and the unwind info from the executable and
DSOs. The drgn contrib/pstack.py script can be used to create such a
stack trace, assuming that the stack memory pages are available. Add
an extension which can include those memory pages into a vmcore without
including *all* of userspace memory.
To implement this extension, add helpers which walk the VMA tree for
both rbtree and maple tree implementations. Iterate each task, identify
the stack pointer, and find the corresponding VMA for the region. Build
a sorted list of anon_vmas along with the desired start and end offset.
When filtering pages, for any anon_vma, query this list and include the
desired pages.
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
extensions/Makefile | 4 +-
extensions/userstack.c | 325 ++++++++++++++++++++++++++++++++++++++++
extensions/vma_mtree.c | 140 +++++++++++++++++
extensions/vma_mtree.h | 7 +
extensions/vma_rbtree.c | 56 +++++++
extensions/vma_rbtree.h | 12 ++
6 files changed, 543 insertions(+), 1 deletion(-)
create mode 100644 extensions/userstack.c
create mode 100644 extensions/vma_mtree.c
create mode 100644 extensions/vma_mtree.h
create mode 100644 extensions/vma_rbtree.c
create mode 100644 extensions/vma_rbtree.h
diff --git a/extensions/Makefile b/extensions/Makefile
index 25e0a07..e8c41d8 100644
--- a/extensions/Makefile
+++ b/extensions/Makefile
@@ -1,5 +1,5 @@
CC ?= gcc
-CONTRIB_SO := sample.so erase_sample.so
+CONTRIB_SO := sample.so erase_sample.so userstack.so
all: $(CONTRIB_SO)
@@ -8,6 +8,8 @@ CFLAGS += -fPIC -shared -Wl,-T,../makedumpfile.ld
$(CONTRIB_SO): %.so: %.c
$(CC) $(CFLAGS) -o $@ $^
+userstack.so: vma_rbtree.c vma_mtree.c
+
clean:
rm -f $(CONTRIB_SO)
diff --git a/extensions/userstack.c b/extensions/userstack.c
new file mode 100644
index 0000000..bb1ea95
--- /dev/null
+++ b/extensions/userstack.c
@@ -0,0 +1,325 @@
+/*
+ * userstack.c: An extension for preserving userspace stack memory pages
+ *
+ * It can be useful to know what userspace tasks were doing at the time of a
+ * crash, but including all userspace memory is usually too much: usually a
+ * simple stack trace would do the trick. This extension preserves the topmost
+ * userspace stack pages for each thread in each process, making it possible
+ * to create a stack trace with a tool such as contrib/pstack.py in drgn.
+ */
+#include <assert.h>
+#include <stdbool.h>
+
+#include "../extension.h"
+#include "../makedumpfile.h"
+#include "../btf_info.h"
+#include "../kallsyms.h"
+#include "vma_mtree.h"
+#include "vma_rbtree.h"
+
+/* Required struct fields */
+INIT_MOD_STRUCT_MEMBER(vmlinux, task_struct, tasks);
+INIT_MOD_STRUCT_MEMBER(vmlinux, task_struct, signal);
+INIT_MOD_STRUCT_MEMBER(vmlinux, task_struct, thread_node);
+INIT_MOD_STRUCT_MEMBER(vmlinux, task_struct, stack);
+INIT_MOD_STRUCT_MEMBER(vmlinux, task_struct, mm);
+INIT_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, anon_vma);
+INIT_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, vm_pgoff);
+INIT_MOD_STRUCT_MEMBER(vmlinux, page, index);
+INIT_MOD_STRUCT_MEMBER(vmlinux, signal_struct, thread_head);
+INIT_MOD_STRUCT_MEMBER(vmlinux, list_head, next);
+INIT_MOD_STRUCT_MEMBER(vmlinux, pt_regs, sp);
+INIT_MOD_STRUCT(vmlinux, pt_regs);
+
+/* Optional struct fields */
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, thread_union, stack);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, mm_struct, mm_mt);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, mm_struct, mm_rb);
+
+/* Required symbols */
+INIT_MOD_SYM(vmlinux, init_task);
+
+/* Optional symbols */
+INIT_OPT_MOD_SYM(vmlinux, fred_rsp0);
+INIT_OPT_MOD_SYM(vmlinux, __start_init_stack);
+INIT_OPT_MOD_SYM(vmlinux, __end_init_stack);
+INIT_OPT_MOD_SYM(vmlinux, __start_init_task);
+INIT_OPT_MOD_SYM(vmlinux, __end_init_task);
+
+unsigned long THREAD_SIZE;
+bool ready;
+
+#define MEMBER_OFF(S, M) \
+ (GET_MOD_STRUCT_MEMBER_MOFF(vmlinux, S, M) / 8)
+
+
+static int for_each_task(bool (*task_fn)(unsigned long))
+{
+ unsigned long curr_proc = GET_MOD_SYM(vmlinux, init_task);
+ do {
+ unsigned long signal;
+ if (!readmem(VADDR, curr_proc + MEMBER_OFF(task_struct, signal),
+ &signal, sizeof(signal)))
+ return FALSE;
+
+ unsigned long thread_head = signal + MEMBER_OFF(signal_struct, thread_head);
+ unsigned long next;
+ if (!readmem(VADDR, thread_head + MEMBER_OFF(list_head, next), &next, sizeof(next)))
+ return FALSE;
+
+ while (next != thread_head) {
+ unsigned long curr_thread = next - MEMBER_OFF(task_struct, thread_node);
+
+ if (!task_fn(curr_thread))
+ return FALSE;
+
+ if (!readmem(VADDR,
+ curr_thread + MEMBER_OFF(task_struct, thread_node) + MEMBER_OFF(list_head, next),
+ &next, sizeof(next)))
+ return FALSE;
+ }
+
+ if (!readmem(VADDR, curr_proc + MEMBER_OFF(task_struct, tasks) + MEMBER_OFF(list_head, next),
+ &next, sizeof(next)))
+ return FALSE;
+ curr_proc = next - MEMBER_OFF(task_struct, tasks);
+
+ } while (curr_proc != GET_MOD_SYM(vmlinux, init_task));
+
+ return TRUE;
+}
+
+static unsigned long task_sp(unsigned long taskp)
+{
+ // The stack pointer is stored on entry to the kernel at the top of the
+ // kernel stack. If the task in on-cpu, the stack pointer will be in the
+ // PRSTATUS, but the stale value is very likely to be useful enough.
+ unsigned long user_sp_loc;
+ if (!readmem(VADDR, taskp + MEMBER_OFF(task_struct, stack),
+ &user_sp_loc, sizeof(user_sp_loc)))
+ return 0;
+
+ user_sp_loc += THREAD_SIZE;
+ user_sp_loc -= GET_MOD_STRUCT_SSIZE(vmlinux, pt_regs);
+ if (MOD_SYM_EXIST(vmlinux, fred_rsp0))
+ user_sp_loc -= 16;
+ user_sp_loc += MEMBER_OFF(pt_regs, sp);
+
+ unsigned long sp;
+ if (!readmem(VADDR, user_sp_loc, &sp, sizeof(sp)))
+ return 0;
+ return sp;
+}
+
+struct task_stack {
+ unsigned long anon_vma;
+ unsigned long index_start;
+ unsigned long index_end;
+};
+
+static struct task_stack *stacks;
+static size_t stacks_count;
+static size_t stacks_alloc;
+
+static bool append_task_stack(struct task_stack *newstack)
+{
+ if (stacks_count == stacks_alloc) {
+ if (stacks_alloc)
+ stacks_alloc *= 2;
+ else
+ stacks_alloc = 512;
+ struct task_stack *newarr = realloc(stacks, stacks_alloc * sizeof(stacks[0]));
+ if (!newarr) {
+ return FALSE;
+ }
+ stacks = newarr;
+ }
+ stacks[stacks_count++] = *newstack;
+ return TRUE;
+}
+
+static bool record_task_stack(unsigned long taskp)
+{
+ unsigned long task_mm;
+ if (!readmem(VADDR, taskp + MEMBER_OFF(task_struct, mm), &task_mm, sizeof(task_mm)))
+ /* Propagate failure to read a value from the task_struct */
+ return FALSE;
+ if (!task_mm)
+ /* NULL task_mm is expected, continue */
+ return TRUE;
+
+ unsigned long sp = task_sp(taskp);
+ if (!sp)
+ /* Propagate error reading SP */
+ return FALSE;
+
+ int ret;
+ unsigned long vma = 0;
+ if (MOD_STRUCT_MEMBER_EXIST(vmlinux, mm_struct, mm_mt))
+ ret = find_vma_mtree(task_mm + MEMBER_OFF(mm_struct, mm_mt), sp, &vma);
+ else if (MOD_STRUCT_MEMBER_EXIST(vmlinux, mm_struct, mm_rb))
+ ret = find_vma_rbtree(task_mm + MEMBER_OFF(mm_struct, mm_rb), sp, &vma);
+ else
+ assert(FALSE); /* should be impossible */
+ if (!ret)
+ /* propagate error from find_vma_xxx() */
+ return FALSE;
+ else if (!vma)
+ /* No VMA found for the stack. This is unexpected, but gracefully
+ * handle the condition and continue. */
+ return TRUE;
+
+ unsigned long vm_start, vm_end, anon_vma, vm_pgoff;
+ if (!readmem(VADDR, vma + MEMBER_OFF(vm_area_struct, anon_vma), &anon_vma, sizeof(anon_vma)))
+ return FALSE;
+
+ if (!anon_vma)
+ /* Not an anonymous VMA. This is unexpected but valid. Move on to
+ * the next task. */
+ return TRUE;
+
+ if (!readmem(VADDR, vma + MEMBER_OFF(vm_area_struct, vm_start), &vm_start, sizeof(vm_start)) ||
+ !readmem(VADDR, vma + MEMBER_OFF(vm_area_struct, vm_end), &vm_end, sizeof(vm_end)) ||
+ !readmem(VADDR, vma + MEMBER_OFF(vm_area_struct, vm_pgoff), &vm_pgoff, sizeof(vm_pgoff)))
+ return FALSE;
+
+ /* Construct a range of indices we would like to retain. This is the
+ * range of stack pages starting with the stack pointer, and continuing
+ * to the top of the stack vma, or until a limit of 128 pages per task
+ * is reached. */
+ unsigned long pgoff_start = (sp - vm_start) >> PAGESHIFT();
+ pgoff_start += vm_pgoff;
+ unsigned long pgoff_end = (vm_end - vm_start) >> PAGESHIFT();
+ pgoff_end += vm_pgoff;
+ if (pgoff_start + 128 < pgoff_end)
+ pgoff_end = pgoff_start + 128;
+
+ struct task_stack stack = {anon_vma | 1, pgoff_start, pgoff_end};
+ if (!append_task_stack(&stack))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int stack_compar(const void *lhs, const void *rhs)
+{
+ const struct task_stack *lhss = lhs, *rhss = rhs;
+ if (lhss->anon_vma < rhss->anon_vma)
+ return -1;
+ else if (lhss->anon_vma > rhss->anon_vma)
+ return 1;
+ else
+ return 0;
+}
+
+void extension_init(void)
+{
+ if (MOD_STRUCT_MEMBER_EXIST(vmlinux, mm_struct, mm_mt)) {
+ if (!vma_mtree_init())
+ return;
+ } else if (MOD_STRUCT_MEMBER_EXIST(vmlinux, mm_struct, mm_rb)) {
+ if (!vma_rbtree_init())
+ return;
+ } else {
+ ERRMSG("error: Neither mtree nor rbtree available for VMA walking\n");
+ return;
+ }
+ if (!MOD_STRUCT_EXIST(vmlinux, pt_regs)) {
+ ERRMSG("error: missing pt_regs incfo\n");
+ return;
+ }
+ if (!MOD_SYM_EXIST(vmlinux, init_task)) {
+ ERRMSG("error: missing init_task symbol\n");
+ return;
+ }
+
+ // Determine THREAD_SIZE, which is necessary to find the offset of the
+ // userspace stack pointer register from the kernel thread stack.
+ //
+ // - Prior to v4.16, 0500871f21b23 ("Construct init thread stack in the
+ // linker script rather than by union"), it was found in thread_union.
+ // - Between v4.16 and v6.10, 8f69cba096b5c ("x86: Rename
+ // __{start,end}_init_task to __{start,end}_init_stack"), the stack
+ // size can be inferred by the __{start,end}_init_task symbols.
+ // - Since v6.10, the size is inferred by __{start,end}_init_stack.
+ if (MOD_STRUCT_MEMBER_EXIST(vmlinux, thread_union, stack)) {
+ THREAD_SIZE = GET_MOD_STRUCT_MEMBER_MSIZE(vmlinux, thread_union, stack);
+ } else if (MOD_SYM_EXIST(vmlinux, __start_init_stack) &&
+ MOD_SYM_EXIST(vmlinux, __end_init_stack) &&
+ GET_MOD_SYM(vmlinux, __end_init_stack) > GET_MOD_SYM(vmlinux, __start_init_stack)) {
+ THREAD_SIZE = GET_MOD_SYM(vmlinux, __end_init_stack) - GET_MOD_SYM(vmlinux, __start_init_stack);
+ } else if (MOD_SYM_EXIST(vmlinux, __start_init_task) &&
+ MOD_SYM_EXIST(vmlinux, __end_init_task) &&
+ GET_MOD_SYM(vmlinux, __end_init_task) > GET_MOD_SYM(vmlinux, __start_init_task)) {
+ THREAD_SIZE = GET_MOD_SYM(vmlinux, __end_init_task) - GET_MOD_SYM(vmlinux, __start_init_task);
+ } else {
+ ERRMSG("Could not determine THREAD_SIZE: neither __start_init_stack "
+ "nor __start_init_task found in kallsyms, nor is thread_union "
+ "found in BTF.\n");
+ return;
+ }
+
+ if (!for_each_task(&record_task_stack)) {
+ free(stacks);
+ stacks = NULL;
+ stacks_alloc = stacks_count = 0;
+ ERRMSG("Could not gather all task stack VMAs, userstack disabled\n");
+ return;
+ }
+ struct task_stack *tmp = realloc(stacks, stacks_count * sizeof(*tmp));
+ if (tmp) {
+ stacks = tmp;
+ stacks_alloc = stacks_count;
+ }
+ qsort(stacks, stacks_count, sizeof(*stacks), &stack_compar);
+ ready = TRUE;
+}
+
+static int count_retained;
+static int count_checked;
+static int count_cached;
+int extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i)
+{
+ unsigned long index;
+ static struct {
+ unsigned long mapping;
+ struct task_stack *result;
+ } cache;
+
+ if (!ready || !isAnon(i))
+ return PG_UNDECID;
+
+ index = ULONG(pcache + MEMBER_OFF(page, index));
+ if (!(i->mapping & 1))
+ return PG_UNDECID;
+
+ if (i->mapping != cache.mapping) {
+ count_checked++;
+ struct task_stack search = {i->mapping, 0, 0};
+ struct task_stack *result = bsearch(&search, stacks, stacks_count,
+ sizeof(search), &stack_compar);
+ if (!result)
+ return PG_UNDECID;
+
+ cache.mapping = i->mapping;
+ cache.result = result;
+ } else {
+ count_cached++;
+ }
+
+ if (cache.result->index_start <= index && index < cache.result->index_end) {
+ count_retained++;
+ return PG_INCLUDE;
+ } else {
+ return PG_UNDECID;
+ }
+}
+
+__attribute__((destructor))
+static void userstack_exit(void) {
+ if (count_retained || count_checked || count_cached || stacks_count) {
+ REPORT_MSG("Extension userstack:\n");
+ REPORT_MSG(" PFNs retained: %d searched: %d, cached: %d\n", count_retained, count_checked, count_cached);
+ REPORT_MSG(" Recorded %zu stack anon_vmas\n", stacks_count);
+ }
+}
diff --git a/extensions/vma_mtree.c b/extensions/vma_mtree.c
new file mode 100644
index 0000000..ac8912e
--- /dev/null
+++ b/extensions/vma_mtree.c
@@ -0,0 +1,140 @@
+#include <stdbool.h>
+#include "../btf_info.h"
+#include "../kallsyms.h"
+#include "../makedumpfile.h"
+
+INIT_OPT_MOD_STRUCT(vmlinux, maple_tree);
+INIT_OPT_MOD_STRUCT(vmlinux, maple_node);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_tree, ma_root);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_arange_64, pivot);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_arange_64, slot);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_arange_64, meta);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_range_64, pivot);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_range_64, slot);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_range_64, meta);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, maple_metadata, end);
+
+#define MEMBER_OFF(S, M) \
+ GET_MOD_STRUCT_MEMBER_MOFF(vmlinux, S, M) / 8
+#define KERN_STRUCT_MEMBER_EXIST(S, M) \
+ MOD_STRUCT_MEMBER_EXIST(vmlinux, S, M)
+#define GET_KERN_SYM(SYM) GET_MOD_SYM(vmlinux, SYM)
+#define KERN_SYM_EXIST(SYM) MOD_SYM_EXIST(vmlinux, SYM)
+#define GET_KERN_STRUCT_SSIZE(S) \
+ GET_MOD_STRUCT_SSIZE(vmlinux, S)
+#define KERN_STRUCT_EXIST(SYM) MOD_STRUCT_EXIST(vmlinux, SYM)
+
+#define MAPLE_NODE_MASK 255UL
+#define MAPLE_NODE_TYPE_MASK 0x0F
+#define MAPLE_NODE_TYPE_SHIFT 0x03
+#define XA_ZERO_ENTRY xa_mk_internal(257)
+
+static unsigned long xa_mk_internal(unsigned long v)
+{
+ return (v << 2) | 2;
+}
+
+static bool xa_is_internal(unsigned long entry)
+{
+ return (entry & 3) == 2;
+}
+
+static bool xa_is_node(unsigned long entry)
+{
+ return xa_is_internal(entry) && entry > 4096;
+}
+
+bool vma_mtree_init(void)
+{
+ if (!KERN_STRUCT_EXIST(maple_tree) ||
+ !KERN_STRUCT_EXIST(maple_node) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_tree, ma_root) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_arange_64, pivot) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_arange_64, slot) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_arange_64, meta) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_range_64, pivot) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_range_64, slot) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_range_64, meta) ||
+ !KERN_STRUCT_MEMBER_EXIST(maple_metadata, end)) {
+ ERRMSG("Missing required maple tree syms/types\n");
+ return false;
+ }
+
+ return true;
+}
+
+int find_vma_mtree(unsigned long mt, unsigned long index, unsigned long *ret)
+{
+ unsigned long long entry;
+
+ if (!readmem(VADDR, mt + MEMBER_OFF(maple_tree, ma_root), &entry, sizeof(entry)))
+ return FALSE;
+
+ if (!xa_is_node(entry)) {
+ if (index == 0)
+ *ret = entry;
+ else
+ *ret = 0;
+ return TRUE;
+ }
+ unsigned long long max = ULONGLONG_MAX;
+ void *node = malloc(GET_KERN_STRUCT_SSIZE(maple_node));
+ if (!node) {
+ ERRMSG("failed to allocate memory\n");
+ return FALSE;
+ }
+
+ for (;;) {
+ if (!readmem(VADDR, entry & ~MAPLE_NODE_MASK, node, GET_KERN_STRUCT_SSIZE(maple_node))) {
+ ERRMSG("failed to read maple node: %llx\n", entry & ~MAPLE_NODE_MASK);
+ free(node);
+ return FALSE;
+ }
+
+ int node_type = (entry >> MAPLE_NODE_TYPE_SHIFT) & MAPLE_NODE_TYPE_MASK;
+ unsigned long long *pivot, *slot;
+ uint8_t end;
+ if (node_type == 3) {
+ pivot = node + MEMBER_OFF(maple_arange_64, pivot);
+ slot = node + MEMBER_OFF(maple_arange_64, slot);
+ end = ((uint8_t *)node)[MEMBER_OFF(maple_arange_64, meta) + MEMBER_OFF(maple_metadata, end)];
+ } else if (node_type == 1 || node_type == 2) {
+ pivot = node + MEMBER_OFF(maple_range_64, pivot);
+ slot = node + MEMBER_OFF(maple_range_64, slot);
+ unsigned long long p = *(slot - 1);
+ if (!p)
+ end = ((uint8_t *)node)[MEMBER_OFF(maple_range_64, meta) + MEMBER_OFF(maple_metadata, end)];
+ else {
+ end = slot - pivot;
+ if (p == max)
+ end--;
+ }
+ } else {
+ ERRMSG("unrecognized maple node type: %d\n", node_type);
+ free(node);
+ return FALSE;
+ }
+ int offset = 0;
+ for (offset = 0; offset < end; offset++) {
+ if (pivot[offset] >= index) {
+ max = pivot[offset];
+ break;
+ }
+ }
+ if (&pivot[offset] >= slot)
+ offset = end;
+
+ entry = slot[offset];
+ if (node_type == 1) {
+ // leaf:
+ free(node);
+ if (entry == XA_ZERO_ENTRY)
+ *ret = 0;
+ else
+ *ret = entry;
+ return TRUE;
+ }
+ }
+ *ret = 0;
+ return TRUE;
+}
diff --git a/extensions/vma_mtree.h b/extensions/vma_mtree.h
new file mode 100644
index 0000000..69d9448
--- /dev/null
+++ b/extensions/vma_mtree.h
@@ -0,0 +1,7 @@
+#ifndef _MAPLE_TREE_H
+#define _MAPLE_TREE_H
+#include <stdbool.h>
+bool vma_mtree_init(void);
+int find_vma_mtree(unsigned long mt, unsigned long address, unsigned long *ret);
+#endif /* _MAPLE_TREE_H */
+
diff --git a/extensions/vma_rbtree.c b/extensions/vma_rbtree.c
new file mode 100644
index 0000000..c947fbc
--- /dev/null
+++ b/extensions/vma_rbtree.c
@@ -0,0 +1,56 @@
+#include "../makedumpfile.h"
+#include "../btf_info.h"
+#include "vma_rbtree.h"
+
+INIT_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, vm_start);
+INIT_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, vm_end);
+
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, vm_rb);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, rb_root, rb_node);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, rb_node, rb_left);
+INIT_OPT_MOD_STRUCT_MEMBER(vmlinux, rb_node, rb_right);
+
+#define MEMBER_OFF(S, M) \
+ (GET_MOD_STRUCT_MEMBER_MOFF(vmlinux, S, M) / 8)
+
+#define MEMBER_EXIST(S, M) \
+ (MOD_STRUCT_MEMBER_EXIST(vmlinux, S, M))
+
+int find_vma_rbtree(unsigned long rb_root, unsigned long address, unsigned long *ret)
+{
+ unsigned long node, vma, vm_start, vm_end, rb_left, rb_right;
+ if (!readmem(VADDR, rb_root, &node, sizeof(node)))
+ return FALSE;
+
+ *ret = 0;
+ while (node > MEMBER_OFF(vm_area_struct, vm_rb)) {
+ vma = node - MEMBER_OFF(vm_area_struct, vm_rb);
+ if (!readmem(VADDR, vma + MEMBER_OFF(vm_area_struct, vm_start), &vm_start, sizeof(vm_start)) ||
+ !readmem(VADDR, vma + MEMBER_OFF(vm_area_struct, vm_end), &vm_end, sizeof(vm_end)) ||
+ !readmem(VADDR, node + MEMBER_OFF(rb_node, rb_left), &rb_left, sizeof(rb_left)) ||
+ !readmem(VADDR, node + MEMBER_OFF(rb_node, rb_right), &rb_right, sizeof(rb_right)))
+ return FALSE;
+
+ if (address < vm_start) {
+ node = rb_left;
+ } else if (address >= vm_end) {
+ node = rb_right;
+ } else {
+ *ret = vma;
+ break;
+ }
+ }
+ return TRUE;
+}
+
+bool vma_rbtree_init(void)
+{
+ if (!MEMBER_EXIST(vm_area_struct, vm_rb) ||
+ !MEMBER_EXIST(rb_root, rb_node) ||
+ !MEMBER_EXIST(rb_node, rb_left) ||
+ !MEMBER_EXIST(rb_node, rb_right)) {
+ ERRMSG("error: missing required vm_area_struct & rbtree definitions");
+ return false;
+ }
+ return true;
+}
diff --git a/extensions/vma_rbtree.h b/extensions/vma_rbtree.h
new file mode 100644
index 0000000..a8b8d4f
--- /dev/null
+++ b/extensions/vma_rbtree.h
@@ -0,0 +1,12 @@
+#ifndef RBTREE_H_
+#define RBTREE_H_
+#include <stdbool.h>
+
+#include "../btf_info.h"
+
+DECLARE_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, vm_start);
+DECLARE_MOD_STRUCT_MEMBER(vmlinux, vm_area_struct, vm_end);
+
+int find_vma_rbtree(unsigned long rb_root, unsigned long address, unsigned long *ret);
+bool vma_rbtree_init(void);
+#endif // RBTREE_H_
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH makedumpfile 9/9] Add elfheader extension
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (7 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 8/9] Add userstack extension Stephen Brennan
@ 2026-07-14 0:45 ` Stephen Brennan
2026-08-03 15:55 ` [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
2026-08-07 1:52 ` HAGIO KAZUHITO(萩尾 一仁)
10 siblings, 0 replies; 18+ messages in thread
From: Stephen Brennan @ 2026-07-14 0:45 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao, Stephen Brennan
This extension preserves the resident ELF header pages from the page
cache, which are mapped by userspace processes. It significantly
increases the chances that an ELF file can be identified by its build ID
and thus a userspace stack trace could be extracted from a vmcore (used
in combination with the userstack.so extension).
Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
---
extensions/Makefile | 2 +-
| 93 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 94 insertions(+), 1 deletion(-)
create mode 100644 extensions/elfheader.c
diff --git a/extensions/Makefile b/extensions/Makefile
index e8c41d8..c7918c7 100644
--- a/extensions/Makefile
+++ b/extensions/Makefile
@@ -1,5 +1,5 @@
CC ?= gcc
-CONTRIB_SO := sample.so erase_sample.so userstack.so
+CONTRIB_SO := sample.so erase_sample.so userstack.so elfheader.so
all: $(CONTRIB_SO)
--git a/extensions/elfheader.c b/extensions/elfheader.c
new file mode 100644
index 0000000..ccb17fa
--- /dev/null
+++ b/extensions/elfheader.c
@@ -0,0 +1,93 @@
+/*
+ * elfheader.c: Preserve resident ELF header pages from page cache.
+ *
+ * In order to accurately unwind userspace stacks, user debuginfo may be
+ * necessary. At a minimum, it is needed to translate addresses to symbols or
+ * function names, but it may also be needed for unwinding when frame pointers
+ * are not in use. Normally, userspace core dumps will retain the first page of
+ * mapped executable files, since they usually contain the build ID necessary
+ * for identifying the executable and debuginfo. This extension takes
+ * inspiration from this approach, with modifications suitable for the kdump
+ * environment:
+ * - Include non-anonymous page cache pages corresponding to executable files
+ * - Only include the page at index 0
+ * - Only include pages which start with the ELF magic header
+ *
+ * None of this guarantees that all ELF headers will be included. They may not
+ * be resident in memory. And even if the first page is included, there's no
+ * guarantee that the build ID resides in the first page. However, the best
+ * effort is already good enough to make many userspace stacks intelligible with
+ * minimal extra dump time or space.
+ */
+#include <limits.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "../extension.h"
+#include "../makedumpfile.h"
+#include "../btf_info.h"
+
+INIT_MOD_STRUCT_MEMBER(vmlinux, page, index);
+INIT_MOD_STRUCT_MEMBER(vmlinux, address_space, host);
+INIT_MOD_STRUCT_MEMBER(vmlinux, inode, i_mode);
+
+#define MEMBER_OFF(S, M) \
+ (GET_MOD_STRUCT_MEMBER_MOFF(vmlinux, S, M) / 8)
+
+static int retained;
+static int checked;
+
+void extension_init(void)
+{
+}
+
+int extension_callback(unsigned long pfn, const void *pcache, const struct pginfo *i)
+{
+ unsigned long index;
+ unsigned long inode;
+ unsigned short mode;
+ char elfmag[SELFMAG];
+
+ /* Only consider non-anonymous file cache pages */
+ if (!(is_cache_page(i->flags) && !isAnon(i)))
+ return PG_UNDECID;
+
+ /* Only consider the first page in the file */
+ index = ULONG(pcache + MEMBER_OFF(page, index));
+ if (index != 0)
+ return PG_UNDECID;
+
+ /* Only retain pages which are actually mapped to userspace */
+ if (OFFSET(page._mapcount) != NOT_FOUND_STRUCTURE &&
+ i->_mapcount == UINT_MAX)
+ return PG_UNDECID;
+
+ /* Only retain pages for executable inodes */
+ checked++;
+ if (!readmem(VADDR, i->mapping + MEMBER_OFF(address_space, host),
+ &inode, sizeof(inode)) || !inode)
+ return PG_UNDECID;
+ if (!readmem(VADDR, inode + MEMBER_OFF(inode, i_mode),
+ &mode, sizeof(mode)))
+ return PG_UNDECID;
+ if (!(mode & 0111))
+ return PG_UNDECID;
+
+ /* Only retain the page if its content looks like ELF */
+ if (!readmem(PADDR, pfn_to_paddr(pfn), elfmag, sizeof(elfmag)) ||
+ memcmp(elfmag, ELFMAG, SELFMAG))
+ return PG_UNDECID;
+
+ retained++;
+ return PG_INCLUDE_HEAD;
+}
+
+__attribute__((destructor))
+static void elfheader_exit(void)
+{
+ if (checked || retained) {
+ REPORT_MSG("Extension elfheader:\n");
+ REPORT_MSG(" ELF headers retained: %d (candidates checked: %d)\n",
+ retained, checked);
+ }
+}
--
2.47.3
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (8 preceding siblings ...)
2026-07-14 0:45 ` [PATCH makedumpfile 9/9] Add elfheader extension Stephen Brennan
@ 2026-08-03 15:55 ` Stephen Brennan
2026-08-04 4:43 ` Tao Liu
2026-08-07 1:52 ` HAGIO KAZUHITO(萩尾 一仁)
10 siblings, 1 reply; 18+ messages in thread
From: Stephen Brennan @ 2026-08-03 15:55 UTC (permalink / raw)
To: yamazaki-msmt, k-hagio-ab, kexec; +Cc: ltao
Hello all,
Just a gentle ping on this series.
Thanks,
Stephen
Stephen Brennan <stephen.s.brennan@oracle.com> writes:
> Hello all,
>
> Building on Tao Liu's excellent work for makedumpfile extensions, I'd like to
> share some incremental improvements to the extension system, as well as my own
> two extensions which enable userspace stack tracing. There are three topics
> related to the core makedumpfile extension system which are addressed here:
>
> 1. Tail pages handling. As patch 1 explains, extensions currently get called for
> tail pages, but their decisions are not respected. I believe the best approach
> is to only call extensions for the head page, but allow extensions to return a
> decision related to just a subset of the pages. To this end, patch 7 adds
> PG_INCLUDE_HEAD to include just the head page (leaving the rest up to the
> default policy), and patch 9 shows the elfheader extension which uses that
> functionality.
>
> 2. Counting pages included by extensions. This is done by patch 4. It alters the
> logic because it's really helpful to know whether a page would have been
> included by makedumpfile's dump-level filtering anyway, so we only count the
> additional pages.
>
> 3. Sharing makedumpfile's logic and knowledge about page metadata. Currently,
> extensions just get a "pcache" pointer, which means they must redo whatever
> logic makedumpfile has already done, in order to get information like
> compound_order. Patches 3, 5, and 6 factor out the page metadata into a new
> struct pginfo, which gets passed to extensions, and it also makes more of the
> helpers like "isSlab()" usable with this pointer.
>
> With those improvements, the final two patches (8 & 9) implement a pair of
> extensions that are useful for including a minimal amount of information which
> can be used to do stack traces of the userspace processes in a vmcore. The
> "userstack" extension includes the stack memory itself for each thread, and the
> "elfheader" extension includes the first page of mapped ELF files, so that ELF
> build IDs can be identified from the vmcore.
>
> My testing so far:
> - Live testing (/proc/kcore) x86_64 on Oracle UEK 8 (6.12.x based)
> - Refiltering on vmcores generated from UEK8, 7 (5.15.x based), 6 (5.4.x based)
> - Using the resulting filtered vmcores with drgn's contrib/pstack.py to generate
> userspace stack traces.
>
> I still have significantly more testing to do and then share out:
> - Testing in kexec environment
> - Testing with upstream kernels, also verifying in a Fedora userspace
> - Sharing runtime and size comparisons with different workloads
>
> Despite the limited testing, I wanted to share these patches as they are,
> because I think the improvements for the core extensions framework are useful
> already, and don't require as much verification.
>
> For the extensions themselves, I would like to consider merging them as well, if
> there is interest. I think it would be useful for makedumpfile to contain shared
> extensions, including those for the GPU buffers which Tao Liu was working on, as
> well as the userspace stack tracing ones which I'm sharing.
>
> Thanks,
> Stephen
>
> Stephen Brennan (9):
> Do not call extensions for tail pages
> Honor CFLAGS in extension/Makefile
> Share page information with extension callbacks
> Introduce a stat for pages retained by extension
> Move page checks into makedumpfile.h
> Simplify arguments for page checks
> Add PG_INCLUDE_HEAD extension return status
> Add userstack extension
> Add elfheader extension
>
> extension.c | 10 +-
> extension.h | 10 +-
> extensions/Makefile | 8 +-
> extensions/elfheader.c | 93 ++++++++++++
> extensions/userstack.c | 325 ++++++++++++++++++++++++++++++++++++++++
> extensions/vma_mtree.c | 140 +++++++++++++++++
> extensions/vma_mtree.h | 7 +
> extensions/vma_rbtree.c | 56 +++++++
> extensions/vma_rbtree.h | 12 ++
> makedumpfile.c | 188 +++++++++--------------
> makedumpfile.h | 78 +++++++++-
> 11 files changed, 798 insertions(+), 129 deletions(-)
> create mode 100644 extensions/elfheader.c
> create mode 100644 extensions/userstack.c
> create mode 100644 extensions/vma_mtree.c
> create mode 100644 extensions/vma_mtree.h
> create mode 100644 extensions/vma_rbtree.c
> create mode 100644 extensions/vma_rbtree.h
>
> --
> 2.47.3
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-08-03 15:55 ` [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
@ 2026-08-04 4:43 ` Tao Liu
0 siblings, 0 replies; 18+ messages in thread
From: Tao Liu @ 2026-08-04 4:43 UTC (permalink / raw)
To: Stephen Brennan; +Cc: yamazaki-msmt, k-hagio-ab, kexec
Hi Stephen,
On Tue, Aug 4, 2026 at 4:14 AM Stephen Brennan
<stephen.s.brennan@oracle.com> wrote:
>
> Hello all,
>
> Just a gentle ping on this series.
Sorry I was busy recently due to some internal team issues. Anyway I
will review the patchset, it's on my queue.
Thanks,
Tao Liu
>
> Thanks,
> Stephen
>
> Stephen Brennan <stephen.s.brennan@oracle.com> writes:
> > Hello all,
> >
> > Building on Tao Liu's excellent work for makedumpfile extensions, I'd like to
> > share some incremental improvements to the extension system, as well as my own
> > two extensions which enable userspace stack tracing. There are three topics
> > related to the core makedumpfile extension system which are addressed here:
> >
> > 1. Tail pages handling. As patch 1 explains, extensions currently get called for
> > tail pages, but their decisions are not respected. I believe the best approach
> > is to only call extensions for the head page, but allow extensions to return a
> > decision related to just a subset of the pages. To this end, patch 7 adds
> > PG_INCLUDE_HEAD to include just the head page (leaving the rest up to the
> > default policy), and patch 9 shows the elfheader extension which uses that
> > functionality.
> >
> > 2. Counting pages included by extensions. This is done by patch 4. It alters the
> > logic because it's really helpful to know whether a page would have been
> > included by makedumpfile's dump-level filtering anyway, so we only count the
> > additional pages.
> >
> > 3. Sharing makedumpfile's logic and knowledge about page metadata. Currently,
> > extensions just get a "pcache" pointer, which means they must redo whatever
> > logic makedumpfile has already done, in order to get information like
> > compound_order. Patches 3, 5, and 6 factor out the page metadata into a new
> > struct pginfo, which gets passed to extensions, and it also makes more of the
> > helpers like "isSlab()" usable with this pointer.
> >
> > With those improvements, the final two patches (8 & 9) implement a pair of
> > extensions that are useful for including a minimal amount of information which
> > can be used to do stack traces of the userspace processes in a vmcore. The
> > "userstack" extension includes the stack memory itself for each thread, and the
> > "elfheader" extension includes the first page of mapped ELF files, so that ELF
> > build IDs can be identified from the vmcore.
> >
> > My testing so far:
> > - Live testing (/proc/kcore) x86_64 on Oracle UEK 8 (6.12.x based)
> > - Refiltering on vmcores generated from UEK8, 7 (5.15.x based), 6 (5.4.x based)
> > - Using the resulting filtered vmcores with drgn's contrib/pstack.py to generate
> > userspace stack traces.
> >
> > I still have significantly more testing to do and then share out:
> > - Testing in kexec environment
> > - Testing with upstream kernels, also verifying in a Fedora userspace
> > - Sharing runtime and size comparisons with different workloads
> >
> > Despite the limited testing, I wanted to share these patches as they are,
> > because I think the improvements for the core extensions framework are useful
> > already, and don't require as much verification.
> >
> > For the extensions themselves, I would like to consider merging them as well, if
> > there is interest. I think it would be useful for makedumpfile to contain shared
> > extensions, including those for the GPU buffers which Tao Liu was working on, as
> > well as the userspace stack tracing ones which I'm sharing.
> >
> > Thanks,
> > Stephen
> >
> > Stephen Brennan (9):
> > Do not call extensions for tail pages
> > Honor CFLAGS in extension/Makefile
> > Share page information with extension callbacks
> > Introduce a stat for pages retained by extension
> > Move page checks into makedumpfile.h
> > Simplify arguments for page checks
> > Add PG_INCLUDE_HEAD extension return status
> > Add userstack extension
> > Add elfheader extension
> >
> > extension.c | 10 +-
> > extension.h | 10 +-
> > extensions/Makefile | 8 +-
> > extensions/elfheader.c | 93 ++++++++++++
> > extensions/userstack.c | 325 ++++++++++++++++++++++++++++++++++++++++
> > extensions/vma_mtree.c | 140 +++++++++++++++++
> > extensions/vma_mtree.h | 7 +
> > extensions/vma_rbtree.c | 56 +++++++
> > extensions/vma_rbtree.h | 12 ++
> > makedumpfile.c | 188 +++++++++--------------
> > makedumpfile.h | 78 +++++++++-
> > 11 files changed, 798 insertions(+), 129 deletions(-)
> > create mode 100644 extensions/elfheader.c
> > create mode 100644 extensions/userstack.c
> > create mode 100644 extensions/vma_mtree.c
> > create mode 100644 extensions/vma_mtree.h
> > create mode 100644 extensions/vma_rbtree.c
> > create mode 100644 extensions/vma_rbtree.h
> >
> > --
> > 2.47.3
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-07-14 0:45 [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
` (9 preceding siblings ...)
2026-08-03 15:55 ` [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension Stephen Brennan
@ 2026-08-07 1:52 ` HAGIO KAZUHITO(萩尾 一仁)
2026-08-07 7:57 ` Tao Liu
10 siblings, 1 reply; 18+ messages in thread
From: HAGIO KAZUHITO(萩尾 一仁) @ 2026-08-07 1:52 UTC (permalink / raw)
To: Stephen Brennan, ltao@redhat.com
Cc: YAMAZAKI MASAMITSU(山崎 真光),
kexec@lists.infradead.org
On 2026/07/14 9:45, Stephen Brennan wrote:
> Hello all,
>
> Building on Tao Liu's excellent work for makedumpfile extensions, I'd like to
> share some incremental improvements to the extension system, as well as my own
> two extensions which enable userspace stack tracing. There are three topics
> related to the core makedumpfile extension system which are addressed here:
>
> 1. Tail pages handling. As patch 1 explains, extensions currently get called for
> tail pages, but their decisions are not respected. I believe the best approach
> is to only call extensions for the head page, but allow extensions to return a
> decision related to just a subset of the pages. To this end, patch 7 adds
> PG_INCLUDE_HEAD to include just the head page (leaving the rest up to the
> default policy), and patch 9 shows the elfheader extension which uses that
> functionality.
>
> 2. Counting pages included by extensions. This is done by patch 4. It alters the
> logic because it's really helpful to know whether a page would have been
> included by makedumpfile's dump-level filtering anyway, so we only count the
> additional pages.
>
> 3. Sharing makedumpfile's logic and knowledge about page metadata. Currently,
> extensions just get a "pcache" pointer, which means they must redo whatever
> logic makedumpfile has already done, in order to get information like
> compound_order. Patches 3, 5, and 6 factor out the page metadata into a new
> struct pginfo, which gets passed to extensions, and it also makes more of the
> helpers like "isSlab()" usable with this pointer.
>
> With those improvements, the final two patches (8 & 9) implement a pair of
> extensions that are useful for including a minimal amount of information which
> can be used to do stack traces of the userspace processes in a vmcore. The
> "userstack" extension includes the stack memory itself for each thread, and the
> "elfheader" extension includes the first page of mapped ELF files, so that ELF
> build IDs can be identified from the vmcore.
>
> My testing so far:
> - Live testing (/proc/kcore) x86_64 on Oracle UEK 8 (6.12.x based)
> - Refiltering on vmcores generated from UEK8, 7 (5.15.x based), 6 (5.4.x based)
> - Using the resulting filtered vmcores with drgn's contrib/pstack.py to generate
> userspace stack traces.
>
> I still have significantly more testing to do and then share out:
> - Testing in kexec environment
> - Testing with upstream kernels, also verifying in a Fedora userspace
> - Sharing runtime and size comparisons with different workloads
>
> Despite the limited testing, I wanted to share these patches as they are,
> because I think the improvements for the core extensions framework are useful
> already, and don't require as much verification.
>
> For the extensions themselves, I would like to consider merging them as well, if
> there is interest. I think it would be useful for makedumpfile to contain shared
> extensions, including those for the GPU buffers which Tao Liu was working on, as
> well as the userspace stack tracing ones which I'm sharing.
Hi Stephen, (and Tao)
sorry for my slow review and thank you for your nice improvements!
I've reviewed the patches 1 to 7, these make good sense and look very
clean to me.
few comments:
* I also think that including extensions is very useful, but for now,
I have to not increase the maintenance cost as far as possible, so
will not include them. (this way has some good points, for example
you can update them freely without my slow review :-)
For user access, I'm thinking about adding makedumpfile extension list
to its wiki page [1]. If you agree on this, please provide their
information (extension name, author, link, description).
* Could you add a description why PG_INCLUDE_HEAD is needed to the
patch 7, as we cannot add the cover letter and the patch 9.
(Text alone is ok, or you can use v2 just for the patch 7.)
Otherwise, there are typos and comment style gaps, but those can
be fixed when merging.
and Tao, please check if your extension can be rebased on this
patchset. and also if you agree the extension list, please provide
the information.
[1] https://github.com/makedumpfile/makedumpfile/wiki#extensions
Thanks,
Kazu
>
> Thanks,
> Stephen
>
> Stephen Brennan (9):
> Do not call extensions for tail pages
> Honor CFLAGS in extension/Makefile
> Share page information with extension callbacks
> Introduce a stat for pages retained by extension
> Move page checks into makedumpfile.h
> Simplify arguments for page checks
> Add PG_INCLUDE_HEAD extension return status
> Add userstack extension
> Add elfheader extension
>
> extension.c | 10 +-
> extension.h | 10 +-
> extensions/Makefile | 8 +-
> extensions/elfheader.c | 93 ++++++++++++
> extensions/userstack.c | 325 ++++++++++++++++++++++++++++++++++++++++
> extensions/vma_mtree.c | 140 +++++++++++++++++
> extensions/vma_mtree.h | 7 +
> extensions/vma_rbtree.c | 56 +++++++
> extensions/vma_rbtree.h | 12 ++
> makedumpfile.c | 188 +++++++++--------------
> makedumpfile.h | 78 +++++++++-
> 11 files changed, 798 insertions(+), 129 deletions(-)
> create mode 100644 extensions/elfheader.c
> create mode 100644 extensions/userstack.c
> create mode 100644 extensions/vma_mtree.c
> create mode 100644 extensions/vma_mtree.h
> create mode 100644 extensions/vma_rbtree.c
> create mode 100644 extensions/vma_rbtree.h
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-08-07 1:52 ` HAGIO KAZUHITO(萩尾 一仁)
@ 2026-08-07 7:57 ` Tao Liu
2026-08-07 21:24 ` Stephen Brennan
0 siblings, 1 reply; 18+ messages in thread
From: Tao Liu @ 2026-08-07 7:57 UTC (permalink / raw)
To: HAGIO KAZUHITO(萩尾 一仁)
Cc: Stephen Brennan,
YAMAZAKI MASAMITSU(山崎 真光),
kexec@lists.infradead.org
Hi Kazu,
On Fri, Aug 7, 2026 at 1:52 PM HAGIO KAZUHITO(萩尾 一仁) <k-hagio-ab@nec.com> wrote:
>
> On 2026/07/14 9:45, Stephen Brennan wrote:
> > Hello all,
> >
> > Building on Tao Liu's excellent work for makedumpfile extensions, I'd like to
> > share some incremental improvements to the extension system, as well as my own
> > two extensions which enable userspace stack tracing. There are three topics
> > related to the core makedumpfile extension system which are addressed here:
> >
> > 1. Tail pages handling. As patch 1 explains, extensions currently get called for
> > tail pages, but their decisions are not respected. I believe the best approach
> > is to only call extensions for the head page, but allow extensions to return a
> > decision related to just a subset of the pages. To this end, patch 7 adds
> > PG_INCLUDE_HEAD to include just the head page (leaving the rest up to the
> > default policy), and patch 9 shows the elfheader extension which uses that
> > functionality.
> >
> > 2. Counting pages included by extensions. This is done by patch 4. It alters the
> > logic because it's really helpful to know whether a page would have been
> > included by makedumpfile's dump-level filtering anyway, so we only count the
> > additional pages.
> >
> > 3. Sharing makedumpfile's logic and knowledge about page metadata. Currently,
> > extensions just get a "pcache" pointer, which means they must redo whatever
> > logic makedumpfile has already done, in order to get information like
> > compound_order. Patches 3, 5, and 6 factor out the page metadata into a new
> > struct pginfo, which gets passed to extensions, and it also makes more of the
> > helpers like "isSlab()" usable with this pointer.
> >
> > With those improvements, the final two patches (8 & 9) implement a pair of
> > extensions that are useful for including a minimal amount of information which
> > can be used to do stack traces of the userspace processes in a vmcore. The
> > "userstack" extension includes the stack memory itself for each thread, and the
> > "elfheader" extension includes the first page of mapped ELF files, so that ELF
> > build IDs can be identified from the vmcore.
> >
> > My testing so far:
> > - Live testing (/proc/kcore) x86_64 on Oracle UEK 8 (6.12.x based)
> > - Refiltering on vmcores generated from UEK8, 7 (5.15.x based), 6 (5.4.x based)
> > - Using the resulting filtered vmcores with drgn's contrib/pstack.py to generate
> > userspace stack traces.
> >
> > I still have significantly more testing to do and then share out:
> > - Testing in kexec environment
> > - Testing with upstream kernels, also verifying in a Fedora userspace
> > - Sharing runtime and size comparisons with different workloads
> >
> > Despite the limited testing, I wanted to share these patches as they are,
> > because I think the improvements for the core extensions framework are useful
> > already, and don't require as much verification.
> >
> > For the extensions themselves, I would like to consider merging them as well, if
> > there is interest. I think it would be useful for makedumpfile to contain shared
> > extensions, including those for the GPU buffers which Tao Liu was working on, as
> > well as the userspace stack tracing ones which I'm sharing.
>
> Hi Stephen, (and Tao)
>
> sorry for my slow review and thank you for your nice improvements!
> I've reviewed the patches 1 to 7, these make good sense and look very
> clean to me.
>
> few comments:
> * I also think that including extensions is very useful, but for now,
> I have to not increase the maintenance cost as far as possible, so
> will not include them. (this way has some good points, for example
> you can update them freely without my slow review :-)
>
> For user access, I'm thinking about adding makedumpfile extension list
> to its wiki page [1]. If you agree on this, please provide their
> information (extension name, author, link, description).
>
> * Could you add a description why PG_INCLUDE_HEAD is needed to the
> patch 7, as we cannot add the cover letter and the patch 9.
> (Text alone is ok, or you can use v2 just for the patch 7.)
>
> Otherwise, there are typos and comment style gaps, but those can
> be fixed when merging.
>
> and Tao, please check if your extension can be rebased on this
> patchset. and also if you agree the extension list, please provide
> the information.
Sure, I will check for the rebase. In the meantime, will the
extensions to be maintained in one repo (e.g. makedumpfile extensions
repo, so everyone can PR to it), or they are maintained in each of
individual repos, and link them under
https://github.com/makedumpfile/makedumpfile/wiki#extensions?
Thanks,
Tao Liu
>
> [1] https://github.com/makedumpfile/makedumpfile/wiki#extensions
>
> Thanks,
> Kazu
>
> >
> > Thanks,
> > Stephen
> >
> > Stephen Brennan (9):
> > Do not call extensions for tail pages
> > Honor CFLAGS in extension/Makefile
> > Share page information with extension callbacks
> > Introduce a stat for pages retained by extension
> > Move page checks into makedumpfile.h
> > Simplify arguments for page checks
> > Add PG_INCLUDE_HEAD extension return status
> > Add userstack extension
> > Add elfheader extension
> >
> > extension.c | 10 +-
> > extension.h | 10 +-
> > extensions/Makefile | 8 +-
> > extensions/elfheader.c | 93 ++++++++++++
> > extensions/userstack.c | 325 ++++++++++++++++++++++++++++++++++++++++
> > extensions/vma_mtree.c | 140 +++++++++++++++++
> > extensions/vma_mtree.h | 7 +
> > extensions/vma_rbtree.c | 56 +++++++
> > extensions/vma_rbtree.h | 12 ++
> > makedumpfile.c | 188 +++++++++--------------
> > makedumpfile.h | 78 +++++++++-
> > 11 files changed, 798 insertions(+), 129 deletions(-)
> > create mode 100644 extensions/elfheader.c
> > create mode 100644 extensions/userstack.c
> > create mode 100644 extensions/vma_mtree.c
> > create mode 100644 extensions/vma_mtree.h
> > create mode 100644 extensions/vma_rbtree.c
> > create mode 100644 extensions/vma_rbtree.h
> >
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-08-07 7:57 ` Tao Liu
@ 2026-08-07 21:24 ` Stephen Brennan
2026-08-08 4:44 ` HAGIO KAZUHITO(萩尾 一仁)
0 siblings, 1 reply; 18+ messages in thread
From: Stephen Brennan @ 2026-08-07 21:24 UTC (permalink / raw)
To: Tao Liu, HAGIO KAZUHITO(萩尾 一仁)
Cc: YAMAZAKI MASAMITSU(山崎 真光),
kexec@lists.infradead.org
Hi Tao & Kazu (responding to both in one message for simplicity :)
Tao Liu <ltao@redhat.com> writes:
> Hi Kazu,
>
> On Fri, Aug 7, 2026 at 1:52 PM HAGIO KAZUHITO(萩尾 一仁) <k-hagio-ab@nec.com> wrote:
>>
>> On 2026/07/14 9:45, Stephen Brennan wrote:
>> > Hello all,
>> >
>> > Building on Tao Liu's excellent work for makedumpfile extensions, I'd like to
>> > share some incremental improvements to the extension system, as well as my own
>> > two extensions which enable userspace stack tracing. There are three topics
>> > related to the core makedumpfile extension system which are addressed here:
>> >
>> > 1. Tail pages handling. As patch 1 explains, extensions currently get called for
>> > tail pages, but their decisions are not respected. I believe the best approach
>> > is to only call extensions for the head page, but allow extensions to return a
>> > decision related to just a subset of the pages. To this end, patch 7 adds
>> > PG_INCLUDE_HEAD to include just the head page (leaving the rest up to the
>> > default policy), and patch 9 shows the elfheader extension which uses that
>> > functionality.
>> >
>> > 2. Counting pages included by extensions. This is done by patch 4. It alters the
>> > logic because it's really helpful to know whether a page would have been
>> > included by makedumpfile's dump-level filtering anyway, so we only count the
>> > additional pages.
>> >
>> > 3. Sharing makedumpfile's logic and knowledge about page metadata. Currently,
>> > extensions just get a "pcache" pointer, which means they must redo whatever
>> > logic makedumpfile has already done, in order to get information like
>> > compound_order. Patches 3, 5, and 6 factor out the page metadata into a new
>> > struct pginfo, which gets passed to extensions, and it also makes more of the
>> > helpers like "isSlab()" usable with this pointer.
>> >
>> > With those improvements, the final two patches (8 & 9) implement a pair of
>> > extensions that are useful for including a minimal amount of information which
>> > can be used to do stack traces of the userspace processes in a vmcore. The
>> > "userstack" extension includes the stack memory itself for each thread, and the
>> > "elfheader" extension includes the first page of mapped ELF files, so that ELF
>> > build IDs can be identified from the vmcore.
>> >
>> > My testing so far:
>> > - Live testing (/proc/kcore) x86_64 on Oracle UEK 8 (6.12.x based)
>> > - Refiltering on vmcores generated from UEK8, 7 (5.15.x based), 6 (5.4.x based)
>> > - Using the resulting filtered vmcores with drgn's contrib/pstack.py to generate
>> > userspace stack traces.
>> >
>> > I still have significantly more testing to do and then share out:
>> > - Testing in kexec environment
>> > - Testing with upstream kernels, also verifying in a Fedora userspace
>> > - Sharing runtime and size comparisons with different workloads
>> >
>> > Despite the limited testing, I wanted to share these patches as they are,
>> > because I think the improvements for the core extensions framework are useful
>> > already, and don't require as much verification.
>> >
>> > For the extensions themselves, I would like to consider merging them as well, if
>> > there is interest. I think it would be useful for makedumpfile to contain shared
>> > extensions, including those for the GPU buffers which Tao Liu was working on, as
>> > well as the userspace stack tracing ones which I'm sharing.
>>
>> Hi Stephen, (and Tao)
>>
>> sorry for my slow review and thank you for your nice improvements!
>> I've reviewed the patches 1 to 7, these make good sense and look very
>> clean to me.
Thank you very much for reviewing.
>> few comments:
>> * I also think that including extensions is very useful, but for now,
>> I have to not increase the maintenance cost as far as possible, so
>> will not include them. (this way has some good points, for example
>> you can update them freely without my slow review :-)
I understand: more code means more maintenance. We can maintain our
extensions separately, thanks for the clarity!
>> For user access, I'm thinking about adding makedumpfile extension list
>> to its wiki page [1]. If you agree on this, please provide their
>> information (extension name, author, link, description).
Here are mine:
- Extension Name: elfheader
Author: Stephen Brennan (my email)
Link: TBD - see below
Description: Includes the first page of any ELF file in the page cache
which is mapped to userspace. This inspired by bit 4 of
coredump_filter (see core(5)) for userspace core dumps. By including
the header of an ELF file, it's likely that a debugger can identify
its build ID and use that to find debugging information for it. This
helps when used in combination with the userstack extension.
- Extension Name: userstack
Author: Stephen Brennan (my email)
Link: TBD - see below
Description: Attempts to include the memory pages for each userspace
thread's stack. This can make it possible to unwind userspace process
stacks using a vmcore, for example with drgn's contrib/pstack.py
script.
>> * Could you add a description why PG_INCLUDE_HEAD is needed to the
>> patch 7, as we cannot add the cover letter and the patch 9.
>> (Text alone is ok, or you can use v2 just for the patch 7.)
Done as a text reply to patch 7.
>> Otherwise, there are typos and comment style gaps, but those can
>> be fixed when merging.
If it's not too much work, please do make those changes when merging.
Thank you!
>> and Tao, please check if your extension can be rebased on this
>> patchset. and also if you agree the extension list, please provide
>> the information.
>
> Sure, I will check for the rebase. In the meantime, will the
> extensions to be maintained in one repo (e.g. makedumpfile extensions
> repo, so everyone can PR to it), or they are maintained in each of
> individual repos, and link them under
> https://github.com/makedumpfile/makedumpfile/wiki#extensions?
I am open to either. I would be willing to co-maintain an extensions
repository with Tao, for example at github.com/makedumpfile/extensions,
so that there is a common place to contribute. That is, if this
arrangement would work for all?
If we did create a common repository, I think we'd want to set up some
expectations on the quality & maintenance. EG, some may be marked as
"maintained" with a maintainer and a scope of maintenance (supported
kernel versions, supported architectures, etc), and others as
"unmaintained", where we just make sure that they build & run with
makedumpfile, but don't validate their behavior on specific kernels or
architectures.
On the other hand, if it's preferable to stay separate, then I'm happy
to find another place to maintain the userstack & elfheader extensions.
Thank you both!
Stephen
> Thanks,
> Tao Liu
>
>>
>> [1] https://github.com/makedumpfile/makedumpfile/wiki#extensions
>>
>> Thanks,
>> Kazu
>>
>> >
>> > Thanks,
>> > Stephen
>> >
>> > Stephen Brennan (9):
>> > Do not call extensions for tail pages
>> > Honor CFLAGS in extension/Makefile
>> > Share page information with extension callbacks
>> > Introduce a stat for pages retained by extension
>> > Move page checks into makedumpfile.h
>> > Simplify arguments for page checks
>> > Add PG_INCLUDE_HEAD extension return status
>> > Add userstack extension
>> > Add elfheader extension
>> >
>> > extension.c | 10 +-
>> > extension.h | 10 +-
>> > extensions/Makefile | 8 +-
>> > extensions/elfheader.c | 93 ++++++++++++
>> > extensions/userstack.c | 325 ++++++++++++++++++++++++++++++++++++++++
>> > extensions/vma_mtree.c | 140 +++++++++++++++++
>> > extensions/vma_mtree.h | 7 +
>> > extensions/vma_rbtree.c | 56 +++++++
>> > extensions/vma_rbtree.h | 12 ++
>> > makedumpfile.c | 188 +++++++++--------------
>> > makedumpfile.h | 78 +++++++++-
>> > 11 files changed, 798 insertions(+), 129 deletions(-)
>> > create mode 100644 extensions/elfheader.c
>> > create mode 100644 extensions/userstack.c
>> > create mode 100644 extensions/vma_mtree.c
>> > create mode 100644 extensions/vma_mtree.h
>> > create mode 100644 extensions/vma_rbtree.c
>> > create mode 100644 extensions/vma_rbtree.h
>> >
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-08-07 21:24 ` Stephen Brennan
@ 2026-08-08 4:44 ` HAGIO KAZUHITO(萩尾 一仁)
2026-08-11 6:11 ` Tao Liu
0 siblings, 1 reply; 18+ messages in thread
From: HAGIO KAZUHITO(萩尾 一仁) @ 2026-08-08 4:44 UTC (permalink / raw)
To: Stephen Brennan, Tao Liu
Cc: YAMAZAKI MASAMITSU(山崎 真光),
kexec@lists.infradead.org
Hi Stephen, Tao,
On 2026/08/08 6:24, Stephen Brennan wrote:
>>> For user access, I'm thinking about adding makedumpfile extension list
>>> to its wiki page [1]. If you agree on this, please provide their
>>> information (extension name, author, link, description).
>
> Here are mine:
>
> - Extension Name: elfheader
> Author: Stephen Brennan (my email)
> Link: TBD - see below
> Description: Includes the first page of any ELF file in the page cache
> which is mapped to userspace. This inspired by bit 4 of
> coredump_filter (see core(5)) for userspace core dumps. By including
> the header of an ELF file, it's likely that a debugger can identify
> its build ID and use that to find debugging information for it. This
> helps when used in combination with the userstack extension.
> - Extension Name: userstack
> Author: Stephen Brennan (my email)
> Link: TBD - see below
> Description: Attempts to include the memory pages for each userspace
> thread's stack. This can make it possible to unwind userspace process
> stacks using a vmcore, for example with drgn's contrib/pstack.py
> script.
Thanks, I copied this to see how it looks.
https://github.com/makedumpfile/makedumpfile/wiki#extensions
Please let me know if there are any points of concern.
>>> * Could you add a description why PG_INCLUDE_HEAD is needed to the
>>> patch 7, as we cannot add the cover letter and the patch 9.
>>> (Text alone is ok, or you can use v2 just for the patch 7.)
>
> Done as a text reply to patch 7.
Thanks, will use that.
>>> and Tao, please check if your extension can be rebased on this
>>> patchset. and also if you agree the extension list, please provide
>>> the information.
>>
>> Sure, I will check for the rebase. In the meantime, will the
>> extensions to be maintained in one repo (e.g. makedumpfile extensions
>> repo, so everyone can PR to it), or they are maintained in each of
>> individual repos, and link them under
>> https://github.com/makedumpfile/makedumpfile/wiki#extensions?
>
> I am open to either. I would be willing to co-maintain an extensions
> repository with Tao, for example at github.com/makedumpfile/extensions,
> so that there is a common place to contribute. That is, if this
> arrangement would work for all?
>
> If we did create a common repository, I think we'd want to set up some
> expectations on the quality & maintenance. EG, some may be marked as
> "maintained" with a maintainer and a scope of maintenance (supported
> kernel versions, supported architectures, etc), and others as
> "unmaintained", where we just make sure that they build & run with
> makedumpfile, but don't validate their behavior on specific kernels or
> architectures.
>
> On the other hand, if it's preferable to stay separate, then I'm happy
> to find another place to maintain the userstack & elfheader extensions.
I was thinking of separate repositories. A single repository would be
fine, but for now I have no plans to create one within the makedumpfile
organization.
(Looking back at crash extensions, some become obsolete quickly or
have very specific functionality, making it difficult for anyone
other than the original author to maintain. For this reason, I think
it is best for the authors themselves to manage them.)
Thanks,
Kazu
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH makedumpfile 0/9] Improvements to makedumpfile extensions, plus userspace stack tracing extension
2026-08-08 4:44 ` HAGIO KAZUHITO(萩尾 一仁)
@ 2026-08-11 6:11 ` Tao Liu
0 siblings, 0 replies; 18+ messages in thread
From: Tao Liu @ 2026-08-11 6:11 UTC (permalink / raw)
To: HAGIO KAZUHITO(萩尾 一仁)
Cc: Stephen Brennan,
YAMAZAKI MASAMITSU(山崎 真光),
kexec@lists.infradead.org
Hi Kazu & Stephen,
On Sat, Aug 8, 2026 at 4:45 PM HAGIO KAZUHITO(萩尾 一仁) <k-hagio-ab@nec.com> wrote:
>
> Hi Stephen, Tao,
>
> On 2026/08/08 6:24, Stephen Brennan wrote:
>
> >>> For user access, I'm thinking about adding makedumpfile extension list
> >>> to its wiki page [1]. If you agree on this, please provide their
> >>> information (extension name, author, link, description).
> >
> > Here are mine:
> >
> > - Extension Name: elfheader
> > Author: Stephen Brennan (my email)
> > Link: TBD - see below
> > Description: Includes the first page of any ELF file in the page cache
> > which is mapped to userspace. This inspired by bit 4 of
> > coredump_filter (see core(5)) for userspace core dumps. By including
> > the header of an ELF file, it's likely that a debugger can identify
> > its build ID and use that to find debugging information for it. This
> > helps when used in combination with the userstack extension.
> > - Extension Name: userstack
> > Author: Stephen Brennan (my email)
> > Link: TBD - see below
> > Description: Attempts to include the memory pages for each userspace
> > thread's stack. This can make it possible to unwind userspace process
> > stacks using a vmcore, for example with drgn's contrib/pstack.py
> > script.
>
> Thanks, I copied this to see how it looks.
> https://github.com/makedumpfile/makedumpfile/wiki#extensions
>
> Please let me know if there are any points of concern.
>
> >>> * Could you add a description why PG_INCLUDE_HEAD is needed to the
> >>> patch 7, as we cannot add the cover letter and the patch 9.
> >>> (Text alone is ok, or you can use v2 just for the patch 7.)
> >
> > Done as a text reply to patch 7.
>
> Thanks, will use that.
>
> >>> and Tao, please check if your extension can be rebased on this
> >>> patchset. and also if you agree the extension list, please provide
> >>> the information.
I have checked my extension to rebase on this patchset. The result is:
1) Rebase fails, but only due to conflicts within extensions/Makefile.
These are small issues which would be very easy to fix.
2) Once 1 is fixed, after compile, my extension can work as expected.
However this is a coincidence. Because my extension takes callback
as:
int extension_callback(unsigned long pfn, const void *pcache)
{
struct ft_page_info *cur = NULL;
return filter_page(pfn, &cur);
}
But this patchset changed the callback into:
run_extension_callback(unsigned long pfn, const void *pcache, const
struct pginfo *i)
My extension works because it only takes pfn as parameter, and the
handle_cbs[i]->cb() doesn't check function parameters. However, I
don't think it is a major issue, once the modification on extension.c
merged upstream, I can adjust the function signature of mine
accordingly.
> >>
> >> Sure, I will check for the rebase. In the meantime, will the
> >> extensions to be maintained in one repo (e.g. makedumpfile extensions
> >> repo, so everyone can PR to it), or they are maintained in each of
> >> individual repos, and link them under
> >> https://github.com/makedumpfile/makedumpfile/wiki#extensions?
> >
> > I am open to either. I would be willing to co-maintain an extensions
> > repository with Tao, for example at github.com/makedumpfile/extensions,
> > so that there is a common place to contribute. That is, if this
> > arrangement would work for all?
> >
> > If we did create a common repository, I think we'd want to set up some
> > expectations on the quality & maintenance. EG, some may be marked as
> > "maintained" with a maintainer and a scope of maintenance (supported
> > kernel versions, supported architectures, etc), and others as
> > "unmaintained", where we just make sure that they build & run with
> > makedumpfile, but don't validate their behavior on specific kernels or
> > architectures.
> >
> > On the other hand, if it's preferable to stay separate, then I'm happy
> > to find another place to maintain the userstack & elfheader extensions.
>
> I was thinking of separate repositories. A single repository would be
> fine, but for now I have no plans to create one within the makedumpfile
> organization.
>
> (Looking back at crash extensions, some become obsolete quickly or
> have very specific functionality, making it difficult for anyone
> other than the original author to maintain. For this reason, I think
> it is best for the authors themselves to manage them.)
Understood, extensions are designed to address one specific issue, and
is easy to get outdated when makedumpfile/crash utility keeping
following up the latest kernel, but extensions are often left behind
due to low usage. Let's go with maintainling the extensions by each
authors seperately and link them into
https://github.com/makedumpfile/makedumpfile/wiki#extensions
Thanks,
Tao Liu
>
> Thanks,
> Kazu
^ permalink raw reply [flat|nested] 18+ messages in thread