Re: [PATCH v2] sys: Display livepatch transition status in KERNEL line
by Tao Liu
Hi Fuli,
On Fri, Feb 13, 2026 at 1:15 AM Fuli Qi (Fujitsu) <qi.fuli(a)fujitsu.com> wrote:
>
> Hello Tao Liu,
>
> Thank you very much.
> I appreciate your help.
^_^
Thanks,
Tao Liu
>
> Best,
> Fuli
>
> > -----Original Message-----
> > From: Tao Liu <ltao(a)redhat.com>
> > Sent: Thursday, February 12, 2026 9:01 PM
> > To: Qi, Fuli/斉 福利 <qi.fuli(a)fujitsu.com>
> > Cc: Motomasa Suzuki <moto0123moto(a)gmail.com>;
> > devel(a)lists.crash-utility.osci.io
> > Subject: Re: [PATCH v2] sys: Display livepatch transition status in KERNEL
> > line
> >
> > [You don't often get email from ltao(a)redhat.com. Learn why this is important
> > at https://aka.ms/LearnAboutSenderIdentification ]
> >
> > applied:
> > https://github.com/crash-utility/crash/commit/24b5405eb6f67a96e8b5bbd5c
> > 8de8f22dfefd092
> >
> > On Thu, Feb 12, 2026 at 7:40 PM Tao Liu <ltao(a)redhat.com> wrote:
> > >
> > > Hi Fuli,
> > >
> > > On Thu, Feb 12, 2026 at 6:57 PM Fuli Qi (Fujitsu) <qi.fuli(a)fujitsu.com>
> > wrote:
> > > >
> > > > Hello Tao Liu,
> > > >
> > > > Thank you for the review.
> > > > Could you please kindly proceed with merging this patch?
> > >
> > > Sure, I will merge it today, sorry for the delay.
> > >
> > > Thanks,
> > > Tao Liu
> > > >
> > > > Best,
> > > > Fuli
> > > >
> > > > > -----Original Message-----
> > > > > From: Tao Liu <ltao(a)redhat.com>
> > > > > Sent: Tuesday, December 30, 2025 1:32 PM
> > > > > To: Motomasa Suzuki <moto0123moto(a)gmail.com>
> > > > > Cc: devel(a)lists.crash-utility.osci.io; Qi, Fuli/斉 福利
> > > > > <qi.fuli(a)fujitsu.com>
> > > > > Subject: Re: [PATCH v2] sys: Display livepatch transition status
> > > > > in KERNEL line
> > > > >
> > > > > [You don't often get email from ltao(a)redhat.com. Learn why this is
> > > > > important at https://aka.ms/LearnAboutSenderIdentification ]
> > > > >
> > > > > On Tue, Dec 23, 2025 at 6:51 PM Motomasa Suzuki
> > > > > <moto0123moto(a)gmail.com> wrote:
> > > > > >
> > > > > > This commit enhances the 'sys' command to show if a kernel
> > > > > > livepatch is currently in a transition phase, directly within the KERNEL
> > output line.
> > > > > >
> > > > > > Currently, diagnosing system state during or immediately after
> > > > > > livepatch operations can be ambiguous. While 'livepatch' is
> > > > > > indicated by '[LIVEPATCH]', there's no direct indicator within
> > > > > > 'crash' itself to show if a livepatch is actively applying,
> > > > > > reverting, or in some other transient state. This lack of
> > > > > > immediate visibility can complicate crash analysis, as the
> > > > > > system might be in an inconsistent state due to an ongoing patch
> > application/reversion.
> > > > > >
> > > > > > This change introduces a new '[TRANSITION]' flag which appears
> > > > > > next to '[LIVEPATCH]' and '[TAINTED]' in the 'sys' command
> > > > > > output. This flag is set if the livepatch subsystem indicates an
> > > > > > in-progress transition (e.g., as exposed via
> > '/sys/kernel/livepatch/<patch_name>/transition').
> > > > > >
> > > > > > Example 'sys' output with this change:
> > > > > > KERNEL: /usr/lib/debug/lib/modules/<version_name>/vmlinux
> > > > > > [LIVEPATCH] [TRANSITION] [TAINTED]
> > > > > >
> > > > > > This enhancement provides critical, at-a-glance information for
> > > > > > developers and administrators, allowing them to quickly
> > > > > > ascertain if ongoing livepatch activity might be influencing a
> > > > > > system's behavior during crash investigations. It directly
> > > > > > leverages existing kernel livepatch status infrastructure to
> > > > > > enrich the crash utility's diagnostic capabilities.
> > > > > >
> > > > > >
> > > > > > Changes v1 -> v2:
> > > > > > - Reworked klp_patches list traversal to use the crash utility’s
> > > > > > do_list() helper instead of manual list handling.
> > > > >
> > > > > The v2 LGTM, so ack.
> > > > >
> > > > > Thanks,
> > > > > Tao Liu
> > > > > >
> > > > > > Signed-off-by: Motomasa Suzuki <moto0123moto(a)gmail.com>
> > > > > > ---
> > > > > > defs.h | 1 +
> > > > > > kernel.c | 75
> > > > > >
> > > > >
> > ++++++++++++++++++++++++++++++++++++++++++++++++
> > +++
> > > > > +++--
> > > > > > 2 files changed, 74 insertions(+), 2 deletions(-)
> > > > > >
> > > > > > diff --git a/defs.h b/defs.h
> > > > > > index ceed3a9..cfdfa08 100644
> > > > > > --- a/defs.h
> > > > > > +++ b/defs.h
> > > > > > @@ -2289,6 +2289,7 @@ struct offset_table { /*
> > stash of
> > > > > commonly-used offsets */
> > > > > > long bpf_ringbuf_consumer_pos;
> > > > > > long bpf_ringbuf_nr_pages;
> > > > > > long hrtimer_clock_base_index;
> > > > > > + long klp_patch_list;
> > > > > > };
> > > > > >
> > > > > > struct size_table { /* stash of commonly-used sizes */
> > > > > > diff --git a/kernel.c b/kernel.c index bb148d0..ccc4b3d 100644
> > > > > > --- a/kernel.c
> > > > > > +++ b/kernel.c
> > > > > > @@ -464,6 +464,11 @@ kernel_init()
> > > > > > "list_head.next offset: %ld: list command may
> > fail\n",
> > > > > > OFFSET(list_head_next));
> > > > > >
> > > > > > + if (STRUCT_EXISTS("klp_patch")) {
> > > > > > + if (MEMBER_EXISTS("klp_patch", "list"))
> > > > > > + MEMBER_OFFSET_INIT(klp_patch_list,
> > > > > > + "klp_patch",
> > > > > "list");
> > > > > > + }
> > > > > > +
> > > > > > MEMBER_OFFSET_INIT(hlist_node_next, "hlist_node",
> > "next");
> > > > > > MEMBER_OFFSET_INIT(hlist_node_pprev, "hlist_node",
> > "pprev");
> > > > > > STRUCT_SIZE_INIT(hlist_head, "hlist_head"); @@ -5689,6
> > > > > > +5694,70 @@ is_livepatch(void)
> > > > > > return FALSE;
> > > > > > }
> > > > > >
> > > > > > +struct klp_transition_ctx {
> > > > > > + ulong transition_patch;
> > > > > > + int found;
> > > > > > +};
> > > > > > +
> > > > > > +static int
> > > > > > +klp_transition_match(void *entry, void *data) {
> > > > > > + struct klp_transition_ctx *ctx = data;
> > > > > > +
> > > > > > + if ((ulong)entry == ctx->transition_patch) {
> > > > > > + ctx->found = TRUE;
> > > > > > + return TRUE;
> > > > > > + }
> > > > > > +
> > > > > > + return FALSE;
> > > > > > +}
> > > > > > +
> > > > > > +static int
> > > > > > +is_livepatch_transition(void)
> > > > > > +{
> > > > > > + struct kernel_list_head head;
> > > > > > + struct list_data ld;
> > > > > > + struct klp_transition_ctx ctx;
> > > > > > + ulong transition_patch;
> > > > > > + ulong list_addr;
> > > > > > + int ret;
> > > > > > +
> > > > > > + if (!try_get_symbol_data("klp_transition_patch",
> > > > > > + sizeof(ulong), &transition_patch) || !transition_patch)
> > > > > > + return FALSE;
> > > > > > +
> > > > > > + if (!STRUCT_EXISTS("klp_patch")
> > > > > || !VALID_MEMBER(klp_patch_list) ||
> > > > > > + !kernel_symbol_exists("klp_patches"))
> > > > > > + return TRUE;
> > > > > > +
> > > > > > + list_addr = symbol_value("klp_patches");
> > > > > > + if (!readmem(list_addr, KVADDR, &head, sizeof(head),
> > "klp_patches",
> > > > > > + RETURN_ON_ERROR | QUIET))
> > > > > > + return TRUE;
> > > > > > +
> > > > > > + if (!head.next || head.next == (void *)list_addr)
> > > > > > + return TRUE;
> > > > > > +
> > > > > > + BZERO(&ctx, sizeof(ctx));
> > > > > > + ctx.transition_patch = transition_patch;
> > > > > > +
> > > > > > + BZERO(&ld, sizeof(ld));
> > > > > > + ld.flags =
> > > > > LIST_CALLBACK|CALLBACK_RETURN|RETURN_ON_LIST_ERROR|
> > > > > > + RETURN_ON_DUPLICATE|LIST_ALLOCATE;
> > > > > > + ld.start = (ulong)head.next;
> > > > > > + ld.end = list_addr;
> > > > > > + ld.member_offset = OFFSET(list_head_next);
> > > > > > + ld.list_head_offset = OFFSET(klp_patch_list);
> > > > > > + ld.callback_func = klp_transition_match;
> > > > > > + ld.callback_data = &ctx;
> > > > > > +
> > > > > > + ret = do_list(&ld);
> > > > > > + if (ret < 0)
> > > > > > + return FALSE;
> > > > > > +
> > > > > > + return ctx.found;
> > > > > > +}
> > > > > > +
> > > > > > /*
> > > > > > * Display system stats at init-time or for the sys command.
> > > > > > */
> > > > > > @@ -5732,17 +5801,19 @@ display_sys_stats(void)
> > > > > > }
> > > > > > } else {
> > > > > > if (pc->system_map) {
> > > > > > - fprintf(fp, " SYSTEM MAP: %s%s%s\n",
> > > > > pc->system_map,
> > > > > > + fprintf(fp, " SYSTEM
> > MAP: %s%s%s%s\n",
> > > > > > + pc->system_map,
> > > > > > is_livepatch() ? " [LIVEPATCH]"
> > > > > > : "",
> > > > > > + is_livepatch_transition() ? "
> > > > > > + [TRANSITION]" : "",
> > > > > > is_kernel_tainted() ? "
> > [TAINTED]" : "");
> > > > > > fprintf(fp, "DEBUG KERNEL: %s %s\n",
> > > > > > pc->namelist_orig ?
> > > > > > pc->namelist_orig :
> > > > > > pc->namelist,
> > > > > >
> > > > > debug_kernel_version(pc->namelist));
> > > > > > } else
> > > > > > - fprintf(fp, " KERNEL: %s%s%s\n",
> > > > > pc->namelist_orig ?
> > > > > > + fprintf(fp, " KERNEL: %s%s%s%s\n",
> > > > > pc->namelist_orig ?
> > > > > > pc->namelist_orig :
> > pc->namelist,
> > > > > > is_livepatch() ? " [LIVEPATCH]"
> > > > > > : "",
> > > > > > + is_livepatch_transition() ? "
> > > > > > + [TRANSITION]" : "",
> > > > > > is_kernel_tainted() ? "
> > [TAINTED]" : "");
> > > > > > }
> > > > > >
> > > > > > --
> > > > > > 2.47.3
> > > > > >
> > > >
>
1 week, 2 days
Re: [PATCH] arm64: Fix broken/incomplete gdb backtrace and unify output format
by lijiang
Hi, Pengfei
I did the test with your patch, and got an incorrect back trace, Can you
also help double check?
crash> gdb bt
#0 0xffffc8c40b0d9b14 in crash_setup_regs
(newregs=newregs@entry=0xffff80008862b638,
oldregs=oldregs@entry=0x0) at ./arch/arm64/include/asm/kexec.h:45
#1 0xffffc8c40b0d9dc4 in __crash_kexec (regs=regs@entry=0x0) at
kernel/crash_core.c:119
#2 0xffffc8c40aed6a44 in vpanic (fmt=fmt@entry=0xffffc8c40c527558 "sysrq
triggered crash\n", args=<error reading variable: Cannot access memory at
address 0x0>) at kernel/panic.c:369
#3 0xffffc8c40aed6cd0 in panic (fmt=fmt@entry=0xffffc8c40c527558 "sysrq
triggered crash\n") at kernel/panic.c:484
#4 0xffffc8c40b82a594 in sysrq_handle_crash (key=<optimized out>) at
drivers/tty/sysrq.c:154
#5 0xffffc8c40b82aee0 in __handle_sysrq (key=99 'c', check_mask=false)
at drivers/tty/sysrq.c:613
#6 0xffffc8c40b82b6b0 in write_sysrq_trigger (file=<optimized out>,
buf=<optimized out>, count=2, ppos=<optimized out>) at
drivers/tty/sysrq.c:1183
#7 0xffffc8c40b46bcec in pde_write (pde=0xffff0000c7fa6a80,
file=<optimized out>, buf=<optimized out>, count=<optimized out>,
ppos=<optimized out>) at fs/proc/inode.c:330
#8 proc_reg_write (file=<optimized out>,
buf=<optimized out>, count=<optimized out>, ppos=<optimized out>) at
fs/proc/inode.c:342
#9 0xffffc8c40b3ad52c in vfs_write (file=file@entry=0xffff0000c86aff00,
buf=buf@entry=0xaaab1a0c37f0 <error: Cannot access memory at address
0xaaab1a0c37f0>, count=count@entry=2, pos=pos@entry=0xffff80008862ba20) at
fs/read_write.c:681
#10 0xffffc8c40b3ad9a4 in ksys_write (fd=<optimized out>,
buf=0xaaab1a0c37f0 <error: Cannot access memory at address 0xaaab1a0c37f0>,
count=2) at fs/read_write.c:736
#11 0xffffc8c40b3ada6c in __do_sys_write (fd=<optimized out>,
buf=<optimized out>, count=<optimized out>) at fs/read_write.c:748
#12 __se_sys_write (fd=<optimized out>,
buf=<optimized out>, count=<optimized out>) at fs/read_write.c:745
#13 __arm64_sys_write (regs=<optimized out>) at
fs/read_write.c:745
#14 0xffffc8c40af0cc7c in __invoke_syscall (regs=0xffff80008862beb0,
syscall_fn=<optimized out>) at arch/arm64/kernel/syscall.c:35
#15 invoke_syscall (regs=regs@entry=0xffff80008862beb0,
scno=<optimized out>, sc_nr=sc_nr@entry=463, syscall_table=<optimized out>)
at arch/arm64/kernel/syscall.c:49
#16 0xffffc8c40af0cd88 in el0_svc_common (sc_nr=463,
syscall_table=<optimized out>, regs=0xffff80008862beb0, scno=<optimized
out>) at arch/arm64/kernel/syscall.c:132
#17 do_el0_svc (regs=regs@entry=0xffff80008862beb0)
at arch/arm64/kernel/syscall.c:151
#18 0xffffc8c40beb9e2c in el0_svc (regs=0xffff80008862beb0) at
arch/arm64/kernel/entry-common.c:879
#19 0xffffc8c40beba2b0 in el0t_64_sync_handler (regs=<optimized out>) at
arch/arm64/kernel/entry-common.c:898
#20 0xffffc8c40aed1684 in el0t_64_sync () at arch/arm64/kernel/entry.S:596
#21 machine_kexec (kimage=<unavailable>) at
arch/arm64/kernel/machine_kexec.c:194
#22 0xffffc8c40b0d9dd8 in __crash_kexec (regs=regs@entry=0x0) at
kernel/crash_core.c:122
#23 0xffffc8c40aed6a44 in vpanic (fmt=fmt@entry=0xffffc8c40c527558 "sysrq
triggered crash\n", args=<error reading variable: Cannot access memory at
address 0x0>) at kernel/panic.c:369
#24 0xffffc8c40aed6cd0 in panic (fmt=fmt@entry=0xffffc8c40c527558 "sysrq
triggered crash\n") at kernel/panic.c:484
#25 0xffffc8c40b82a594 in sysrq_handle_crash (key=<optimized out>) at
drivers/tty/sysrq.c:154
#26 0xffffc8c40b82aee0 in __handle_sysrq (key=99 'c',
check_mask=<unavailable>) at drivers/tty/sysrq.c:613
#27 0xffffc8c40b82b6b0 in write_sysrq_trigger (file=<optimized out>,
buf=<optimized out>, count=2, ppos=<optimized out>) at
drivers/tty/sysrq.c:1183
#28 0xffffc8c40b46bcec in pde_write (pde=0xffff0000c7fa6a80,
file=<optimized out>, buf=<optimized out>, count=<optimized out>,
ppos=<optimized out>) at fs/proc/inode.c:330
#29 proc_reg_write (file=<optimized out>,
buf=<optimized out>, count=<optimized out>, ppos=<optimized out>) at
fs/proc/inode.c:342
#30 0xffffc8c40b3ad52c in vfs_write (file=file@entry=0xffff0000c86aff00,
buf=buf@entry=0xaaab1a0c37f0 <error: Cannot access memory at address
0xaaab1a0c37f0>, count=count@entry=2, pos=pos@entry=0xffff80008862ba20) at
fs/read_write.c:681
#31 0xffffc8c40b3ad9a4 in ksys_write (fd=<optimized out>,
buf=0xaaab1a0c37f0 <error: Cannot access memory at address 0xaaab1a0c37f0>,
count=2) at fs/read_write.c:736
#32 0xffffc8c40b3ada6c in __do_sys_write (fd=<optimized out>,
buf=<optimized out>, count=<optimized out>) at fs/read_write.c:748
#33 __se_sys_write (fd=<optimized out>,
buf=<optimized out>, count=<optimized out>) at fs/read_write.c:745
#34 __arm64_sys_write (regs=<optimized out>) at
fs/read_write.c:745
#35 0xffffc8c40af0cc7c in __invoke_syscall (regs=0xffff80008862beb0,
syscall_fn=<optimized out>) at arch/arm64/kernel/syscall.c:35
#36 invoke_syscall (regs=regs@entry=0xffff80008862beb0,
scno=<optimized out>, sc_nr=sc_nr@entry=463, syscall_table=<optimized out>)
at arch/arm64/kernel/syscall.c:49
#37 0xffffc8c40af0cd88 in el0_svc_common (sc_nr=463,
syscall_table=<optimized out>, regs=0xffff80008862beb0, scno=<optimized
out>) at arch/arm64/kernel/syscall.c:132
#38 do_el0_svc (regs=regs@entry=0xffff80008862beb0)
at arch/arm64/kernel/syscall.c:151
#39 0xffffc8c40beb9e2c in el0_svc (regs=0xffff80008862beb0) at
arch/arm64/kernel/entry-common.c:879
#40 0xffffc8c40beba2b0 in el0t_64_sync_handler (regs=<optimized out>) at
arch/arm64/kernel/entry-common.c:898
#41 0xffffc8c40aed1684 in el0t_64_sync () at arch/arm64/kernel/entry.S:596
I can get the correct back trace without the patch:
crash> gdb bt
#0 0xffffc8c40b0d9b14 in crash_setup_regs
(newregs=newregs@entry=0xffff80008862b638,
oldregs=oldregs@entry=0x0) at ./arch/arm64/include/asm/kexec.h:45
#1 0xffffc8c40b0d9dc4 in __crash_kexec (regs=regs@entry=0x0) at
kernel/crash_core.c:119
#2 0xffffc8c40aed6a44 in vpanic (fmt=fmt@entry=0xffffc8c40c527558 "sysrq
triggered crash\n", args=<error reading variable: Cannot access memory at
address 0x0>) at kernel/panic.c:369
#3 0xffffc8c40aed6cd0 in panic (fmt=fmt@entry=0xffffc8c40c527558 "sysrq
triggered crash\n") at kernel/panic.c:484
#4 0xffffc8c40b82a594 in sysrq_handle_crash (key=<optimized out>) at
drivers/tty/sysrq.c:154
#5 0xffffc8c40b82aee0 in __handle_sysrq (key=99 'c', check_mask=false) at
drivers/tty/sysrq.c:613
#6 0xffffc8c40b82b6b0 in write_sysrq_trigger (file=<optimized out>,
buf=<optimized out>, count=2, ppos=<optimized out>) at
drivers/tty/sysrq.c:1183
#7 0xffffc8c40b46bcec in pde_write (pde=0xffff0000c7fa6a80,
file=<optimized out>, buf=<optimized out>, count=<optimized out>,
ppos=<optimized out>) at fs/proc/inode.c:330
#8 proc_reg_write (file=<optimized out>, buf=<optimized out>,
count=<optimized out>, ppos=<optimized out>) at fs/proc/inode.c:342
#9 0xffffc8c40b3ad52c in vfs_write (file=file@entry=0xffff0000c86aff00,
buf=buf@entry=0xaaab1a0c37f0 <error: Cannot access memory at address
0xaaab1a0c37f0>, count=count@entry=2, pos=pos@entry=0xffff80008862ba20) at
fs/read_write.c:681
#10 0xffffc8c40b3ad9a4 in ksys_write (fd=<optimized out>,
buf=0xaaab1a0c37f0 <error: Cannot access memory at address 0xaaab1a0c37f0>,
count=2) at fs/read_write.c:736
#11 0xffffc8c40b3ada6c in __do_sys_write (fd=<optimized out>,
buf=<optimized out>, count=<optimized out>) at fs/read_write.c:748
#12 __se_sys_write (fd=<optimized out>, buf=<optimized out>,
count=<optimized out>) at fs/read_write.c:745
#13 __arm64_sys_write (regs=<optimized out>) at fs/read_write.c:745
#14 0xffffc8c40af0cc7c in __invoke_syscall (regs=0xffff80008862beb0,
syscall_fn=<optimized out>) at arch/arm64/kernel/syscall.c:35
#15 invoke_syscall (regs=regs@entry=0xffff80008862beb0, scno=<optimized
out>, sc_nr=sc_nr@entry=463, syscall_table=<optimized out>) at
arch/arm64/kernel/syscall.c:49
#16 0xffffc8c40af0cd88 in el0_svc_common (sc_nr=463,
syscall_table=<optimized out>, regs=0xffff80008862beb0, scno=<optimized
out>) at arch/arm64/kernel/syscall.c:132
#17 do_el0_svc (regs=regs@entry=0xffff80008862beb0) at
arch/arm64/kernel/syscall.c:151
#18 0xffffc8c40beb9e2c in el0_svc (regs=0xffff80008862beb0) at
arch/arm64/kernel/entry-common.c:879
#19 0xffffc8c40beba2b0 in el0t_64_sync_handler (regs=<optimized out>) at
arch/arm64/kernel/entry-common.c:898
#20 0xffffc8c40aed1684 in el0t_64_sync () at arch/arm64/kernel/entry.S:596
Anyway, I haven't checked the patch carefully, just tried to test it and
found this error.
In addition, I would suggest splitting this patch into smaller patches, and
each patch should only do one thing(keep its functionality independent),
which will help us to review it easily.
Thanks
Lianbo
On Fri, Jan 30, 2026 at 5:21 PM <devel-request(a)lists.crash-utility.osci.io>
wrote:
> Date: Mon, 26 Jan 2026 11:43:42 -0000
> From: lipengfei28(a)xiaomi.com
> Subject: [Crash-utility] [PATCH] arm64: Fix broken/incomplete gdb
> backtrace and unify output format
> To: devel(a)lists.crash-utility.osci.io
> Message-ID: <20260126114342.13119.56270(a)lists.crash-utility.osci.io>
> Content-Type: text/plain; charset="utf-8"
>
> >From 4d832a33ebd80bd109cc5a47f98c6b35fdcbd956 Mon Sep 17 00:00:00 2001
> From: lipengfei28 <lipengfei28(a)xiaomi.com>
> Date: Fri, 23 Jan 2026 16:24:07 +0800
> Subject: [PATCH] arm64: Fix broken/incomplete gdb backtrace and unify
> output
> format
>
> This patch fixes multiple issues with 'gdb bt' on ARM64, where the
> backtrace
> would be interrupted, contain garbage threads, or display fragmented
> output.
>
> 1. Fix Out-of-Bounds Read in Exception Frame Handling:
> In `arm64_print_exception_frame`, the code previously used `memcpy` to
> copy
> `sizeof(struct arm64_pt_regs)` bytes from a `struct arm64_stackframe
> *` source.
> Since `stackframe` is significantly smaller than `pt_regs`, this
> caused an
> out-of-bounds read, populating the GDB thread registers with stack
> garbage
> (often resulting in invalid addresses like -3/0xff...fd).
> This is fixed by manually copying only the valid registers (PC, SP,
> FP, etc.)
> and properly initializing the bitmap.
>
> 2. Bridge the Gap Between IRQ and Process Stacks:
> Previously, GDB unwinding would stop at `call_on_irq_stack` because it
> could
> not automatically unwind through the assembly trampoline back to the
> process
> stack.
> Modified `arm64_switch_stack` (and the overflow variant) to "peek" one
> frame
> ahead (reading the saved FP/PC of the caller) before registering the
> new
> GDB substack. This effectively bridges the discontinuity, allowing GDB
> to
> show frames like `do_interrupt_handler` that were previously missing.
>
> 3. Unify and Format GDB Output:
> Modified `gdb_interface.c` to:
> - Strip "Thread <id>" headers to present a continuous backtrace
> similar to
> the native `crash bt`.
> - Renumber stack frames sequentially (e.g., #0 to #30) instead of
> resetting
> at each stack switch.
> - Add indentation/alignment for frames where GDB omits the address
> (e.g.,
> inline functions) to improve readability.
>
> 4. Prevent Invalid Thread Creation:
> Added checks to ensure a GDB substack is only created if the Program
> Counter
> (PC) is non-zero, preventing the display of "corrupt" or empty threads.
>
> Tested on: Android 6.x ARM64
>
> Signed-off-by: lipengfei28 <lipengfei28(a)xiaomi.com>
> ---
> arm64.c | 117 +++++++++++++++++++++++++++++++++++++------
> gdb_interface.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 229 insertions(+), 18 deletions(-)
>
> diff --git a/arm64.c b/arm64.c
> index c125655..f842739 100644
> --- a/arm64.c
> +++ b/arm64.c
> @@ -3026,6 +3026,9 @@ static char *arm64_exception_functions[] = {
> "do_el0_irq_bp_hardening",
> "do_sp_pc_abort",
> "handle_bad_stack",
> + "el1h_64_sync",
> + "el1h_64_irq",
> + "el1h_64_error",
> NULL
> };
>
> @@ -3123,6 +3126,11 @@ arm64_print_stackframe_entry(struct bt_info *bt,
> int level, struct arm64_stackfr
>
> fprintf(ofp, "\n");
>
> + if (STREQ(name, "el1h_64_irq") || STREQ(name, "el1h_64_sync"))
> + if (arm64_is_kernel_exception_frame(bt, frame->sp)) {
> + arm64_print_exception_frame(bt, frame->sp,
> KERNEL_MODE, ofp);
> + }
> +
> if (bt->flags & BT_LINE_NUMBERS) {
> get_line_number(branch_pc, buf, FALSE);
> if (strlen(buf))
> @@ -3775,11 +3783,12 @@ arm64_back_trace_cmd(struct bt_info *bt)
> REG_SEQ(arm64_pt_regs, pc));
> SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap,
> REG_SEQ(arm64_pt_regs, sp));
> - if (!bt->machdep ||
> + if (extra_stacks_regs[extra_stacks_idx]->ur.pc &&
> + (!bt->machdep ||
> (extra_stacks_regs[extra_stacks_idx]->ur.sp !=
> ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.sp &&
> extra_stacks_regs[extra_stacks_idx]->ur.pc !=
> - ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc)) {
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc))) {
> gdb_add_substack (extra_stacks_idx++);
> }
> }
> @@ -3925,11 +3934,12 @@ arm64_back_trace_cmd_v2(struct bt_info *bt)
> REG_SEQ(arm64_pt_regs, pc));
> SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap,
> REG_SEQ(arm64_pt_regs, sp));
> - if (!bt->machdep ||
> + if (extra_stacks_regs[extra_stacks_idx]->ur.pc &&
> + (!bt->machdep ||
> (extra_stacks_regs[extra_stacks_idx]->ur.sp !=
> ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.sp &&
> extra_stacks_regs[extra_stacks_idx]->ur.pc !=
> - ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc)) {
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc))) {
> gdb_add_substack (extra_stacks_idx++);
> }
> }
> @@ -4263,8 +4273,40 @@ arm64_switch_stack(struct bt_info *bt, struct
> arm64_stackframe *frame, FILE *ofp
> if (frame->fp == 0)
> return USER_MODE;
>
> - if (!(machdep->flags & UNW_4_14))
> + if (!(machdep->flags & UNW_4_14)) {
> arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE,
> ofp);
> + } else {
> + if (!extra_stacks_regs[extra_stacks_idx]) {
> + extra_stacks_regs[extra_stacks_idx] = (struct
> user_regs_bitmap_struct *)
> + malloc(sizeof(struct
> user_regs_bitmap_struct));
> + }
> + memset(extra_stacks_regs[extra_stacks_idx], 0,
> sizeof(struct user_regs_bitmap_struct));
> + struct user_regs_bitmap_struct *ur_ptr =
> extra_stacks_regs[extra_stacks_idx];
> +
> + ulong next_fp = GET_STACK_ULONG(frame->fp);
> + ulong next_pc = GET_STACK_ULONG(frame->fp + 8);
> + ulong next_sp = frame->fp + 16;
> +
> + if (is_kernel_text(next_pc |
> ms->CONFIG_ARM64_KERNELPACMASK))
> + next_pc |= ms->CONFIG_ARM64_KERNELPACMASK;
> +
> + ur_ptr->ur.pc = next_pc;
> + ur_ptr->ur.sp = next_sp;
> + ur_ptr->ur.regs[29] = next_fp;
> +
> + SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, pc));
> + SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, sp));
> + SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, regs[0]) +
> 29);
> +
> + if (ur_ptr->ur.pc &&
> + (!bt->machdep ||
> + (ur_ptr->ur.sp !=
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.sp &&
> + ur_ptr->ur.pc !=
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc))) {
> + gdb_add_substack (extra_stacks_idx++);
> + }
> + }
>
> return KERNEL_MODE;
> }
> @@ -4300,8 +4342,40 @@ arm64_switch_stack_from_overflow(struct bt_info
> *bt, struct arm64_stackframe *fr
> if (frame->fp == 0)
> return USER_MODE;
>
> - if (!(machdep->flags & UNW_4_14))
> + if (!(machdep->flags & UNW_4_14)) {
> arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE,
> ofp);
> + } else {
> + if (!extra_stacks_regs[extra_stacks_idx]) {
> + extra_stacks_regs[extra_stacks_idx] = (struct
> user_regs_bitmap_struct *)
> + malloc(sizeof(struct
> user_regs_bitmap_struct));
> + }
> + memset(extra_stacks_regs[extra_stacks_idx], 0,
> sizeof(struct user_regs_bitmap_struct));
> + struct user_regs_bitmap_struct *ur_ptr =
> extra_stacks_regs[extra_stacks_idx];
> +
> + ulong next_fp = GET_STACK_ULONG(frame->fp);
> + ulong next_pc = GET_STACK_ULONG(frame->fp + 8);
> + ulong next_sp = frame->fp + 16;
> +
> + if (is_kernel_text(next_pc |
> ms->CONFIG_ARM64_KERNELPACMASK))
> + next_pc |= ms->CONFIG_ARM64_KERNELPACMASK;
> +
> + ur_ptr->ur.pc = next_pc;
> + ur_ptr->ur.sp = next_sp;
> + ur_ptr->ur.regs[29] = next_fp;
> +
> + SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, pc));
> + SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, sp));
> + SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, regs[0]) +
> 29);
> +
> + if (ur_ptr->ur.pc &&
> + (!bt->machdep ||
> + (ur_ptr->ur.sp !=
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.sp &&
> + ur_ptr->ur.pc !=
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc))) {
> + gdb_add_substack (extra_stacks_idx++);
> + }
> + }
>
> return KERNEL_MODE;
> }
> @@ -4556,16 +4630,27 @@ arm64_print_exception_frame(struct bt_info *bt,
> ulong pt_regs, int mode, FILE *o
> }
> memset(extra_stacks_regs[extra_stacks_idx], 0,
> sizeof(struct user_regs_bitmap_struct));
> - memcpy(&extra_stacks_regs[extra_stacks_idx]->ur,
> regs,
> - sizeof(struct arm64_pt_regs));
> - for (int i = 0; i < sizeof(struct
> arm64_pt_regs)/sizeof(long); i++)
> -
> SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap, i);
> - if (!bt->machdep ||
> - (extra_stacks_regs[extra_stacks_idx]->ur.sp !=
> - ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.sp &&
> - extra_stacks_regs[extra_stacks_idx]->ur.pc !=
> - ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc)) {
> - gdb_add_substack (extra_stacks_idx++);
> + {
> + struct user_regs_bitmap_struct *ur_ptr =
> extra_stacks_regs[extra_stacks_idx];
> + int i;
> +
> + ur_ptr->ur.pc = regs->pc;
> + ur_ptr->ur.sp = regs->sp;
> + ur_ptr->ur.pstate = regs->pstate;
> + for (i = 0; i < 31; i++)
> + ur_ptr->ur.regs[i] = regs->regs[i];
> +
> + for (i = 0; i < 34; i++)
> + SET_BIT(ur_ptr->bitmap, i);
> +
> + if (ur_ptr->ur.pc &&
> + (!bt->machdep ||
> + (ur_ptr->ur.sp !=
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.sp &&
> + ur_ptr->ur.pc !=
> + ((struct user_regs_bitmap_struct
> *)(bt->machdep))->ur.pc))) {
> + gdb_add_substack
> (extra_stacks_idx++);
> + }
> }
> }
> }
> diff --git a/gdb_interface.c b/gdb_interface.c
> index 9f76f85..d14144e 100644
> --- a/gdb_interface.c
> +++ b/gdb_interface.c
> @@ -16,6 +16,7 @@
> */
>
> #include "defs.h"
> +#include <ctype.h>
>
> #if !defined(GDB_10_2) && !defined(GDB_16_2)
> static void exit_after_gdb_info(void);
> @@ -779,22 +780,53 @@ strip_redirection(char *buf)
> /*
> * Command for passing strings directly to gdb.
> */
> +static void format_and_print_gdb_bt(FILE *input_fp);
> +
> void
> cmd_gdb(void)
> {
> char buf[BUFSIZE];
> char **argv;
> + int i;
>
> argv = STREQ(args[0], "gdb") ? &args[1] : &args[0];
>
> if (*argv == NULL)
> cmd_usage(pc->curcmd, SYNOPSIS);
>
> + if (STREQ(*argv, "bt")) {
> + FILE *tmp_fp;
> +
> + if ((tmp_fp = tmpfile()) == NULL) {
> + error(FATAL, "cannot create temporary file for GDB
> output\n");
> + }
> +
> + strcpy(buf, "thread apply all bt");
> + /* Append any arguments that were passed to 'bt' */
> + /* args[0] is "gdb", args[1] is "bt", so options start at
> index 2 */
> + for (i = 2; i < argcnt; i++) {
> + strcat(buf, " ");
> + strcat(buf, args[i]);
> + }
> +
> + if (!gdb_pass_through(buf, tmp_fp, GNU_RETURN_ON_ERROR)) {
> + fclose(tmp_fp);
> + error(INFO, "gdb request failed: %s\n", buf);
> + return;
> + }
> +
> + rewind(tmp_fp);
> + format_and_print_gdb_bt(tmp_fp);
> +
> + fclose(tmp_fp);
> + return;
> + }
> +
> if (STREQ(*argv, "set") && argv[1]) {
> /*
> * Intercept set commands in case something has to be
> done
> - * here or elsewhere.
> - */
> + * here or elsewhere.
> + */
> if (STREQ(argv[1], "gdb")) {
> cmd_set();
> return;
> @@ -821,6 +853,100 @@ cmd_gdb(void)
> }
> }
>
> +#define MAX_BT_LINES 1024
> +#define MAX_THREAD_BLOCKS 64
> +
> +/*
> + * Helper function to parse 'thread apply all bt' output, reverse the
> + * thread blocks, and print the result.
> + */
> +static void format_and_print_gdb_bt(FILE *input_fp)
> +{
> + char *lines[MAX_BT_LINES];
> + int line_count = 0;
> + int thread_starts[MAX_THREAD_BLOCKS];
> + int thread_count = 0;
> + char line_buffer[BUFSIZE];
> + int i, j;
> + int global_frame_cnt = 0;
> +
> + // Read all lines into memory
> + while (fgets(line_buffer, BUFSIZE, input_fp) && line_count <
> MAX_BT_LINES) {
> + if (strstr(line_buffer, "Thread ") == line_buffer) {
> + if (thread_count < MAX_THREAD_BLOCKS) {
> + thread_starts[thread_count++] = line_count;
> + }
> + }
> + lines[line_count] = strdup(line_buffer);
> + if (!lines[line_count]) {
> + error(FATAL, "strdup failed while reading gdb output\n");
> + }
> + line_count++;
> + }
> +
> + if (thread_count == 0) { // If no threads, just print everything as
> is.
> + for (i = 0; i < line_count; i++) {
> + fputs(lines[i], fp);
> + }
> + } else {
> + // Iterate threads in reverse order to fix the display order
> + for (i = thread_count - 1; i >= 0; i--) {
> + int start_line = thread_starts[i];
> + int end_line = (i == thread_count - 1) ? line_count :
> thread_starts[i+1];
> +
> + // Print thread frames, stripping header and renumbering
> + for (j = start_line; j < end_line; j++) {
> + char *line = lines[j];
> + // Skip "Thread " line
> + if (strncmp(line, "Thread ", 7) == 0) continue;
> + // Skip "Backtrace stopped" or similar noise if desired,
> + // but usually GDB prints "Backtrace stopped" at the very
> end.
> + // We'll keep it for info unless it's in the middle of
> our merge.
> + // Actually, duplicate frames or stops might appear.
> + // For now, simple renumbering.
> +
> + char *ptr = line;
> + while (*ptr == ' ' || *ptr == '\t') ptr++;
> +
> + if (*ptr == '#') {
> + // Frame line: #0 0x...
> + char *rest = ptr + 1;
> + while (isdigit(*rest)) rest++; // Skip old number
> +
> + // Parse content to check for address
> + char *p = rest;
> + while (*p == ' ' || *p == '\t') p++;
> +
> + int has_addr = (strncmp(p, "0x", 2) == 0);
> +
> + // Print number
> + fprintf(fp, "#%-3d", global_frame_cnt++);
> +
> + // Align if address is missing
> + if (!has_addr) {
> + // Align function name with frames that have
> addresses
> + // 64-bit: 0x... (18 chars) + " in " (4 chars) =
> 22 chars
> + // 32-bit: 0x... (10 chars) + " in " (4 chars) =
> 14 chars
> + int width = (machdep->bits == 64) ? 22 : 14;
> + for (int k = 0; k < width; k++) fputc(' ', fp);
> + }
> +
> + fputs(rest, fp);
> + } else {
> + // Other lines (e.g. variable info, code)
> + fputs(line, fp);
> + }
> + }
> + }
> + }
> +
> + // Cleanup
> + for (i = 0; i < line_count; i++) {
> + free(lines[i]);
> + }
> +}
> +
> +
> /*
> * The gdb target_xfer_memory() has a hook installed to re-route
> * all memory accesses back here; reads of 1 or 4 bytes come primarily
> --
> 2.34.1
>
1 week, 2 days
Re: [PATCH v2] sys: Display livepatch transition status in KERNEL line
by Tao Liu
Hi Fuli,
On Thu, Feb 12, 2026 at 6:57 PM Fuli Qi (Fujitsu) <qi.fuli(a)fujitsu.com> wrote:
>
> Hello Tao Liu,
>
> Thank you for the review.
> Could you please kindly proceed with merging this patch?
Sure, I will merge it today, sorry for the delay.
Thanks,
Tao Liu
>
> Best,
> Fuli
>
> > -----Original Message-----
> > From: Tao Liu <ltao(a)redhat.com>
> > Sent: Tuesday, December 30, 2025 1:32 PM
> > To: Motomasa Suzuki <moto0123moto(a)gmail.com>
> > Cc: devel(a)lists.crash-utility.osci.io; Qi, Fuli/斉 福利 <qi.fuli(a)fujitsu.com>
> > Subject: Re: [PATCH v2] sys: Display livepatch transition status in KERNEL line
> >
> > [You don't often get email from ltao(a)redhat.com. Learn why this is important at
> > https://aka.ms/LearnAboutSenderIdentification ]
> >
> > On Tue, Dec 23, 2025 at 6:51 PM Motomasa Suzuki
> > <moto0123moto(a)gmail.com> wrote:
> > >
> > > This commit enhances the 'sys' command to show if a kernel livepatch
> > > is currently in a transition phase, directly within the KERNEL output line.
> > >
> > > Currently, diagnosing system state during or immediately after
> > > livepatch operations can be ambiguous. While 'livepatch' is indicated
> > > by '[LIVEPATCH]', there's no direct indicator within 'crash' itself to
> > > show if a livepatch is actively applying, reverting, or in some other
> > > transient state. This lack of immediate visibility can complicate
> > > crash analysis, as the system might be in an inconsistent state due to
> > > an ongoing patch application/reversion.
> > >
> > > This change introduces a new '[TRANSITION]' flag which appears next to
> > > '[LIVEPATCH]' and '[TAINTED]' in the 'sys' command output. This flag
> > > is set if the livepatch subsystem indicates an in-progress transition
> > > (e.g., as exposed via '/sys/kernel/livepatch/<patch_name>/transition').
> > >
> > > Example 'sys' output with this change:
> > > KERNEL: /usr/lib/debug/lib/modules/<version_name>/vmlinux
> > > [LIVEPATCH] [TRANSITION] [TAINTED]
> > >
> > > This enhancement provides critical, at-a-glance information for
> > > developers and administrators, allowing them to quickly ascertain if
> > > ongoing livepatch activity might be influencing a system's behavior
> > > during crash investigations. It directly leverages existing kernel
> > > livepatch status infrastructure to enrich the crash utility's
> > > diagnostic capabilities.
> > >
> > >
> > > Changes v1 -> v2:
> > > - Reworked klp_patches list traversal to use the crash utility’s
> > > do_list() helper instead of manual list handling.
> >
> > The v2 LGTM, so ack.
> >
> > Thanks,
> > Tao Liu
> > >
> > > Signed-off-by: Motomasa Suzuki <moto0123moto(a)gmail.com>
> > > ---
> > > defs.h | 1 +
> > > kernel.c | 75
> > >
> > +++++++++++++++++++++++++++++++++++++++++++++++++++
> > +++--
> > > 2 files changed, 74 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/defs.h b/defs.h
> > > index ceed3a9..cfdfa08 100644
> > > --- a/defs.h
> > > +++ b/defs.h
> > > @@ -2289,6 +2289,7 @@ struct offset_table { /* stash of
> > commonly-used offsets */
> > > long bpf_ringbuf_consumer_pos;
> > > long bpf_ringbuf_nr_pages;
> > > long hrtimer_clock_base_index;
> > > + long klp_patch_list;
> > > };
> > >
> > > struct size_table { /* stash of commonly-used sizes */
> > > diff --git a/kernel.c b/kernel.c
> > > index bb148d0..ccc4b3d 100644
> > > --- a/kernel.c
> > > +++ b/kernel.c
> > > @@ -464,6 +464,11 @@ kernel_init()
> > > "list_head.next offset: %ld: list command may fail\n",
> > > OFFSET(list_head_next));
> > >
> > > + if (STRUCT_EXISTS("klp_patch")) {
> > > + if (MEMBER_EXISTS("klp_patch", "list"))
> > > + MEMBER_OFFSET_INIT(klp_patch_list, "klp_patch",
> > "list");
> > > + }
> > > +
> > > MEMBER_OFFSET_INIT(hlist_node_next, "hlist_node", "next");
> > > MEMBER_OFFSET_INIT(hlist_node_pprev, "hlist_node", "pprev");
> > > STRUCT_SIZE_INIT(hlist_head, "hlist_head"); @@ -5689,6
> > > +5694,70 @@ is_livepatch(void)
> > > return FALSE;
> > > }
> > >
> > > +struct klp_transition_ctx {
> > > + ulong transition_patch;
> > > + int found;
> > > +};
> > > +
> > > +static int
> > > +klp_transition_match(void *entry, void *data) {
> > > + struct klp_transition_ctx *ctx = data;
> > > +
> > > + if ((ulong)entry == ctx->transition_patch) {
> > > + ctx->found = TRUE;
> > > + return TRUE;
> > > + }
> > > +
> > > + return FALSE;
> > > +}
> > > +
> > > +static int
> > > +is_livepatch_transition(void)
> > > +{
> > > + struct kernel_list_head head;
> > > + struct list_data ld;
> > > + struct klp_transition_ctx ctx;
> > > + ulong transition_patch;
> > > + ulong list_addr;
> > > + int ret;
> > > +
> > > + if (!try_get_symbol_data("klp_transition_patch",
> > > + sizeof(ulong), &transition_patch) || !transition_patch)
> > > + return FALSE;
> > > +
> > > + if (!STRUCT_EXISTS("klp_patch")
> > || !VALID_MEMBER(klp_patch_list) ||
> > > + !kernel_symbol_exists("klp_patches"))
> > > + return TRUE;
> > > +
> > > + list_addr = symbol_value("klp_patches");
> > > + if (!readmem(list_addr, KVADDR, &head, sizeof(head), "klp_patches",
> > > + RETURN_ON_ERROR | QUIET))
> > > + return TRUE;
> > > +
> > > + if (!head.next || head.next == (void *)list_addr)
> > > + return TRUE;
> > > +
> > > + BZERO(&ctx, sizeof(ctx));
> > > + ctx.transition_patch = transition_patch;
> > > +
> > > + BZERO(&ld, sizeof(ld));
> > > + ld.flags =
> > LIST_CALLBACK|CALLBACK_RETURN|RETURN_ON_LIST_ERROR|
> > > + RETURN_ON_DUPLICATE|LIST_ALLOCATE;
> > > + ld.start = (ulong)head.next;
> > > + ld.end = list_addr;
> > > + ld.member_offset = OFFSET(list_head_next);
> > > + ld.list_head_offset = OFFSET(klp_patch_list);
> > > + ld.callback_func = klp_transition_match;
> > > + ld.callback_data = &ctx;
> > > +
> > > + ret = do_list(&ld);
> > > + if (ret < 0)
> > > + return FALSE;
> > > +
> > > + return ctx.found;
> > > +}
> > > +
> > > /*
> > > * Display system stats at init-time or for the sys command.
> > > */
> > > @@ -5732,17 +5801,19 @@ display_sys_stats(void)
> > > }
> > > } else {
> > > if (pc->system_map) {
> > > - fprintf(fp, " SYSTEM MAP: %s%s%s\n",
> > pc->system_map,
> > > + fprintf(fp, " SYSTEM MAP: %s%s%s%s\n",
> > > + pc->system_map,
> > > is_livepatch() ? " [LIVEPATCH]" : "",
> > > + is_livepatch_transition() ? "
> > > + [TRANSITION]" : "",
> > > is_kernel_tainted() ? " [TAINTED]" : "");
> > > fprintf(fp, "DEBUG KERNEL: %s %s\n",
> > > pc->namelist_orig ?
> > > pc->namelist_orig : pc->namelist,
> > >
> > debug_kernel_version(pc->namelist));
> > > } else
> > > - fprintf(fp, " KERNEL: %s%s%s\n",
> > pc->namelist_orig ?
> > > + fprintf(fp, " KERNEL: %s%s%s%s\n",
> > pc->namelist_orig ?
> > > pc->namelist_orig : pc->namelist,
> > > is_livepatch() ? " [LIVEPATCH]" : "",
> > > + is_livepatch_transition() ? "
> > > + [TRANSITION]" : "",
> > > is_kernel_tainted() ? " [TAINTED]" : "");
> > > }
> > >
> > > --
> > > 2.47.3
> > >
>
1 week, 2 days
[PATCH] maple_tree: add support for maple_tree.c output to respect the global radix and per-command -x/d flags
by stalexan@redhat.com
The commands that generate maple tree output do not currently respect the
global radix setting, nor do they respect the -x output flag. This
patch aims to fix this inconsistency, so that the output commands
respect both the global radix setting as well as the per-command -x/d
flags.
Signed-off-by: Sterling Alexander <stalexan(a)redhat.com>
---
maple_tree.c | 93 ++++++++++++++++++++++++++++++++++------------------
1 file changed, 61 insertions(+), 32 deletions(-)
diff --git a/maple_tree.c b/maple_tree.c
index 8c804d0..3b11ddd 100644
--- a/maple_tree.c
+++ b/maple_tree.c
@@ -67,12 +67,15 @@ struct req_entry *fill_member_offsets(char *);
void dump_struct_members_fast(struct req_entry *, int, ulong);
void dump_struct_members_for_tree(struct tree_data *, int, ulong);
-static void mt_dump_range(ulong min, ulong max, uint depth)
+static void mt_dump_range(ulong min, ulong max, uint depth, int radix)
{
- if (min == max)
- fprintf(fp, "%.*s%lu: ", depth * 2, spaces, min);
- else
- fprintf(fp, "%.*s%lu-%lu: ", depth * 2, spaces, min, max);
+ if (min == max) {
+ fprintf(fp, (radix == 16) ? "%.*s%lx: " : "%.*s%lu: ",
+ depth * 2, spaces, min);
+ } else {
+ fprintf(fp, (radix == 16) ? "%.*s%lx-%lx: " : "%.*s%lu-%lu: ",
+ depth * 2, spaces, min, max);
+ }
}
static inline bool mt_is_reserved(ulong entry)
@@ -92,13 +95,28 @@ static uint mt_height(char *mt_buf)
>> MT_FLAGS_HEIGHT_OFFSET;
}
-static void dump_mt_range64(char *mr64_buf)
+/*
+ * Determine the output radix for maple tree display.
+ * Priority: 1) -x/-d flags, 2) global pc->output_radix
+ */
+static inline int mt_output_radix(struct tree_data *td)
+{
+ if (td) {
+ if (td->flags & TREE_STRUCT_RADIX_10)
+ return 10;
+ else if (td->flags & TREE_STRUCT_RADIX_16)
+ return 16;
+ }
+ return pc->output_radix;
+}
+
+static void dump_mt_range64(char *mr64_buf, int radix)
{
int i;
fprintf(fp, " contents: ");
for (i = 0; i < mt_slots[maple_range_64] - 1; i++)
- fprintf(fp, "%p %lu ",
+ fprintf(fp, (radix == 16) ? "%p %lx " : "%p %lu ",
VOID_PTR(mr64_buf + OFFSET(maple_range_64_slot)
+ sizeof(void *) * i),
ULONG(mr64_buf + OFFSET(maple_range_64_pivot)
@@ -107,14 +125,15 @@ static void dump_mt_range64(char *mr64_buf)
+ sizeof(void *) * i));
}
-static void dump_mt_arange64(char *ma64_buf)
+static void dump_mt_arange64(char *ma64_buf, int radix)
{
int i;
fprintf(fp, " contents: ");
for (i = 0; i < mt_slots[maple_arange_64]; i++)
- fprintf(fp, "%lu ", ULONG(ma64_buf + OFFSET(maple_arange_64_gap)
- + sizeof(ulong) * i));
+ fprintf(fp, (radix == 16) ? "%lx " : "%lu ",
+ ULONG(ma64_buf + OFFSET(maple_arange_64_gap)
+ + sizeof(ulong) * i));
fprintf(fp, "| %02X %02X| ",
UCHAR(ma64_buf + OFFSET(maple_arange_64_meta) +
@@ -123,7 +142,7 @@ static void dump_mt_arange64(char *ma64_buf)
OFFSET(maple_metadata_gap)));
for (i = 0; i < mt_slots[maple_arange_64] - 1; i++)
- fprintf(fp, "%p %lu ",
+ fprintf(fp, (radix == 16) ? "%p %lx " : "%p %lu ",
VOID_PTR(ma64_buf + OFFSET(maple_arange_64_slot) +
sizeof(void *) * i),
ULONG(ma64_buf + OFFSET(maple_arange_64_pivot) +
@@ -132,25 +151,27 @@ static void dump_mt_arange64(char *ma64_buf)
sizeof(void *) * i));
}
-static void dump_mt_entry(ulong entry, ulong min, ulong max, uint depth)
+static void dump_mt_entry(ulong entry, ulong min, ulong max, uint depth, int radix)
{
- mt_dump_range(min, max, depth);
+ mt_dump_range(min, max, depth, radix);
- if (xa_is_value(entry))
- fprintf(fp, "value %ld (0x%lx) [0x%lx]\n", xa_to_value(entry),
+ if (xa_is_value(entry)) {
+ fprintf(fp, (radix == 16) ? "value 0x%lx [0x%lx]\n" : "value %ld [0x%lx]\n",
xa_to_value(entry), entry);
- else if (xa_is_zero(entry))
- fprintf(fp, "zero (%ld)\n", xa_to_internal(entry));
- else if (mt_is_reserved(entry))
+ } else if (xa_is_zero(entry)) {
+ fprintf(fp, (radix == 16) ? "zero (0x%lx)\n" : "zero (%ld)\n",
+ xa_to_internal(entry));
+ } else if (mt_is_reserved(entry)) {
fprintf(fp, "UNKNOWN ENTRY (0x%lx)\n", entry);
- else
- fprintf(fp, "0x%lx\n", entry);
+ } else {
+ fprintf(fp, (radix == 16) ? "0x%lx\n" : "%lu\n", entry);
+ }
}
static void dump_mt_node(ulong maple_node, char *node_data, uint type,
- ulong min, ulong max, uint depth)
+ ulong min, ulong max, uint depth, int radix)
{
- mt_dump_range(min, max, depth);
+ mt_dump_range(min, max, depth, radix);
fprintf(fp, "node 0x%lx depth %d type %d parent %p",
maple_node, depth, type,
@@ -169,6 +190,7 @@ static void do_mt_range64(ulong entry, ulong min, ulong max,
int i;
int len = strlen(path);
struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+ int radix = mt_output_radix(td);
char *mr64_buf;
if (SIZE(maple_node) > MAPLE_BUFSIZE)
@@ -180,7 +202,7 @@ static void do_mt_range64(ulong entry, ulong min, ulong max,
mr64_buf = node_buf + OFFSET(maple_node_mr64);
if (td && td->flags & TREE_STRUCT_VERBOSE) {
- dump_mt_range64(mr64_buf);
+ dump_mt_range64(mr64_buf, radix);
}
for (i = 0; i < mt_slots[maple_range_64]; i++) {
@@ -230,6 +252,7 @@ static void do_mt_arange64(ulong entry, ulong min, ulong max,
int i;
int len = strlen(path);
struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+ int radix = mt_output_radix(td);
char *ma64_buf;
if (SIZE(maple_node) > MAPLE_BUFSIZE)
@@ -241,7 +264,7 @@ static void do_mt_arange64(ulong entry, ulong min, ulong max,
ma64_buf = node_buf + OFFSET(maple_node_ma64);
if (td && td->flags & TREE_STRUCT_VERBOSE) {
- dump_mt_arange64(ma64_buf);
+ dump_mt_arange64(ma64_buf, radix);
}
for (i = 0; i < mt_slots[maple_arange_64]; i++) {
@@ -286,6 +309,7 @@ static void do_mt_entry(ulong entry, ulong min, ulong max, uint depth,
int print_radix = 0, i;
static struct req_entry **e = NULL;
struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+ int output_radix = mt_output_radix(td);
if (ops->entry && entry)
ops->entry(entry, entry, path, max, ops->private);
@@ -306,12 +330,15 @@ static void do_mt_entry(ulong entry, ulong min, ulong max, uint depth,
td->count++;
if (td->flags & TREE_STRUCT_VERBOSE) {
- dump_mt_entry(entry, min, max, depth);
- } else if (td->flags & VERBOSE && entry)
- fprintf(fp, "%lx\n", entry);
- if (td->flags & TREE_POSITION_DISPLAY && entry)
- fprintf(fp, " index: %ld position: %s/%u\n",
+ dump_mt_entry(entry, min, max, depth, output_radix);
+ } else if (td->flags & VERBOSE && entry) {
+ fprintf(fp, (output_radix == 16) ? "%lx\n" : "%lu\n", entry);
+ }
+ if (td->flags & TREE_POSITION_DISPLAY && entry) {
+ fprintf(fp, (output_radix == 16) ? " index: %lx position: %s/%u\n" :
+ " index: %ld position: %s/%u\n",
++(*global_index), path, index);
+ }
if (td->structname && entry) {
if (td->flags & TREE_STRUCT_RADIX_10)
@@ -319,7 +346,7 @@ static void do_mt_entry(ulong entry, ulong min, ulong max, uint depth,
else if (td->flags & TREE_STRUCT_RADIX_16)
print_radix = 16;
else
- print_radix = 0;
+ print_radix = output_radix;
for (i = 0; i < td->structname_args; i++) {
switch (count_chars(td->structname[i], '.')) {
@@ -348,6 +375,7 @@ static void do_mt_node(ulong entry, ulong min, ulong max,
uint i;
char node_buf[MAPLE_BUFSIZE];
struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+ int radix = mt_output_radix(td);
if (SIZE(maple_node) > MAPLE_BUFSIZE)
error(FATAL, "MAPLE_BUFSIZE should be larger than maple_node struct");
@@ -356,7 +384,7 @@ static void do_mt_node(ulong entry, ulong min, ulong max,
"mt_dump_node read maple_node", FAULT_ON_ERROR);
if (td && td->flags & TREE_STRUCT_VERBOSE) {
- dump_mt_node(maple_node, node_buf, type, min, max, depth);
+ dump_mt_node(maple_node, node_buf, type, min, max, depth, radix);
}
switch (type) {
@@ -457,7 +485,8 @@ static void do_maple_tree_dump(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_maple_tree_info *info = private;
- fprintf(fp, "[%lu] %lx\n", index, slot);
+ fprintf(fp, (pc->output_radix == 16) ? "[%lx] %lx\n" : "[%lu] %lx\n",
+ index, slot);
info->count++;
}
--
2.52.0
1 week, 2 days
Re: [PATCH] Ensure all child processes are properly cleaned up in restore_ifile_sanity
by Lianbo Jiang
Hi, Feng
Thank you for the patch. This looks good to me, so: Ack.
Lianbo
On 1/28/26 9:42 AM, devel-request(a)lists.crash-utility.osci.io wrote:
> Date: Wed, 3 Dec 2025 10:35:57 +0800
> From: neilfsun<loyou85(a)gmail.com>
> Subject: [Crash-utility] [PATCH] Ensure all child processes are
> properly cleaned up in restore_ifile_sanity
> To:devel@lists.crash-utility.osci.io
> Cc: neilfsun<neilfsun(a)tencent.com>, Feng Sun<loyou85(a)gmail.com>
> Message-ID:<20251203023557.55333-1-neilfsun(a)tencent.com>
>
> Run input file with pipe cmdlines will get nothing output.
>
> crash> p cpu_info:0 | grep 'x86 ='
> x86 = 0x19,
> crash> cat crashrc
> p cpu_info:0 | grep 'x86 ='
> p cpu_info:0 | grep 'x86 ='
> crash> < crashrc
> crash> p cpu_info:0 | grep 'x86 ='
> x86 = 0x19,
> crash> p cpu_info:0 | grep 'x86 ='
>
> crash>
>
> Signed-off-by: neilfsun<neilfsun(a)tencent.com>
> Signed-off-by: Feng Sun<loyou85(a)gmail.com>
> ---
> cmdline.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/cmdline.c b/cmdline.c
> index 65da04c..4add548 100644
> --- a/cmdline.c
> +++ b/cmdline.c
> @@ -1285,6 +1285,8 @@ restore_ifile_sanity(void)
> close_tmpfile2();
> }
>
> + wait_for_children(ALL_CHILDREN);
> +
> restore_gdb_sanity();
>
> free_all_bufs();
> -- 2.50.1
1 week, 2 days
Re: [PATCH] Fix prompt output interfering with piped commands in input files
by Lianbo Jiang
Hi, Feng
Thank you for the patch.
For this one: Ack.
Lianbo
On 1/28/26 9:42 AM, devel-request(a)lists.crash-utility.osci.io wrote:
> Date: Thu, 4 Dec 2025 14:34:37 +0800
> From: neilfsun<loyou85(a)gmail.com>
> Subject: [Crash-utility] [PATCH] Fix prompt output interfering with
> piped commands in input files
> To:devel@lists.crash-utility.osci.io
> Cc: neilfsun<neilfsun(a)tencent.com>, Feng Sun<loyou85(a)gmail.com>
> Message-ID:<20251204063437.4008-1-neilfsun(a)tencent.com>
>
> When executing piped commands from input files, the prompt text was
> incorrectly written to the pipe instead of stdout.
> This caused commands read the prompt as their first line,
> consuming it instead of the actual command output.
>
> crash> p cpu_info:0 | head -1
> per_cpu(cpu_info, 0) = $29 = {
> crash> p cpu_info:0 | head -2
> per_cpu(cpu_info, 0) = $4 = {
> x86 = 0x6,
> crash> cat crashrc1
> p cpu_info:0 | head -1
> crash> < crashrc1
> crash> p cpu_info:0 | head -1
>
> crash> cat crashrc2
> p cpu_info:0 | head -2
> crash> < crashrc2
> crash> p cpu_info:0 | head -2
> per_cpu(cpu_info, 0) = $30 = {
> crash>
>
> Fix: Output prompt to incoming_fp (stdout) instead of fp (which may be the pipe
> after setup_redirect()), ensuring prompts display on terminal while command output
> goes to the pipe as intended.
>
> Signed-off-by: neilfsun<neilfsun(a)tencent.com>
> Signed-off-by: Feng Sun<loyou85(a)gmail.com>
> ---
> cmdline.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/cmdline.c b/cmdline.c
> index 4add548..030ae56 100644
> --- a/cmdline.c
> +++ b/cmdline.c
> @@ -1488,8 +1488,8 @@ exec_input_file(void)
> continue;
>
> if (!(pc->flags & SILENT)) {
> - fprintf(fp, "%s%s", pc->prompt, buf);
> - fflush(fp);
> + fprintf(incoming_fp, "%s%s", pc->prompt, buf);
> + fflush(incoming_fp);
> }
>
> exec_command();
> -- 2.50.1
1 week, 2 days
Re: [PATCH] Reapply "vmcoreinfo: read vmcoreinfo using 'vmcoreinfo_data' when unavailable in elf note"
by Misbah Anjum N
> Commit 7636c13 ("vmcoreinfo: read vmcoreinfo using 'vmcoreinfo_data'
> when unavailable in elf note") moved the vmcoreinfo reading to always
> read from memory instead of relying on diskdump/netdump's local
> handlers. This was later reverted to fix regression in X86_64 kslar
> images.
>
> Reintroduce the `vmcoreinfo_read_from_memory` as fallback to
> diskdump/netdump vmcores.
>
> This reverts commit 72e2776caf1ca41dffcc8aba11c55c636565725b.
>
> Cc: Aditya Gupta <adityag(a)linux.ibm.com>
> Cc: Tao Liu <ltao(a)redhat.com>
> Signed-off-by: Shivang Upadhyay <shivangu(a)linux.ibm.com>
Tested-by: Misbah Anjum N <misanjum(a)linux.ibm.com>
Hi,
I have tested this patch successfully on the following platform:
Platform: IBM PowerPC 64LE (PPC64LE)
Architecture: ppc64le
Host Kernel: 6.18.0-8-generic
Guest Kernel: 6.18.0-8-generic
OS: Ubuntu 26.04 (Resolute Raccoon)
Crash version: 9.0.1 (patched)
Compiler: gcc 15.2.0
GDB version: 16.2 (embedded)
Test cases verified:
--------------------
1. virsh dump --memory-only (elf, kdump-zlib compression)
2. virsh dump --memory-only --bypass-cache (elf, kdump-zlib compression)
Test results:
------------
- Patch applied cleanly to crash-9.0.1 (with offsets)
- Build completed successfully with all dependencies
- vmcoreinfo_read_from_memory fallback is active in diskdump.c and netdump.c
- Successfully reads vmcoreinfo from memory when ELF note method is unavailable
- Resolves the X86_64 kslar image regression
- No regressions detected
Test logs (trimmed):
-------------------
# virsh dump ubuntu2604 vmcore-mem --memory-only
Domain 'ubuntu2604' dumped to vmcore-mem
# crash /usr/lib/debug/boot/vmlinux-6.18.0-8-generic vmcore-mem
crash 9.0.1
...
KERNEL: /usr/lib/debug/boot/vmlinux-6.18.0-8-generic
DUMPFILE: vmcore-mem
...
crash> bt
PID: 0 TASK: c0000000038b1580 CPU: 0 COMMAND: "swapper/0"
[NIP : plpar_hcall_norets_notrace+24]
[LR : check_and_cede_processor+72]
...
The patch works as intended and fixes the vmcoreinfo reading issue.
Thanks,
Misbah Anjum N
1 week, 2 days
[PATCH RFC] sys: Display livepatch transition status in KERNEL line
by Motomasa Suzuki
This commit enhances the 'sys' command to show if a kernel livepatch is
currently in a transition phase, directly within the KERNEL output line.
Currently, diagnosing system state during or immediately after livepatch
operations can be ambiguous. While 'livepatch' is indicated by
'[LIVEPATCH]', there's no direct indicator within 'crash' itself to show
if a livepatch is actively applying, reverting, or in some other
transient state. This lack of immediate visibility can complicate crash
analysis, as the system might be in an inconsistent state due to an
ongoing patch application/reversion.
This change introduces a new '[TRANSITION]' flag which appears next to
'[LIVEPATCH]' and '[TAINTED]' in the 'sys' command output. This flag is
set if the livepatch subsystem indicates an in-progress transition
(e.g., as exposed via '/sys/kernel/livepatch/<patch_name>/transition').
Example 'sys' output with this change:
KERNEL: /usr/lib/debug/lib/modules/<version_name>/vmlinux [LIVEPATCH]
[TRANSITION] [TAINTED]
This enhancement provides critical, at-a-glance information for
developers and administrators, allowing them to quickly ascertain if
ongoing livepatch activity might be influencing a system's behavior
during crash investigations. It directly leverages existing kernel
livepatch status infrastructure to enrich the crash utility's diagnostic
capabilities.
Signed-off-by: Motomasa Suzuki <suzuki.motomasa(a)fujitsu.com>
---
defs.h | 1 +
kernel.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h
index ab4aee8..b459d4d 100644
--- a/defs.h
+++ b/defs.h
@@ -2280,6 +2280,7 @@ struct offset_table { /* stash of commonly-used offsets */
long bpf_ringbuf_map_rb;
long bpf_ringbuf_consumer_pos;
long bpf_ringbuf_nr_pages;
+ long klp_patch_list;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/kernel.c b/kernel.c
index 13f3374..1ca18e4 100644
--- a/kernel.c
+++ b/kernel.c
@@ -461,7 +461,10 @@ kernel_init()
error(WARNING,
"list_head.next offset: %ld: list command may fail\n",
OFFSET(list_head_next));
-
+ if (STRUCT_EXISTS("klp_patch")) {
+ if (MEMBER_EXISTS("klp_patch", "list"))
+ MEMBER_OFFSET_INIT(klp_patch_list, "klp_patch", "list");
+ }
MEMBER_OFFSET_INIT(hlist_node_next, "hlist_node", "next");
MEMBER_OFFSET_INIT(hlist_node_pprev, "hlist_node", "pprev");
STRUCT_SIZE_INIT(hlist_head, "hlist_head");
@@ -5681,6 +5684,48 @@ is_livepatch(void)
return FALSE;
}
+#define KLP_PATCH_ITER_LIMIT 1024
+
+static int
+is_livepatch_transition(void)
+{
+ struct kernel_list_head head;
+ struct kernel_list_head node;
+ ulong transition_patch;
+ ulong list_addr;
+ ulong current;
+ ulong patch_addr;
+ int loops;
+
+ if (!try_get_symbol_data("klp_transition_patch", sizeof(ulong),
+ &transition_patch) || !transition_patch)
+ return FALSE;
+
+ if (!kernel_symbol_exists("klp_patches") || !VALID_MEMBER(klp_patch_list))
+ return FALSE;
+
+ list_addr = symbol_value("klp_patches");
+ if (!readmem(list_addr, KVADDR, &head, sizeof(head), "klp_patches",
+ RETURN_ON_ERROR | QUIET))
+ return FALSE;
+
+ for (current = (ulong)head.next, loops = 0;
+ current && current != list_addr && loops < KLP_PATCH_ITER_LIMIT;
+ current = (ulong)node.next, loops++) {
+
+ if (!readmem(list_addr, KVADDR, &head, sizeof(head),
+ "klp_patch list entry", RETURN_ON_ERROR | QUIET))
+ return FALSE;
+
+ patch_addr = current - (ulong)OFFSET(klp_patch_list);
+
+ if (patch_addr == transition_patch)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*
* Display system stats at init-time or for the sys command.
*/
@@ -5724,17 +5769,19 @@ display_sys_stats(void)
}
} else {
if (pc->system_map) {
- fprintf(fp, " SYSTEM MAP: %s%s%s\n", pc->system_map,
+ fprintf(fp, " SYSTEM MAP: %s%s%s%s\n", pc->system_map,
is_livepatch() ? " [LIVEPATCH]" : "",
+ is_livepatch_transition() ? " [TRANSITION]" : "",
is_kernel_tainted() ? " [TAINTED]" : "");
fprintf(fp, "DEBUG KERNEL: %s %s\n",
pc->namelist_orig ?
pc->namelist_orig : pc->namelist,
debug_kernel_version(pc->namelist));
} else
- fprintf(fp, " KERNEL: %s%s%s\n", pc->namelist_orig ?
+ fprintf(fp, " KERNEL: %s%s%s%s\n", pc->namelist_orig ?
pc->namelist_orig : pc->namelist,
is_livepatch() ? " [LIVEPATCH]" : "",
+ is_livepatch_transition() ? " [TRANSITION]" : "",
is_kernel_tainted() ? " [TAINTED]" : "");
}
--
2.47.3
1 week, 2 days
[PATCH 0/4] xarray: add large folio support
by Huang Shijie
The linux kernel supports the large folio for page cache by default.
But the current CRASH does not support the large folio.
So we may meet the errors when we detected the large folio sometimes,
such as in the email:
https://www.spinics.net/linux/fedora/redhat-crash-utility/msg11238.html
------------------------------
files: page_to_nid: invalid page: 0
files: page_to_nid: invalid page: 0
files: page_to_nid: invalid page: 0
files: page_to_nid: invalid page: 10
files: page_to_nid: invalid page: 10
files: page_to_nid: invalid page: 10
files: page_to_nid: invalid page: 20
files: page_to_nid: invalid page: 20
files: page_to_nid: invalid page: 20
files: page_to_nid: invalid page: 30
files: page_to_nid: invalid page: 30
files: page_to_nid: invalid page: 30
files: page_to_nid: invalid page: 40
files: page_to_nid: invalid page: 40
files: page_to_nid: invalid page: 40
files: page_to_nid: invalid page: 50
files: page_to_nid: invalid page: 50
files: page_to_nid: invalid page: 50
files: page_to_nid: invalid page: 60
files: page_to_nid: invalid page: 60
------------------------------
The first 3 patches are used to add large folio support for CRASH.
The last patch is newly version of an old patch:
it add "files -n" command.
Huang Shijie (4):
xarray: add a new parameter for do_xarray
add folio_order function
xarray: add large folio support
add "files -n" command for an inode
bpf.c | 8 +++---
defs.h | 10 ++++++-
dev.c | 4 +--
diskdump.c | 10 +++++--
filesys.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++--------
help.c | 24 ++++++++++++++++-
ipcs.c | 4 +--
kernel.c | 4 +--
memory.c | 26 ++++++++++++++++--
symbols.c | 2 ++
task.c | 4 +--
tools.c | 10 ++++---
12 files changed, 151 insertions(+), 32 deletions(-)
--
2.43.0
1 week, 3 days
[PATCH v4] add "files -n" command for an inode
by Huang Shijie
In the NUMA machine, it is useful to know the memory distribution of
an inode page cache:
How many pages in the node 0?
How many pages in the node 1?
Add "files -n" command to get the memory distribution information:
1.) Add new argument for dump_inode_page_cache_info()
2.) make page_to_nid() a global function.
3.) Add summary_inode_page() to check each page's node
information.
4.) Use print_inode_summary_info() to print the
memory distribution information of an inode.
Signed-off-by: Huang Shijie <huangsj(a)hygon.cn>
---
v3 -- > v4:
Rebase the old patch on the latest Crash code.
Tested with Hygon machine.
Changes some help message.
v3: https://www.spinics.net/linux/fedora/redhat-crash-utility/msg11238.html
---
defs.h | 1 +
filesys.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
help.c | 24 +++++++++++++++++++++-
memory.c | 4 ++--
4 files changed, 81 insertions(+), 8 deletions(-)
diff --git a/defs.h b/defs.h
index ceed3a9..fc30aa5 100644
--- a/defs.h
+++ b/defs.h
@@ -5945,6 +5945,7 @@ int dump_inode_page(ulong);
ulong valid_section_nr(ulong);
void display_memory_from_file_offset(ulonglong, long, void *);
void swap_info_init(void);
+int page_to_nid(ulong);
/*
* filesys.c
diff --git a/filesys.c b/filesys.c
index 34944e2..0405a60 100644
--- a/filesys.c
+++ b/filesys.c
@@ -49,7 +49,7 @@ static int match_file_string(char *, char *, char *);
static ulong get_root_vfsmount(char *);
static void check_live_arch_mismatch(void);
static long get_inode_nrpages(ulong);
-static void dump_inode_page_cache_info(ulong);
+static void dump_inode_page_cache_info(ulong, void *callback);
#define DENTRY_CACHE (20)
#define INODE_CACHE (20)
@@ -2240,8 +2240,38 @@ get_inode_nrpages(ulong i_mapping)
return nrpages;
}
+/* Used to collect the numa information for an inode */
+static ulong *numa_node;
+
static void
-dump_inode_page_cache_info(ulong inode)
+print_inode_summary_info(void)
+{
+ int i;
+
+ fprintf(fp, " NODE PAGES\n");
+ for (i = 0; i < vt->numnodes; i++)
+ fprintf(fp, " %2d %8ld\n", i, numa_node[i]);
+}
+
+static int
+summary_inode_page(ulong page)
+{
+ int node;
+
+ if (!is_page_ptr(page, NULL))
+ error(FATAL, "Invalid inode page(0x%lx)\n", page);
+
+ node = page_to_nid(page);
+ if (node < 0 || node >= vt->numnodes)
+ error(FATAL, "Invalid node(%d) for page(0x%lx)\n", node, page);
+
+ numa_node[node]++;
+
+ return 1;
+}
+
+static void
+dump_inode_page_cache_info(ulong inode, void *callback)
{
char *inode_buf;
ulong i_mapping, nrpages, root_rnode, xarray, count;
@@ -2284,7 +2314,7 @@ dump_inode_page_cache_info(ulong inode)
root_rnode = i_mapping + OFFSET(address_space_page_tree);
lp.index = 0;
- lp.value = (void *)&dump_inode_page;
+ lp.value = callback;
if (root_rnode)
count = do_radix_tree(root_rnode, RADIX_TREE_DUMP_CB, &lp);
@@ -2324,7 +2354,7 @@ cmd_files(void)
ref = NULL;
refarg = NULL;
- while ((c = getopt(argcnt, args, "d:R:p:c")) != EOF) {
+ while ((c = getopt(argcnt, args, "d:n:R:p:c")) != EOF) {
switch(c)
{
case 'R':
@@ -2343,11 +2373,31 @@ cmd_files(void)
display_dentry_info(value);
return;
+ case 'n':
+ if (VALID_MEMBER(address_space_page_tree) &&
+ VALID_MEMBER(inode_i_mapping)) {
+ value = htol(optarg, FAULT_ON_ERROR, NULL);
+
+ /* Allocate the array for this inode */
+ numa_node = malloc(sizeof(ulong) * vt->numnodes);
+ BZERO(numa_node, sizeof(ulong) * vt->numnodes);
+
+ dump_inode_page_cache_info(value, (void *)&summary_inode_page);
+
+ /* Print out the NUMA node information for this inode */
+ print_inode_summary_info();
+
+ free(numa_node);
+ numa_node = NULL;
+ } else
+ option_not_supported('n');
+ return;
+
case 'p':
if (VALID_MEMBER(address_space_page_tree) &&
VALID_MEMBER(inode_i_mapping)) {
value = htol(optarg, FAULT_ON_ERROR, NULL);
- dump_inode_page_cache_info(value);
+ dump_inode_page_cache_info(value, (void *)&dump_inode_page);
} else
option_not_supported('p');
return;
diff --git a/help.c b/help.c
index 91d1550..6f97289 100644
--- a/help.c
+++ b/help.c
@@ -7900,7 +7900,7 @@ NULL
char *help_files[] = {
"files",
"open files",
-"[-d dentry] | [-p inode] | [-c] [-R reference] [pid | taskp] ... ",
+"[-d dentry] | [-p inode] | [-n inode]|[-c] [-R reference] [pid | taskp] ... ",
" This command displays information about open files of a context.",
" It prints the context's current root directory and current working",
" directory, and then for each open file descriptor it prints a pointer",
@@ -7915,6 +7915,8 @@ char *help_files[] = {
" super block, file type, and full pathname.",
" -p inode given a hexadecimal inode address, dump all of its pages",
" that are in the page cache.",
+" -n inode given a hexadecimal inode address, check all the pages",
+" in the page cache, and display a NUMA node distribution.",
" -c for each open file descriptor, prints a pointer to its",
" inode, a pointer to the inode's i_mapping address_space",
" structure, the number of pages of the inode that are in",
@@ -8024,6 +8026,26 @@ char *help_files[] = {
" ca1ddde0 2eeef000 f59b91ac 3 2 82c referenced,uptodate,lru,private",
" ca36b300 3b598000 f59b91ac 4 2 82c referenced,uptodate,lru,private",
" ca202680 30134000 f59b91ac 5 2 82c referenced,uptodate,lru,private",
+" ",
+" For the inode at address ffff8898857c55a8, display the NUMA node",
+" distribution of its pages that are in the page cache:",
+" %s> files -n ffff8898857c55a8",
+" INODE NRPAGES",
+" ffff8898857c55a8 51748",
+" ",
+" NODE PAGES",
+" 0 51536",
+" 1 0",
+" 2 0",
+" 3 0",
+" 4 0",
+" 5 212",
+" 6 0",
+" 7 0",
+" 8 0",
+" 9 0",
+" 10 0",
+" 11 0",
" ",
NULL
};
diff --git a/memory.c b/memory.c
index cbc8d2f..9ba262a 100644
--- a/memory.c
+++ b/memory.c
@@ -301,7 +301,7 @@ static int dump_vm_event_state(void);
static int dump_page_states(void);
static int generic_read_dumpfile(ulonglong, void *, long, char *, ulong);
static int generic_write_dumpfile(ulonglong, void *, long, char *, ulong);
-static int page_to_nid(ulong);
+int page_to_nid(ulong);
static int get_kmem_cache_list(ulong **);
static int get_kmem_cache_root_list(ulong **);
static int get_kmem_cache_child_list(ulong **, ulong);
@@ -20054,7 +20054,7 @@ is_kmem_cache_addr_common(ulong vaddr, char *kbuf)
/*
* Kernel-config-neutral page-to-node evaluator.
*/
-static int
+int
page_to_nid(ulong page)
{
int i;
--
2.43.0
2 weeks, 2 days