Hi Oleksandr,
Looks good -- queued for crash-7.2.7:
https://github.com/crash-utility/crash/commit/496d503e84147a24bdaf86782ab...
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(a)redhat.com>
---
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