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.
`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?
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(a)redhat.com>
---
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