----- 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. > > The commit doesn't implement human-readable format for TTE. This will > be addressed in a subsequent submission once this one is refined. OK, so far, so good... > > `help` output is not amended since I do not have original vmcores to > preserve old printout. Should I replace it with my own data, or you can > run `timer` on those vmcores? I can't even find any old vmcores that still have "timer_table" entries, so yeah, I would go ahead an replace it with your own data. Thanks, Dave > > 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 + > kernel.c | 255 ++++++++++++++++++++++++++++++++++++++----------------- > tools.c | 5 +- > 3 files changed, 184 insertions(+), 77 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/kernel.c b/kernel.c > index 22909d2..77cdc16 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 *); > @@ -7504,6 +7504,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) > @@ -7567,6 +7568,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; > @@ -7626,10 +7628,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) > @@ -7639,9 +7642,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 { > @@ -7649,8 +7653,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")); > } > @@ -7664,12 +7669,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]; > @@ -7689,7 +7694,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 + > @@ -7704,7 +7709,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); > } > > @@ -7715,14 +7720,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, " "); > @@ -7753,6 +7759,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, > @@ -7808,6 +7817,7 @@ struct timer_data { > ulong address; > ulong expires; > ulong function; > + long tte; > }; > > struct tv_range { > @@ -7828,14 +7838,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) { > @@ -7889,15 +7900,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)); > @@ -7909,6 +7920,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) > @@ -7920,21 +7932,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); > > @@ -7947,13 +7962,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 ", > @@ -7992,15 +8015,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]; > > /* > */ > @@ -8027,33 +8051,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); > > @@ -8066,8 +8092,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")); > > @@ -8075,6 +8106,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))); > @@ -8112,17 +8146,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)); > @@ -8169,33 +8204,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); > > @@ -8218,8 +8255,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")); > > @@ -8227,6 +8269,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))); > @@ -8263,17 +8308,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); > @@ -8314,33 +8360,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); > > @@ -8358,8 +8406,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")); > > @@ -8367,6 +8420,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))); > @@ -8506,7 +8562,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; > @@ -8584,8 +8642,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++; > } > } > @@ -8648,8 +8709,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++; > } > } > @@ -8666,7 +8730,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; > @@ -8732,8 +8798,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++; > } > } > @@ -8755,7 +8824,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; > @@ -8815,6 +8884,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) { > @@ -8841,12 +8911,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"); > @@ -8901,12 +8972,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; > @@ -8935,6 +9036,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