Hi Oleksandr, Looks good -- queued for crash-7.2.7: https://github.com/crash-utility/crash/commit/496d503e84147a24bdaf86782ab1d8af2fd76c9e Thanks, Dave ----- Original Message ----- > Extend `timer` command to show time to expire (TTE) for each timer. > > This is useful to verify what CPU is blocked due to looping with > interrupts disabled or due to lack of resources to run the vCPU on a > hypervisor side. > > `help` output is amended accordingly. Old examples are replaced with two > new ones, both are for the vmcore from the modern RHEL8 system. > > The commit was tested on the vmcores with the following kernel versions: > > * 2.6.18-436.el5 > * 2.6.32-431.el6.x86_64 > * 3.10.0-693.11.6.el7.x86_64 > * 4.18.0-80.1.2.el8_0.x86_64 > > Signed-off-by: Oleksandr Natalenko <oleksandr@xxxxxxxxxx> > --- > defs.h | 1 + > help.c | 167 ++++++++++++------------------------ > kernel.c | 255 ++++++++++++++++++++++++++++++++++++++----------------- > tools.c | 5 +- > 4 files changed, 236 insertions(+), 192 deletions(-) > > diff --git a/defs.h b/defs.h > index ccffe58..ceb6eb7 100644 > --- a/defs.h > +++ b/defs.h > @@ -4387,6 +4387,7 @@ struct machine_specific { > #define INT_HEX (0x40) > #define LONGLONG_HEX (0x80) > #define ZERO_FILL (0x100) > +#define SLONG_DEC (0x200) > > #define INIT_TIME (1) > #define RUN_TIME (2) > diff --git a/help.c b/help.c > index 581e616..4f67ccf 100644 > --- a/help.c > +++ b/help.c > @@ -2891,128 +2891,65 @@ char *help_timer[] = { > " -C cpu Restrict the output to one or more CPUs, where multiple cpu[s] > can", > " be specified, for example, as \"1,3,5\", \"1-3\", or > \"1,3,5-7,10\".", > "\nEXAMPLES", > +" Display the timer queue on an SMP system:\n", > " %s> timer", > " JIFFIES", > -" 68102", > -" EXPIRES TIMER_LIST/TABLE FUNCTION", > -" 68346 c0241934 c01775d4 <tcp_sltimer_handler>", > -" 68379 c0241204 c01696d8 <dev_do_watchdog>", > -" 68523 c7fcdfc0 c0112d6c <process_timeout>", > -" 68718 c7fd8edc c018719c <irlmp_discovery_timer_expired>", > -" 68723 timer_table[2] c01c707c <rs_timer>", > -" 68742 c20c1f7c c0112d6c <process_timeout>", > -" 68742 c20c1f7c c0112d6c <process_timeout>", > -" 68742 c20c1f7c c0112d6c <process_timeout>", > -" 68752 c7fd1fc4 c0112d6c <process_timeout>", > -" 68752 c7fd1fc4 c0112d6c <process_timeout>", > -" 68989 c0241d40 c0168060 <neigh_periodic_timer>", > -" 69028 c2533f7c c0112d6c <process_timeout>", > -" 69134 c22dd868 c0181948 <unix_destroy_timer>", > -" 71574 c0241430 c0169ea4 <rt_check_expire>", > -" 72179 c7fb1c48 c01cb9a0 <vortex_timer>", > -" 73144 c1b17f10 c0112d6c <process_timeout>", > -" 73259 c17a5f10 c0112d6c <process_timeout>", > -" 112929 c203ff10 c0112d6c <process_timeout>", > -" 372010 c2323f7c c0112d6c <process_timeout>", > -" 372138 c2191f10 c0112d6c <process_timeout>", > -" 8653052 c1f13f10 c0112d6c <process_timeout>", > -" ", > -" Display the timer queue on a 2-cpu system:\n", > -" %s> timer", > -" TVEC_BASES[0]: c1299be0", > -" JIFFIES", > -" 18256298", > -" EXPIRES TIMER_LIST FUNCTION", > -" 18256406 cd5ddec0 c01232bb <process_timeout>", > -" 18256677 ceea93e0 c011e3cc <it_real_fn>", > -" 18256850 ceea7f64 c01232bb <process_timeout>", > -" 18258751 cd1d4f64 c01232bb <process_timeout>", > -" 18258792 cf5782f0 c011e3cc <it_real_fn>", > -" 18261266 c03c9f80 c022fad5 <rt_check_expire>", > -" 18262196 c02dc2e0 c0233329 <peer_check_expire>", > -" 18270518 ceb8bf1c c01232bb <process_timeout>", > -" 18271327 c03c9120 c0222074 <flow_cache_new_hashrnd>", > -" 18271327 c03ca580 c0233ace <ipfrag_secret_rebuild>", > -" 18272532 c02d1e18 c0129946 <delayed_work_timer_fn>", > -" 18276518 c03c9fc0 c022fd40 <rt_secret_rebuild>", > -" 18332334 ceea9970 c011e3cc <it_real_fn>", > -" 18332334 cfb6a840 c011e3cc <it_real_fn>", > -" 18665378 cec25ec0 c01232bb <process_timeout>", > -" TVEC_BASES[1]: c12a1be0", > -" JIFFIES", > -" 18256298", > -" EXPIRES TIMER_LIST FUNCTION", > -" 18256493 c02c7d00 c013dad5 <wb_timer_fn>", > -" 18256499 c12a2db8 c0129946 <delayed_work_timer_fn>", > -" 18277900 ceebaec0 c01232bb <process_timeout>", > -" 18283769 cf739f64 c01232bb <process_timeout>", > -" 18331902 cee8af64 c01232bb <process_timeout>", > +" 4296291038", > +" ...", > +" TIMER_BASES[1][BASE_STD]: ffff9801aba5aa00", > +" EXPIRES TTE TIMER_LIST FUNCTION", > +" 4296282997 -8041 ffff9801aba55ce0 ffffffff83a3bda0 > <mce_timer_fn>", > +" 4296283104 -7934 ffff97fd84bd35e0 ffffffff83ac6b70 > <delayed_work_timer_fn>", > +" 4296291061 23 ffffa6b283967de0 ffffffff83b29880 > <process_timeout>", > +" 4296291112 74 ffff9800c9b62ad8 ffffffff83e6b550 > <cursor_timer_handler>", > +" 4296291345 307 ffff980186d5ef88 ffffffff84146b80 > <tcp_keepalive_timer>", > +" 4296291484 446 ffff9801a7c54740 ffffffff84147f50 > <tcp_write_timer>", > +" 4296291997 959 ffffffffc073f880 ffffffff83ac6b70 > <delayed_work_timer_fn>", > +" 4296296213 5175 ffffa6b28339be18 ffffffff83b29880 > <process_timeout>", > +" 4296304383 13345 ffff980194ca72a8 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296305724 14686 ffff980194ca6918 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296306036 14998 ffff980194ca6d58 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296306883 15845 ffff980194ca7e58 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296307588 16550 ffff9801aaa27e58 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296307625 16587 ffff980194ca6a28 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296313542 22504 ffff980194ca7c38 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296317680 26642 ffff9800c9149c58 ffffffff840da870 > <neigh_timer_handler>", > +" 4296317744 26706 ffff9801a5354468 ffffffff83ac6b70 > <delayed_work_timer_fn>", > +" 4296343322 52284 ffff980194ca63c8 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296343581 52543 ffff980194ca7088 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296343597 52559 ffff9801aaa274c8 ffffffff8412e4e0 > <tw_timer_handler>", > +" 4296714205 423167 ffffffff84caf3c0 ffffffff83ac6b70 > <delayed_work_timer_fn>", > +" TIMER_BASES[1][BASE_DEF]: ffff9801aba5bc80", > +" EXPIRES TTE TIMER_LIST FUNCTION", > +" 4296291264 226 ffffffff855eb238 ffffffff83c08fb0 > <writeout_period>", > +" 4296319997 28959 ffffffffc06ede40 ffffffff83ac6b70 > <delayed_work_timer_fn>", > +" 4296506084 215046 ffff9801aba629c8 ffffffff83ac5ea0 > <idle_worker_timeout>", > +" ...", > " ", > " Display a new-style hrtimer queue:\n", > " %s> timer -r", > -" CPU: 0 HRTIMER_CPU_BASE: c1e03fc0", > -" CLOCK: 0 HRTIMER_CLOCK_BASE: c1e03fc4 [ktime_get_real]", > -" (empty)", > -" ", > -" CLOCK: 1 HRTIMER_CLOCK_BASE: c1e03ff0 [ktime_get]", > -" CURRENT", > -" 322894000000", > -" SOFTEXPIRES EXPIRES HRTIMER FUNCTION", > -" 322895000000 322895000000 c1e04080 c04833e0 <tick_sched_timer>", > -" 324022213609 324022213609 c1e041c0 c04b17d0 <watchdog_timer_fn>", > -" 326766922781 326766972781 f3a45f44 c0477ed0 <hrtimer_wakeup>", > -" 364516801997 364516851997 f43bbf44 c0477ed0 <hrtimer_wakeup>", > -" ", > -" CLOCK: 2 HRTIMER_CLOCK_BASE: c1e0401c [ktime_get_boottime]", > -" (empty)", > -" ", > -" CPU: 1 HRTIMER_CPU_BASE: c1e43fc0", > -" CLOCK: 0 HRTIMER_CLOCK_BASE: c1e43fc4 [ktime_get_real]", > -" (empty)", > -" ", > -" CLOCK: 1 HRTIMER_CLOCK_BASE: c1e43ff0 [ktime_get]", > -" CURRENT", > -" 322894000000", > -" SOFTEXPIRES EXPIRES HRTIMER FUNCTION", > -" 322895062500 322895062500 c1e44080 c04833e0 <tick_sched_timer>", > -" 324087213609 324087213609 c1e441c0 c04b17d0 <watchdog_timer_fn>", > -" 381034500892 381034550892 f3a1bea0 c0477ed0 <hrtimer_wakeup>", > -" ", > -" CLOCK: 2 HRTIMER_CLOCK_BASE: c1e4401c [ktime_get_boottime]", > -" (empty)", > " ...", > -" ", > -" Display an old-style hrtimer queue:\n", > -" %s> timer -r", > -" CPU: 0", > -" CLOCK: 0 HRTIMER_BASE: ca00dae0 [ktime_get_real]", > -" (empty)", > -" ", > -" CLOCK: 1 HRTIMER_BASE: ca00db0c [ktime_get]", > -" CURRENT", > -" 1480537567000000", > -" EXPIRES HRTIMER FUNCTION", > -" 1480997557052703 f79c4944 c0427d18 <it_real_fn>", > -" 1481009329944302 cdcbaf6c c0436a1e <hrtimer_wakeup>", > -" 1481026181758643 ea01cf6c c0436a1e <hrtimer_wakeup>", > -" 1481497068511094 f79a6244 c0427d18 <it_real_fn>", > -" 1481589831928489 f7af6944 c0427d18 <it_real_fn>", > -" 1481592731187337 f64ed784 c0427d18 <it_real_fn>", > -" ", > -" CPU: 1", > -" CLOCK: 0 HRTIMER_BASE: ca0148c4 [ktime_get_real]", > +" CPU: 2 HRTIMER_CPU_BASE: ffff9801aba9cf00", > +" CLOCK: 0 HRTIMER_CLOCK_BASE: ffff9801aba9cf40 [ktime_get]", > +" CURRENT", > +" 1623742000000", > +" SOFTEXPIRES EXPIRES TTE HRTIMER > FUNCTION", > +" 1623741000000 1623741000000 -1000000 ffff9801aba9d540 > ffffffff83b3c8e0 <tick_sched_timer>", > +" 1624024000000 1624024000000 282000000 ffff9801aba9d720 > ffffffff83b7e7a0 <watchdog_timer_fn>", > +" 1626000939806 1626010929804 2268929804 ffffa6b28399fa40 > ffffffff83b2c1e0 <hrtimer_wakeup>", > +" 1627576915615 1627576915615 3834915615 ffff9801a5727978 > ffffffff83b365c0 <posix_timer_fn>", > +" 1627637194488 1627647194487 3905194487 ffffa6b283977db0 > ffffffff83b2c1e0 <hrtimer_wakeup>", > +" 1629937423000 1629937423000 6195423000 ffff9801a9af2900 > ffffffff83cf3d30 <timerfd_tmrproc>", > +" ", > +" CLOCK: 1 HRTIMER_CLOCK_BASE: ffff9801aba9cf80 [ktime_get_real]", > +" CURRENT", > +" 1558362388334558243", > +" SOFTEXPIRES EXPIRES TTE HRTIMER > FUNCTION", > +" 1558362389331238000 1558362389331288000 996729757 > ffffa6b28574bcf0 ffffffff83b2c1e0 <hrtimer_wakeup>", > +" 1558364372000000000 1558364372000000000 1983665441757 > ffff9801a3513278 ffffffff83b365c0 <posix_timer_fn>", > +" ", > +" CLOCK: 2 HRTIMER_CLOCK_BASE: ffff9801aba9cfc0 > [ktime_get_boottime]", > " (empty)", > -" ", > -" CLOCK: 1 HRTIMER_BASE: ca0148f0 [ktime_get]", > -" CURRENT", > -" 1480537567000000", > -" EXPIRES HRTIMER FUNCTION", > -" 1481017523822478 ca3b15c4 c0427d18 <it_real_fn>", > -" 1481238077723188 f5f35f6c c0436a1e <hrtimer_wakeup>", > -" 1481492107740948 f5dadf6c c0436a1e <hrtimer_wakeup>", > -" 1482936527251241 ca1becc4 c0427d18 <it_real_fn>", > -" 1482936545249010 f7d42e84 c0427d18 <it_real_fn>", > -" 1492850155229325 ea01ef6c c0436a1e <hrtimer_wakeup>", > " ...", > " ", > NULL > diff --git a/kernel.c b/kernel.c > index f01dc2e..818c29a 100644 > --- a/kernel.c > +++ b/kernel.c > @@ -42,8 +42,8 @@ static void dump_hrtimer_data(const ulong *cpus); > static void dump_hrtimer_clock_base(const void *, const int); > static void dump_hrtimer_base(const void *, const int); > static void dump_active_timers(const void *, ulonglong); > -static int get_expires_len(const int, const ulong *, const int); > -static void print_timer(const void *); > +static int get_expires_len(const int, const ulong *, ulonglong, const int); > +static void print_timer(const void *, ulonglong); > static ulonglong ktime_to_ns(const void *); > static void dump_timer_data(const ulong *cpus); > static void dump_timer_data_tvec_bases_v1(const ulong *cpus); > @@ -52,10 +52,10 @@ static void dump_timer_data_tvec_bases_v3(const ulong > *cpus); > static void dump_timer_data_timer_bases(const ulong *cpus); > struct tv_range; > static void init_tv_ranges(struct tv_range *, int, int, int); > -static int do_timer_list(ulong,int, ulong *, void *,ulong *,struct tv_range > *); > -static int do_timer_list_v3(ulong, int, ulong *, void *,ulong *); > +static int do_timer_list(ulong,int, ulong *, void *,ulong *, ulong *, struct > tv_range *, ulong); > +static int do_timer_list_v3(ulong, int, ulong *, void *,ulong *, ulong *, > ulong); > struct timer_bases_data; > -static int do_timer_list_v4(struct timer_bases_data *); > +static int do_timer_list_v4(struct timer_bases_data *, ulong); > static int compare_timer_data(const void *, const void *); > static void panic_this_kernel(void); > static void dump_waitq(ulong, char *); > @@ -7515,6 +7515,7 @@ dump_hrtimer_data(const ulong *cpus) > > static int expires_len = -1; > static int softexpires_len = -1; > +static int tte_len = -1; > > static void > dump_hrtimer_clock_base(const void *hrtimer_bases, const int num) > @@ -7578,6 +7579,7 @@ dump_active_timers(const void *base, ulonglong now) > char buf2[BUFSIZE]; > char buf3[BUFSIZE]; > char buf4[BUFSIZE]; > + char buf5[BUFSIZE]; > > next = 0; > timer_list = 0; > @@ -7637,10 +7639,11 @@ next_one: > > /* dump hrtimers */ > /* print header */ > - expires_len = get_expires_len(timer_cnt, timer_list, 0); > + expires_len = get_expires_len(timer_cnt, timer_list, 0, 0); > if (expires_len < 7) > expires_len = 7; > - softexpires_len = get_expires_len(timer_cnt, timer_list, 1); > + softexpires_len = get_expires_len(timer_cnt, timer_list, 0, 1); > + tte_len = get_expires_len(timer_cnt, timer_list, now, 2); > > if (softexpires_len > -1) { > if (softexpires_len < 11) > @@ -7650,9 +7653,10 @@ next_one: > sprintf(buf1, "%lld", now); > fprintf(fp, " %s\n", mkstring(buf1, softexpires_len, > CENTER|RJUST, NULL)); > - fprintf(fp, " %s %s %s %s\n", > + fprintf(fp, " %s %s %s %s %s\n", > mkstring(buf1, softexpires_len, CENTER|RJUST, "SOFTEXPIRES"), > mkstring(buf2, expires_len, CENTER|RJUST, "EXPIRES"), > + mkstring(buf5, tte_len, CENTER|RJUST, "TTE"), > mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "HRTIMER"), > mkstring(buf4, VADDR_PRLEN, CENTER|LJUST, "FUNCTION")); > } else { > @@ -7660,8 +7664,9 @@ next_one: > "CURRENT")); > sprintf(buf1, "%lld", now); > fprintf(fp, " %s\n", mkstring(buf1, expires_len, CENTER|RJUST, NULL)); > - fprintf(fp, " %s %s %s\n", > + fprintf(fp, " %s %s %s %s\n", > mkstring(buf1, expires_len, CENTER|RJUST, "EXPIRES"), > + mkstring(buf5, tte_len, CENTER|RJUST, "TTE"), > mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "HRTIMER"), > mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION")); > } > @@ -7675,12 +7680,12 @@ next_one: > else > timer = (void *)(timer_list[t] - OFFSET(hrtimer_node)); > > - print_timer(timer); > + print_timer(timer, now); > } > } > > static int > -get_expires_len(const int timer_cnt, const ulong *timer_list, const int > getsoft) > +get_expires_len(const int timer_cnt, const ulong *timer_list, ulonglong now, > const int getsoft) > { > void *last_timer; > char buf[BUFSIZE]; > @@ -7700,7 +7705,7 @@ get_expires_len(const int timer_cnt, const ulong > *timer_list, const int getsoft) > last_timer = (void *)(timer_list[timer_cnt -1] - > OFFSET(hrtimer_node)); > > - if (getsoft) { > + if (getsoft == 1) { > /* soft expires exist*/ > if (VALID_MEMBER(hrtimer_softexpires)) { > softexpires = ktime_to_ns(last_timer + > @@ -7715,7 +7720,7 @@ get_expires_len(const int timer_cnt, const ulong > *timer_list, const int getsoft) > expires = ktime_to_ns(last_timer + OFFSET(hrtimer_node) + > OFFSET(timerqueue_node_expires)); > > - sprintf(buf, "%lld", expires); > + sprintf(buf, "%lld", getsoft ? expires - now : expires); > len = strlen(buf); > } > > @@ -7726,14 +7731,15 @@ get_expires_len(const int timer_cnt, const ulong > *timer_list, const int getsoft) > * print hrtimer and its related information > */ > static void > -print_timer(const void *timer) > +print_timer(const void *timer, ulonglong now) > { > - ulonglong softexpires, expires; > + ulonglong softexpires, expires, tte; > > ulong function; > char buf1[BUFSIZE]; > char buf2[BUFSIZE]; > char buf3[BUFSIZE]; > + char buf4[BUFSIZE]; > > /* align information */ > fprintf(fp, " "); > @@ -7764,6 +7770,9 @@ print_timer(const void *timer) > sprintf(buf1, "%lld", expires); > fprintf(fp, "%s ", mkstring(buf2, expires_len, CENTER|RJUST, buf1)); > > + tte = expires - now; > + fprintf(fp, "%s ", mkstring(buf4, tte_len, SLONG_DEC|RJUST, MKSTR(tte))); > + > fprintf(fp, "%lx ", (ulong)timer); > > if (readmem((ulong)(timer + OFFSET(hrtimer_function)), KVADDR, &function, > @@ -7819,6 +7828,7 @@ struct timer_data { > ulong address; > ulong expires; > ulong function; > + long tte; > }; > > struct tv_range { > @@ -7839,14 +7849,15 @@ dump_timer_data(const ulong *cpus) > } timer_table[32]; > char buf[BUFSIZE]; > char buf1[BUFSIZE]; > + char buf4[BUFSIZE]; > struct timer_struct *tp; > - ulong mask, highest, function; > + ulong mask, highest, highest_tte, function; > ulong jiffies, timer_jiffies; > ulong *vec; > long count; > int vec_root_size, vec_size; > struct timer_data *td; > - int flen, tdx, old_timers_exist; > + int flen, tlen, tdx, old_timers_exist; > struct tv_range tv[TVN]; > > if (kt->flags2 & TIMER_BASES) { > @@ -7900,15 +7911,15 @@ dump_timer_data(const ulong *cpus) > init_tv_ranges(tv, vec_root_size, vec_size, 0); > > count += do_timer_list(symbol_value("tv1") + > OFFSET(timer_vec_root_vec), > - vec_root_size, vec, NULL, NULL, tv); > + vec_root_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > > td = (struct timer_data *) > GETBUF((count*2) * sizeof(struct timer_data)); > @@ -7920,6 +7931,7 @@ dump_timer_data(const ulong *cpus) > get_symbol_data("timer_active", sizeof(ulong), &timer_active); > > highest = 0; > + highest_tte = 0; > for (i = 0, mask = 1, tp = timer_table+0; old_timers_exist && mask; > i++, tp++, mask += mask) { > if (mask > timer_active) > @@ -7931,21 +7943,24 @@ dump_timer_data(const ulong *cpus) > td[tdx].address = i; > td[tdx].expires = tp->expires; > td[tdx].function = (ulong)tp->fn; > + td[tdx].tte = tp->expires - jiffies; > if (td[tdx].expires > highest) > highest = td[tdx].expires; > + if (abs(td[tdx].tte) > highest_tte) > + highest_tte = abs(td[tdx].tte); > tdx++; > } > > do_timer_list(symbol_value("tv1") + OFFSET(timer_vec_root_vec), > - vec_root_size, vec, (void *)td, &highest, tv); > + vec_root_size, vec, (void *)td, &highest, &highest_tte, tv, jiffies); > do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, jiffies); > do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, jiffies); > do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, jiffies); > tdx = do_timer_list(symbol_value("tv5") + OFFSET(timer_vec_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, jiffies); > > qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); > > @@ -7958,13 +7973,21 @@ dump_timer_data(const ulong *cpus) > fprintf(fp, "%s\n", mkstring(buf, flen, CENTER|LJUST, "JIFFIES")); > fprintf(fp, "%s\n", mkstring(buf, flen, RJUST|LONG_DEC,MKSTR(jiffies))); > > - fprintf(fp, "%s TIMER_LIST/TABLE FUNCTION\n", > - mkstring(buf, flen, CENTER|LJUST, "EXPIRES")); > + /* +1 accounts possible "-" sign */ > + sprintf(buf4, "%ld", highest_tte); > + tlen = MAX(strlen(buf4) + 1, strlen("TTE")); > + > + fprintf(fp, "%s %s TIMER_LIST/TABLE FUNCTION\n", > + mkstring(buf, flen, CENTER|LJUST, "EXPIRES"), > + mkstring(buf4, tlen, CENTER|LJUST, "TTE")); > > for (i = 0; i < tdx; i++) { > fprintf(fp, "%s", > mkstring(buf, flen, RJUST|LONG_DEC, MKSTR(td[i].expires))); > > + fprintf(fp, " %s", > + mkstring(buf4, tlen, RJUST|SLONG_DEC, > MKSTR(td[i].tte))); > + > if (td[i].address < 32) { > sprintf(buf, "timer_table[%ld]", td[i].address); > fprintf(fp, " %s ", > @@ -8003,15 +8026,16 @@ dump_timer_data(const ulong *cpus) > static void > dump_timer_data_tvec_bases_v1(const ulong *cpus) > { > - int i, cpu, tdx, flen; > + int i, cpu, tdx, flen, tlen; > struct timer_data *td; > int vec_root_size, vec_size; > struct tv_range tv[TVN]; > - ulong *vec, jiffies, highest, function; > + ulong *vec, jiffies, highest, highest_tte, function; > long count; > char buf1[BUFSIZE]; > char buf2[BUFSIZE]; > char buf3[BUFSIZE]; > + char buf4[BUFSIZE]; > > /* > */ > @@ -8038,33 +8062,35 @@ next_cpu: > init_tv_ranges(tv, vec_root_size, vec_size, cpu); > > count += do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), > - vec_root_size, vec, NULL, NULL, tv); > + vec_root_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > > if (count) > td = (struct timer_data *) > GETBUF((count*2) * sizeof(struct timer_data)); > tdx = 0; > highest = 0; > + highest_tte = 0; > + > get_symbol_data("jiffies", sizeof(ulong), &jiffies); > > do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), > - vec_root_size, vec, (void *)td, &highest, tv); > + vec_root_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > do_timer_list(tv[2].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > do_timer_list(tv[3].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > do_timer_list(tv[4].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > > qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); > > @@ -8077,8 +8103,13 @@ next_cpu: > fprintf(fp, "%s\n", mkstring(buf1,flen, > RJUST|LONG_DEC,MKSTR(jiffies))); > > - fprintf(fp, "%s %s %s\n", > + /* +1 accounts possible "-" sign */ > + sprintf(buf4, "%ld", highest_tte); > + tlen = MAX(strlen(buf4) + 1, strlen("TTE")); > + > + fprintf(fp, "%s %s %s %s\n", > mkstring(buf1, flen, CENTER|RJUST, "EXPIRES"), > + mkstring(buf4, tlen, CENTER|RJUST, "TTE"), > mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "TIMER_LIST"), > mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION")); > > @@ -8086,6 +8117,9 @@ next_cpu: > fprintf(fp, "%s", > mkstring(buf1, flen, RJUST|LONG_DEC, > MKSTR(td[i].expires))); > > + fprintf(fp, " %s", > + mkstring(buf4, tlen, RJUST|SLONG_DEC, > MKSTR(td[i].tte))); > + > fprintf(fp, " %s ", mkstring(buf1, > MAX(VADDR_PRLEN, strlen("TIMER_LIST")), > RJUST|CENTER|LONG_HEX, MKSTR(td[i].address))); > @@ -8123,17 +8157,18 @@ next_cpu: > static void > dump_timer_data_tvec_bases_v2(const ulong *cpus) > { > - int i, cpu, tdx, flen; > + int i, cpu, tdx, flen, tlen; > struct timer_data *td; > int vec_root_size, vec_size; > struct tv_range tv[TVN]; > - ulong *vec, jiffies, highest, function; > + ulong *vec, jiffies, highest, highest_tte, function; > ulong tvec_bases; > long count; > struct syment *sp; > char buf1[BUFSIZE]; > char buf2[BUFSIZE]; > char buf3[BUFSIZE]; > + char buf4[BUFSIZE]; > > vec_root_size = (i = ARRAY_LENGTH(tvec_root_s_vec)) ? > i : get_array_length("tvec_root_s.vec", NULL, > SIZE(list_head)); > @@ -8180,33 +8215,35 @@ next_cpu: > init_tv_ranges(tv, vec_root_size, vec_size, cpu); > > count += do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), > - vec_root_size, vec, NULL, NULL, tv); > + vec_root_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL, tv); > + vec_size, vec, NULL, NULL, NULL, tv, 0); > > if (count) > td = (struct timer_data *) > GETBUF((count*2) * sizeof(struct timer_data)); > tdx = 0; > highest = 0; > + highest_tte = 0; > + > get_symbol_data("jiffies", sizeof(ulong), &jiffies); > > do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), > - vec_root_size, vec, (void *)td, &highest, tv); > + vec_root_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > do_timer_list(tv[2].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > do_timer_list(tv[3].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > do_timer_list(tv[4].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest, tv); > + vec_size, vec, (void *)td, &highest, &highest_tte, tv, > jiffies); > > qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); > > @@ -8229,8 +8266,13 @@ next_cpu: > fprintf(fp, "%s\n", mkstring(buf1,flen, > RJUST|LONG_DEC,MKSTR(jiffies))); > > - fprintf(fp, "%s %s %s\n", > + /* +1 accounts possible "-" sign */ > + sprintf(buf4, "%ld", highest_tte); > + tlen = MAX(strlen(buf4) + 1, strlen("TTE")); > + > + fprintf(fp, "%s %s %s %s\n", > mkstring(buf1, flen, CENTER|RJUST, "EXPIRES"), > + mkstring(buf4, tlen, CENTER|RJUST, "TTE"), > mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "TIMER_LIST"), > mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION")); > > @@ -8238,6 +8280,9 @@ next_cpu: > fprintf(fp, "%s", > mkstring(buf1, flen, RJUST|LONG_DEC, > MKSTR(td[i].expires))); > > + fprintf(fp, " %s", > + mkstring(buf4, tlen, RJUST|SLONG_DEC, > MKSTR(td[i].tte))); > + > fprintf(fp, " %s ", mkstring(buf1, > MAX(VADDR_PRLEN, strlen("TIMER_LIST")), > RJUST|CENTER|LONG_HEX, MKSTR(td[i].address))); > @@ -8274,17 +8319,18 @@ next_cpu: > static void > dump_timer_data_tvec_bases_v3(const ulong *cpus) > { > - int i, cpu, tdx, flen; > + int i, cpu, tdx, flen, tlen; > struct timer_data *td; > int vec_root_size, vec_size; > struct tv_range tv[TVN]; > - ulong *vec, jiffies, highest, function; > + ulong *vec, jiffies, highest, highest_tte, function; > ulong tvec_bases; > long count, head_size; > struct syment *sp; > char buf1[BUFSIZE]; > char buf2[BUFSIZE]; > char buf3[BUFSIZE]; > + char buf4[BUFSIZE]; > > vec_root_size = vec_size = 0; > head_size = SIZE(hlist_head); > @@ -8325,33 +8371,35 @@ next_cpu: > init_tv_ranges(tv, vec_root_size, vec_size, cpu); > > count += do_timer_list_v3(tv[1].base + OFFSET(tvec_root_s_vec), > - vec_root_size, vec, NULL, NULL); > + vec_root_size, vec, NULL, NULL, NULL, 0); > count += do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL); > + vec_size, vec, NULL, NULL, NULL, 0); > count += do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL); > + vec_size, vec, NULL, NULL, NULL, 0); > count += do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL); > + vec_size, vec, NULL, NULL, NULL, 0); > count += do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec), > - vec_size, vec, NULL, NULL); > + vec_size, vec, NULL, NULL, NULL, 0); > > if (count) > td = (struct timer_data *) > GETBUF((count*2) * sizeof(struct timer_data)); > tdx = 0; > highest = 0; > + highest_tte = 0; > + > get_symbol_data("jiffies", sizeof(ulong), &jiffies); > > do_timer_list_v3(tv[1].base + OFFSET(tvec_root_s_vec), > - vec_root_size, vec, (void *)td, &highest); > + vec_root_size, vec, (void *)td, &highest, &highest_tte, jiffies); > do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest); > + vec_size, vec, (void *)td, &highest, &highest_tte, jiffies); > do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest); > + vec_size, vec, (void *)td, &highest, &highest_tte, jiffies); > do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest); > + vec_size, vec, (void *)td, &highest, &highest_tte, jiffies); > tdx = do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec), > - vec_size, vec, (void *)td, &highest); > + vec_size, vec, (void *)td, &highest, &highest_tte, jiffies); > > qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); > > @@ -8369,8 +8417,13 @@ next_cpu: > fprintf(fp, "%s\n", mkstring(buf1,flen, > RJUST|LONG_DEC,MKSTR(jiffies))); > > - fprintf(fp, "%s %s %s\n", > + /* +1 accounts possible "-" sign */ > + sprintf(buf4, "%ld", highest_tte); > + tlen = MAX(strlen(buf4) + 1, strlen("TTE")); > + > + fprintf(fp, "%s %s %s %s\n", > mkstring(buf1, flen, CENTER|RJUST, "EXPIRES"), > + mkstring(buf4, tlen, CENTER|RJUST, "TTE"), > mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "TIMER_LIST"), > mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION")); > > @@ -8378,6 +8431,9 @@ next_cpu: > fprintf(fp, "%s", > mkstring(buf1, flen, RJUST|LONG_DEC, MKSTR(td[i].expires))); > > + fprintf(fp, " %s", > + mkstring(buf4, tlen, RJUST|SLONG_DEC, MKSTR(td[i].tte))); > + > fprintf(fp, " %s ", mkstring(buf1, > MAX(VADDR_PRLEN, strlen("TIMER_LIST")), > RJUST|CENTER|LONG_HEX, MKSTR(td[i].address))); > @@ -8517,7 +8573,9 @@ do_timer_list(ulong vec_kvaddr, > ulong *vec, > void *option, > ulong *highest, > - struct tv_range *tv) > + ulong *highest_tte, > + struct tv_range *tv, > + ulong jiffies) > { > int i, t; > int count, tdx; > @@ -8595,8 +8653,11 @@ do_timer_list(ulong vec_kvaddr, > td[tdx].address = timer_list[t]; > td[tdx].expires = expires; > td[tdx].function = function; > + td[tdx].tte = expires - jiffies; > if (highest && (expires > *highest)) > *highest = expires; > + if (highest_tte && (abs(td[tdx].tte) > > *highest_tte)) > + *highest_tte = > abs(td[tdx].tte); > tdx++; > } > } > @@ -8659,8 +8720,11 @@ new_timer_list_format: > td[tdx].address = timer_list[t]; > td[tdx].expires = expires; > td[tdx].function = function; > + td[tdx].tte = expires - jiffies; > if (highest && (expires > *highest)) > *highest = expires; > + if (highest_tte && (abs(td[tdx].tte) > > *highest_tte)) > + *highest_tte = abs(td[tdx].tte); > tdx++; > } > } > @@ -8677,7 +8741,9 @@ do_timer_list_v3(ulong vec_kvaddr, > int size, > ulong *vec, > void *option, > - ulong *highest) > + ulong *highest, > + ulong *highest_tte, > + ulong jiffies) > { > int i, t; > int count, tdx; > @@ -8743,8 +8809,11 @@ do_timer_list_v3(ulong vec_kvaddr, > td[tdx].address = timer_list[t]; > td[tdx].expires = expires; > td[tdx].function = function; > + td[tdx].tte = expires - jiffies; > if (highest && (expires > *highest)) > *highest = expires; > + if (highest_tte && (abs(td[tdx].tte) > *highest_tte)) > + *highest_tte = abs(td[tdx].tte); > tdx++; > } > } > @@ -8766,7 +8835,7 @@ struct timer_bases_data { > }; > > static int > -do_timer_list_v4(struct timer_bases_data *data) > +do_timer_list_v4(struct timer_bases_data *data, ulong jiffies) > { > int i, t, timer_cnt, found; > struct list_data list_data, *ld; > @@ -8826,6 +8895,7 @@ do_timer_list_v4(struct timer_bases_data *data) > data->timers[data->cnt].address = timer_list[t]; > data->timers[data->cnt].expires = expires; > data->timers[data->cnt].function = function; > + data->timers[data->cnt].tte = expires - jiffies; > data->cnt++; > > if (data->cnt == data->total) { > @@ -8852,12 +8922,13 @@ do_timer_list_v4(struct timer_bases_data *data) > static void > dump_timer_data_timer_bases(const ulong *cpus) > { > - int i, cpu, flen, base, nr_bases, found, display, j = 0; > + int i, cpu, flen, tlen, base, nr_bases, found, display, j = 0; > struct syment *sp; > - ulong timer_base, jiffies, function; > + ulong timer_base, jiffies, function, highest_tte; > struct timer_bases_data data; > char buf1[BUFSIZE]; > char buf2[BUFSIZE]; > + char buf4[BUFSIZE]; > > if (!(data.num_vectors = get_array_length("timer_base.vectors", NULL, 0))) > error(FATAL, "cannot determine timer_base.vectors[] array size\n"); > @@ -8912,12 +8983,42 @@ next_base: > data.cnt = 0; > data.timer_base = timer_base; > > - found = do_timer_list_v4(&data); > + found = do_timer_list_v4(&data, jiffies); > > qsort(data.timers, found, sizeof(struct timer_data), compare_timer_data); > > - fprintf(fp, " %s TIMER_LIST FUNCTION\n", > - mkstring(buf1, flen, LJUST, "EXPIRES")); > + highest_tte = 0; > + for (i = 0; i < found; i++) { > + display = FALSE; > + > + if (is_kernel_text(data.timers[i].function)) { > + display = TRUE; > + } else { > + if (readmem(data.timers[i].function, KVADDR, &function, > + sizeof(ulong), "timer function", > + RETURN_ON_ERROR|QUIET) && is_kernel_text(function)) { > + display = TRUE; > + } else { > + if (LIVE()) > + display = FALSE; > + else > + display = TRUE; > + } > + } > + > + if (display) { > + if (abs(data.timers[i].tte) > highest_tte) > + highest_tte = abs(data.timers[i].tte); > + } > + } > + > + /* +1 accounts possible "-" sign */ > + sprintf(buf4, "%ld", highest_tte); > + tlen = MAX(strlen(buf4) + 1, strlen("TTE")); > + > + fprintf(fp, " %s %s TIMER_LIST FUNCTION\n", > + mkstring(buf1, flen, LJUST, "EXPIRES"), > + mkstring(buf4, tlen, LJUST, "TTE")); > > for (i = 0; i < found; i++) { > display = FALSE; > @@ -8946,6 +9047,8 @@ next_base: > if (display) { > fprintf(fp, " %s", > mkstring(buf1, flen, RJUST|LONG_DEC, MKSTR(data.timers[i].expires))); > + fprintf(fp, " %s", > + mkstring(buf4, tlen, RJUST|SLONG_DEC, MKSTR(data.timers[i].tte))); > mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX, > MKSTR(data.timers[i].address)); > fprintf(fp, " %s ", mkstring(buf2, 16, CENTER, buf1)); > fprintf(fp, "%s <%s>\n", > diff --git a/tools.c b/tools.c > index 2d95c3a..5c0e63e 100644 > --- a/tools.c > +++ b/tools.c > @@ -1650,11 +1650,14 @@ mkstring(char *s, int size, ulong flags, const char > *opt) > int left; > int right; > > - switch (flags & (LONG_DEC|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX|ZERO_FILL)) > + switch (flags & > (LONG_DEC|SLONG_DEC|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX|ZERO_FILL)) > { > case LONG_DEC: > sprintf(s, "%lu", (ulong)opt); > break; > + case SLONG_DEC: > + sprintf(s, "%ld", (ulong)opt); > + break; > case LONG_HEX: > sprintf(s, "%lx", (ulong)opt); > break; > -- > 2.22.0 > > -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility