[Crash-utility] [PATCH v2] kernel/timers: show time to expire for each timer

Oleksandr Natalenko oleksandr at redhat.com
Tue Jun 25 11:36:41 UTC 2019


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 at 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




More information about the Crash-utility mailing list