[PATCH v2] kernel/timers: show time to expire for each timer
by Oleksandr Natalenko
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
5 years, 5 months
[ANNOUNCE] crash gcore command, version 1.5.1 is released
by d.hatayama@fujitsu.com
This is the release of crash gcore command, version 1.5.1.
This release includes the following 2 bug fixes.
ChangeLog:
- Fix the invalid structure member failure of
thread_struct_fpsimd_state caused by the commit
65896545b69ffaac947c12e11d3dcc57fd1fb772 merged at v4.17-rc2
("arm64: uaccess: Fix omissions from usercopy whitelist") that
changed struct thread_struct. Without this fix, gcore command
fails with the following error message:
crash> gcore 2190
gcore: invalid structure member offset: thread_struct_fpsimd_state
FILE: libgcore/gcore_arm64.c LINE: 46 FUNCTION: fpr_get()
[./crash] error trace: ffff81c44c8c => ffff81c48fb8 => 51afd8 => 51af50
51af50: OFFSET_verify.part.28+72
51afd8: store_module_kallsyms_v2.part.29+0
gcore: invalid structure member offset: thread_struct_fpsimd_state
FILE: libgcore/gcore_arm64.c LINE: 46 FUNCTION: fpr_get()
Failed.
(anderson(a)redhat.com)
- Fix the invalid structure size failure of pid_link on v4.19 and
newer kernel introduced by the commit 2c4704756cab ("pids: Move
the pgrp and session pid pointers from task_struct to
signal_struct") that removed struct pid_link array from
task_struct and added struct pid array into signal_struct. Without
this fix, gcore command fails with the following error message:
gcore: invalid structure size: pid_link
FILE: libgcore/gcore_coredump_table.c LINE: 423 FUNCTION: pid_alive()
[./crash] error trace: 7f4a7a6762b8 => 7f4a7a676e0f => 53b391 => 53b316
53b316: SIZE_verify.part.31+70
53b391: SIZE_verify+49
gcore: invalid structure size: pid_link
FILE: libgcore/gcore_coredump_table.c LINE: 423 FUNCTION: pid_alive()
Failed.
(m.mizuma(a)jp.fujitsu.com)
MD5 CheckSum:
# md5sum crash-gcore-command-1.5.1.tar.gz
255c9bab30f3cac68b30676ce295245a crash-gcore-command-1.5.1.tar.gz
--
Thanks.
HATAYAMA, Daisuke
5 years, 5 months
[PATCH] extension/gcore: Fix the invalid struct size failure of pid_link on 4.19 and newer kernel.
by Masayoshi Mizuma
From: Masayoshi Mizuma <m.mizuma(a)jp.fujitsu.com>
On 4.19 and newer kernel, gcore command fails as following:
===========================================================
gcore: invalid structure size: pid_link
FILE: libgcore/gcore_coredump_table.c LINE: 423 FUNCTION: pid_alive()
[./crash] error trace: 7f4a7a6762b8 => 7f4a7a676e0f => 53b391 => 53b316
53b316: SIZE_verify.part.31+70
53b391: SIZE_verify+49
gcore: invalid structure size: pid_link
FILE: libgcore/gcore_coredump_table.c LINE: 423 FUNCTION: pid_alive()
Failed.
===========================================================
That is because struct pid_link is removed and struct pid array is added to
struct signal_struct by commit 2c4704756cab ("pids: Move the pgrp and session
pid pointers from task_struct to signal_struct").
Get the pointer of struct pid from task_struct->thread_pid or
signal_struct->pids[] to fix the failure.
Signed-off-by: Masayoshi Mizuma <m.mizuma(a)jp.fujitsu.com>
---
gcore.c | 4 +++
libgcore/gcore_coredump_table.c | 48 +++++++++++++++++++++++++++------
libgcore/gcore_defs.h | 2 ++
3 files changed, 46 insertions(+), 8 deletions(-)
diff --git a/gcore.c b/gcore.c
index 65dd7a4..4e1304f 100644
--- a/gcore.c
+++ b/gcore.c
@@ -501,6 +501,10 @@ static void gcore_offset_table_init(void)
GCORE_MEMBER_OFFSET_INIT(vfp_hard_struct_fpscr, "vfp_hard_struct", "fpscr");
GCORE_MEMBER_OFFSET_INIT(thread_struct_fpsimd_state, "thread_struct", "fpsimd_state");
GCORE_MEMBER_OFFSET_INIT(thread_struct_tp_value, "thread_struct", "tp_value");
+ if (MEMBER_EXISTS("task_struct", "thread_pid"))
+ GCORE_MEMBER_OFFSET_INIT(task_struct_thread_pid, "task_struct", "thread_pid");
+ if (MEMBER_EXISTS("signal_struct", "pids"))
+ GCORE_MEMBER_OFFSET_INIT(signal_struct_pids, "signal_struct", "pids");
}
static void gcore_size_table_init(void)
diff --git a/libgcore/gcore_coredump_table.c b/libgcore/gcore_coredump_table.c
index a073591..4cf9c38 100644
--- a/libgcore/gcore_coredump_table.c
+++ b/libgcore/gcore_coredump_table.c
@@ -224,7 +224,7 @@ __task_pid_nr_ns(ulong task, enum pid_type type)
sizeof(ns), "__task_pid_nr_ns: ns", gcore_verbose_error_handle());
if (pid_alive(task)) {
- ulong pids_type_pid;
+ ulong pids_type_pid, signal;
if (type != PIDTYPE_PID)
readmem(task + MEMBER_OFFSET("task_struct",
@@ -233,10 +233,34 @@ __task_pid_nr_ns(ulong task, enum pid_type type)
"__task_pid_nr_ns: group_leader",
gcore_verbose_error_handle());
- readmem(task + OFFSET(task_struct_pids) + type * SIZE(pid_link)
- + OFFSET(pid_link_pid), KVADDR, &pids_type_pid,
- sizeof(pids_type_pid),
- "__task_pid_nr_ns: pids_type_pid", gcore_verbose_error_handle());
+ if (VALID_MEMBER(task_struct_pids))
+ readmem(task + OFFSET(task_struct_pids) +
+ type * SIZE(pid_link) + OFFSET(pid_link_pid),
+ KVADDR, &pids_type_pid,
+ sizeof(pids_type_pid),
+ "__task_pid_nr_ns: pids_type_pid",
+ gcore_verbose_error_handle());
+ else
+ if (type == PIDTYPE_PID)
+ readmem(task + GCORE_OFFSET(task_struct_thread_pid),
+ KVADDR, &pids_type_pid,
+ sizeof(pids_type_pid),
+ "__task_pid_nr_ns: pids_type_pid",
+ gcore_verbose_error_handle());
+ else {
+ readmem(task + OFFSET(task_struct_signal),
+ KVADDR, &signal,
+ sizeof(signal),
+ "__task_pid_nr_ns: signal",
+ gcore_verbose_error_handle());
+
+ readmem(signal + GCORE_OFFSET(signal_struct_pids) +
+ type * sizeof(void *),
+ KVADDR, &pids_type_pid,
+ sizeof(pids_type_pid),
+ "__task_pid_nr_ns: pids_type_pid",
+ gcore_verbose_error_handle());
+ }
nr = pid_nr_ns(pids_type_pid, ns);
}
@@ -420,9 +444,17 @@ pid_alive(ulong task)
{
pid_t pid;
- readmem(task + OFFSET(task_struct_pids) + PIDTYPE_PID * SIZE(pid_link)
- + OFFSET(pid_link_pid), KVADDR, &pid, sizeof(pid), "pid_alive",
- gcore_verbose_error_handle());
+ if (VALID_MEMBER(task_struct_pids))
+ readmem(task + OFFSET(task_struct_pids) +
+ PIDTYPE_PID * SIZE(pid_link) + OFFSET(pid_link_pid),
+ KVADDR, &pid, sizeof(pid),
+ "pid_alive",
+ gcore_verbose_error_handle());
+ else
+ readmem(task + GCORE_OFFSET(task_struct_thread_pid),
+ KVADDR, &pid, sizeof(pid),
+ "task_struct.thread_pid",
+ gcore_verbose_error_handle());
return !!pid;
}
diff --git a/libgcore/gcore_defs.h b/libgcore/gcore_defs.h
index 18c4c2c..3c839f9 100644
--- a/libgcore/gcore_defs.h
+++ b/libgcore/gcore_defs.h
@@ -1077,6 +1077,7 @@ struct gcore_offset_table
long sched_entity_sum_exec_runtime;
long signal_struct_cutime;
long signal_struct_pgrp;
+ long signal_struct_pids;
long signal_struct_session;
long signal_struct_stime;
long signal_struct_sum_sched_runtime;
@@ -1090,6 +1091,7 @@ struct gcore_offset_table
long task_struct_static_prio;
long task_struct_uid;
long task_struct_used_math;
+ long task_struct_thread_pid;
long thread_info_status;
long thread_info_fpstate;
long thread_info_vfpstate;
--
2.18.1
5 years, 5 months
Re: [Crash-utility] [PATCH] Fix unsigned signed comparison causing segfault for small VMCOREINFO notes
by Nuno Das Neves
> -----Original Message-----
> From: Dave Anderson <anderson(a)redhat.com>
> Sent: Wednesday, June 5, 2019 2:56 PM
> To: Nuno Das Neves <Nuno.Das(a)microsoft.com>
> Cc: Nuno Das Neves <Nuno.Das(a)microsoft.com>
> Subject: Fwd: [Crash-utility] [PATCH] Fix unsigned signed comparison causing segfault for small VMCOREINFO notes
>
>
>
> ----- Forwarded Message -----
> From: "Dave Anderson" <anderson(a)redhat.com>
> To: "Discussion list for crash utility usage, maintenance and development" <crash-utility(a)redhat.com>
> Sent: Wednesday, June 5, 2019 4:18:02 PM
> Subject: Re: [Crash-utility] [PATCH] Fix unsigned signed comparison causing segfault for small VMCOREINFO notes
>
>
>
> ----- Original Message -----
> > Hi,
> >
> > This is a fix for a signed/unsigned comparison bug in vmcoreinfo_read_string.
> > The bug causes a segmentation fault if size_vmcoreinfo + 1 is smaller than
> > the length of the key string passed in.
>
> I suppose that's true, but can you describe the instance where that actually happened?
I'm debugging some issues with vm2core (https://github.com/Azure/azure-linux-utils/tree/master/vm2core), a tool for converting Hyper-V snapshots to elf core files that can be analyzed with Crash.
There was a problem where versions of Crash newer than 7.1.5 crashed with elf files generated by vm2core due to this issue.
> Can you show the actual note contents as shown by "help -D"?
Elf64_Nhdr:
n_namesz: 11 ("VMCOREINFO")
n_descsz: 10
n_type: 0 (unused)
VMCOREINFO
So it seems the note doesn't really contain valid data as defined in Documentation/kdump/vmcoreinfo.txt.
I've done some additional testing and it appears this note isn't needed by Crash at all - I can simply patch vm2core so it doesn't add the note.
So, I suppose this fix isn't required to solve my particular issue.
However, could the VMCOREINFO note legitimately contain a single field e.g. "PAGE_SIZE=4096"?
If so, this is just 14 characters; 14 + 1 < strlen("NUMBER(KERNEL_IMAGE_SIZE)") - this string is used as the argument to vmcoreinfo_read_string on line 202 of x86_64.c.
Regards,
Nuno
>
> Thanks,
> Dave
>
>
> >
> > Signed-off-by: Nuno Das Neves <nudasnev(a)microsoft.com>
> > ---
> > netdump.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/netdump.c b/netdump.c
> > index 40f9cde..d257ecd 100644
> > --- a/netdump.c
> > +++ b/netdump.c
> > @@ -1838,7 +1838,7 @@ vmcoreinfo_read_string(const char *key)
> > return NULL;
> >
> > /* the '+ 1' is the equal sign */
> > - for (i = 0; i < (size_vmcoreinfo - key_length + 1); i++) {
> > + for (i = 0; i < (int)(size_vmcoreinfo - key_length + 1); i++) {
> > /*
> > * We must also check if we're at the beginning of VMCOREINFO
> > * or the separating newline is there, and of course if we
> > --
> > 1.8.3.1
> >
> > --
> > Crash-utility mailing list
> > Crash-utility(a)redhat.com
> > https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.red...
> utility&data=02%7C01%7CNuno.Das%40microsoft.com%7C3a91428c54b34ed7855d08d6ea008afe%7C72f988bf86f141af91ab2d7cd0
> 11db47%7C1%7C0%7C636953685382744097&sdata=BVID2b0QYkDmBip0bzPjX%2Fl4vltJtf78JrTE7pnqSIM%3D&reserved=0
> >
5 years, 5 months
[PATCH RFC] kernel/timers: show time to expire for each timer
by Oleksandr Natalenko
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
5 years, 5 months
[PATCH] timers: add option to show expired timers only
by Oleksandr Natalenko
Implement `timer` command option (namely, `-e`) that allows filtering
out all the non-expired timers.
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.
Signed-off-by: Oleksandr Natalenko <oleksandr(a)redhat.com>
---
help.c | 1 +
kernel.c | 213 ++++++++++++++++++++++++++++++++-----------------------
2 files changed, 125 insertions(+), 89 deletions(-)
diff --git a/help.c b/help.c
index c0c750f..fa856d1 100644
--- a/help.c
+++ b/help.c
@@ -2886,6 +2886,7 @@ char *help_timer[] = {
" chronological order. In the case of the old-style hrtimers, the",
" expiration time is a single value; in the new-style hrtimers, the",
" expiration time is a range.",
+" -e Display expired timers only.",
" -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",
diff --git a/kernel.c b/kernel.c
index 22909d2..afc66da 100644
--- a/kernel.c
+++ b/kernel.c
@@ -38,24 +38,24 @@ static void display_bh_1(void);
static void display_bh_2(void);
static void display_bh_3(void);
static void display_bh_4(void);
-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 void dump_hrtimer_data(const ulong *, int);
+static void dump_hrtimer_clock_base(const void *, const int, int);
+static void dump_hrtimer_base(const void *, const int, int);
+static void dump_active_timers(const void *, ulonglong, int);
static int get_expires_len(const int, const ulong *, const int);
-static void print_timer(const void *);
+static void print_timer(const void *, ulonglong, int);
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);
-static void dump_timer_data_tvec_bases_v2(const ulong *cpus);
-static void dump_timer_data_tvec_bases_v3(const ulong *cpus);
-static void dump_timer_data_timer_bases(const ulong *cpus);
+static void dump_timer_data(const ulong *, int);
+static void dump_timer_data_tvec_bases_v1(const ulong *, int);
+static void dump_timer_data_tvec_bases_v2(const ulong *, int);
+static void dump_timer_data_tvec_bases_v3(const ulong *, int);
+static void dump_timer_data_timer_bases(const ulong *, int);
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 *,struct tv_range *, ulong, int);
+static int do_timer_list_v3(ulong, int, ulong *, void *,ulong *, ulong, int);
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, int);
static int compare_timer_data(const void *, const void *);
static void panic_this_kernel(void);
static void dump_waitq(ulong, char *);
@@ -7402,18 +7402,24 @@ cmd_timer(void)
{
int c;
int rflag;
+ int eflag;
char *cpuspec;
ulong *cpus = NULL;
rflag = 0;
+ eflag = 0;
- while ((c = getopt(argcnt, args, "rC:")) != EOF) {
+ while ((c = getopt(argcnt, args, "reC:")) != EOF) {
switch(c)
{
case 'r':
rflag = 1;
break;
+ case 'e':
+ eflag = 1;
+ break;
+
case 'C':
cpuspec = optarg;
cpus = get_cpumask_buf();
@@ -7430,16 +7436,16 @@ cmd_timer(void)
cmd_usage(pc->curcmd, SYNOPSIS);
if (rflag)
- dump_hrtimer_data(cpus);
+ dump_hrtimer_data(cpus, eflag);
else
- dump_timer_data(cpus);
+ dump_timer_data(cpus, eflag);
if (cpus)
FREEBUF(cpus);
}
static void
-dump_hrtimer_data(const ulong *cpus)
+dump_hrtimer_data(const ulong *cpus, int eflag)
{
int i, j, k = 0;
int hrtimer_max_clock_bases, max_hrtimer_bases;
@@ -7487,7 +7493,7 @@ dump_hrtimer_data(const ulong *cpus)
fprintf(fp, "\n");
dump_hrtimer_clock_base(
(void *)(hrtimer_bases->value) +
- kt->__per_cpu_offset[i], j);
+ kt->__per_cpu_offset[i], j, eflag);
}
} else {
fprintf(fp, "\n");
@@ -7496,7 +7502,7 @@ dump_hrtimer_data(const ulong *cpus)
fprintf(fp, "\n");
dump_hrtimer_base(
(void *)(hrtimer_bases->value) +
- kt->__per_cpu_offset[i], j);
+ kt->__per_cpu_offset[i], j, eflag);
}
}
}
@@ -7506,7 +7512,7 @@ static int expires_len = -1;
static int softexpires_len = -1;
static void
-dump_hrtimer_clock_base(const void *hrtimer_bases, const int num)
+dump_hrtimer_clock_base(const void *hrtimer_bases, const int num, int eflag)
{
void *base;
ulonglong current_time, now;
@@ -7530,11 +7536,11 @@ dump_hrtimer_clock_base(const void *hrtimer_bases, const int num)
offset = ktime_to_ns(base + OFFSET(hrtimer_clock_base_offset));
now = current_time * (1000000000LL / machdep->hz) + offset;
- dump_active_timers(base, now);
+ dump_active_timers(base, now, eflag);
}
static void
-dump_hrtimer_base(const void *hrtimer_bases, const int num)
+dump_hrtimer_base(const void *hrtimer_bases, const int num, int eflag)
{
void *base;
ulonglong current_time, now;
@@ -7552,11 +7558,11 @@ dump_hrtimer_base(const void *hrtimer_bases, const int num)
get_uptime(NULL, ¤t_time);
now = current_time * (1000000000LL / machdep->hz);
- dump_active_timers(base, now);
+ dump_active_timers(base, now, eflag);
}
static void
-dump_active_timers(const void *base, ulonglong now)
+dump_active_timers(const void *base, ulonglong now, int eflag)
{
int next, i, t;
struct rb_node *curr;
@@ -7664,7 +7670,7 @@ next_one:
else
timer = (void *)(timer_list[t] - OFFSET(hrtimer_node));
- print_timer(timer);
+ print_timer(timer, now, eflag);
}
}
@@ -7715,7 +7721,7 @@ 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, int eflag)
{
ulonglong softexpires, expires;
@@ -7724,6 +7730,15 @@ print_timer(const void *timer)
char buf2[BUFSIZE];
char buf3[BUFSIZE];
+ if (VALID_MEMBER(hrtimer_expires))
+ expires = ktime_to_ns(timer + OFFSET(hrtimer_expires));
+ else
+ expires = ktime_to_ns(timer + OFFSET(hrtimer_node) +
+ OFFSET(timerqueue_node_expires));
+
+ if (eflag && expires >= now)
+ return;
+
/* align information */
fprintf(fp, " ");
@@ -7732,11 +7747,6 @@ print_timer(const void *timer)
return;
}
- if (VALID_MEMBER(hrtimer_expires))
- expires = ktime_to_ns(timer + OFFSET(hrtimer_expires));
- else
- expires = ktime_to_ns(timer + OFFSET(hrtimer_node) +
- OFFSET(timerqueue_node_expires));
if (VALID_MEMBER(hrtimer_softexpires)) {
softexpires = ktime_to_ns(timer + OFFSET(hrtimer_softexpires));
@@ -7818,7 +7828,7 @@ struct tv_range {
#define TVN (6)
static void
-dump_timer_data(const ulong *cpus)
+dump_timer_data(const ulong *cpus, int eflag)
{
int i;
ulong timer_active;
@@ -7839,16 +7849,16 @@ dump_timer_data(const ulong *cpus)
struct tv_range tv[TVN];
if (kt->flags2 & TIMER_BASES) {
- dump_timer_data_timer_bases(cpus);
+ dump_timer_data_timer_bases(cpus, eflag);
return;
} else if (kt->flags2 & TVEC_BASES_V3) {
- dump_timer_data_tvec_bases_v3(cpus);
+ dump_timer_data_tvec_bases_v3(cpus, eflag);
return;
} else if (kt->flags & TVEC_BASES_V2) {
- dump_timer_data_tvec_bases_v2(cpus);
+ dump_timer_data_tvec_bases_v2(cpus, eflag);
return;
} else if (kt->flags & TVEC_BASES_V1) {
- dump_timer_data_tvec_bases_v1(cpus);
+ dump_timer_data_tvec_bases_v1(cpus, eflag);
return;
}
@@ -7888,23 +7898,24 @@ dump_timer_data(const ulong *cpus)
init_tv_ranges(tv, vec_root_size, vec_size, 0);
+ get_symbol_data("jiffies", sizeof(ulong), &jiffies);
+ get_symbol_data("timer_jiffies", sizeof(ulong), &timer_jiffies);
+
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, tv, jiffies, eflag);
count += do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
- get_symbol_data("jiffies", sizeof(ulong), &jiffies);
- get_symbol_data("timer_jiffies", sizeof(ulong), &timer_jiffies);
if (old_timers_exist)
get_symbol_data("timer_active", sizeof(ulong), &timer_active);
@@ -7916,6 +7927,8 @@ dump_timer_data(const ulong *cpus)
if (!(mask & timer_active))
continue;
+ if (eflag && tp->expires >= jiffies)
+ continue;
td[tdx].address = i;
td[tdx].expires = tp->expires;
@@ -7926,15 +7939,15 @@ dump_timer_data(const ulong *cpus)
}
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, tv, jiffies, eflag);
do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
tdx = do_timer_list(symbol_value("tv5") + OFFSET(timer_vec_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
@@ -7951,6 +7964,9 @@ dump_timer_data(const ulong *cpus)
mkstring(buf, flen, CENTER|LJUST, "EXPIRES"));
for (i = 0; i < tdx; i++) {
+ if (eflag && td[i].expires >= jiffies)
+ continue;
+
fprintf(fp, "%s",
mkstring(buf, flen, RJUST|LONG_DEC, MKSTR(td[i].expires)));
@@ -7990,7 +8006,7 @@ dump_timer_data(const ulong *cpus)
*/
static void
-dump_timer_data_tvec_bases_v1(const ulong *cpus)
+dump_timer_data_tvec_bases_v1(const ulong *cpus, int eflag)
{
int i, cpu, tdx, flen;
struct timer_data *td;
@@ -8026,34 +8042,35 @@ next_cpu:
init_tv_ranges(tv, vec_root_size, vec_size, cpu);
+ get_symbol_data("jiffies", sizeof(ulong), &jiffies);
+
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, tv, jiffies, eflag);
count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
if (count)
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
highest = 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, tv, jiffies, eflag);
do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
@@ -8110,7 +8127,7 @@ next_cpu:
*/
static void
-dump_timer_data_tvec_bases_v2(const ulong *cpus)
+dump_timer_data_tvec_bases_v2(const ulong *cpus, int eflag)
{
int i, cpu, tdx, flen;
struct timer_data *td;
@@ -8168,34 +8185,35 @@ next_cpu:
init_tv_ranges(tv, vec_root_size, vec_size, cpu);
+ get_symbol_data("jiffies", sizeof(ulong), &jiffies);
+
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, tv, jiffies, eflag);
count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL, tv);
+ vec_size, vec, NULL, NULL, tv, jiffies, eflag);
if (count)
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
highest = 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, tv, jiffies, eflag);
do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest, tv);
+ vec_size, vec, (void *)td, &highest, tv, jiffies, eflag);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
@@ -8261,7 +8279,7 @@ next_cpu:
* Linux 4.2 timers use new tvec_root, tvec and timer_list structures
*/
static void
-dump_timer_data_tvec_bases_v3(const ulong *cpus)
+dump_timer_data_tvec_bases_v3(const ulong *cpus, int eflag)
{
int i, cpu, tdx, flen;
struct timer_data *td;
@@ -8313,34 +8331,35 @@ next_cpu:
BZERO(tv, sizeof(struct tv_range) * TVN);
init_tv_ranges(tv, vec_root_size, vec_size, cpu);
+ get_symbol_data("jiffies", sizeof(ulong), &jiffies);
+
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, jiffies, eflag);
count += do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL);
+ vec_size, vec, NULL, NULL, jiffies, eflag);
count += do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL);
+ vec_size, vec, NULL, NULL, jiffies, eflag);
count += do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL);
+ vec_size, vec, NULL, NULL, jiffies, eflag);
count += do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec),
- vec_size, vec, NULL, NULL);
+ vec_size, vec, NULL, NULL, jiffies, eflag);
if (count)
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
highest = 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, jiffies, eflag);
do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest);
+ vec_size, vec, (void *)td, &highest, jiffies, eflag);
do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest);
+ vec_size, vec, (void *)td, &highest, jiffies, eflag);
do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest);
+ vec_size, vec, (void *)td, &highest, jiffies, eflag);
tdx = do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec),
- vec_size, vec, (void *)td, &highest);
+ vec_size, vec, (void *)td, &highest, jiffies, eflag);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
@@ -8506,7 +8525,9 @@ do_timer_list(ulong vec_kvaddr,
ulong *vec,
void *option,
ulong *highest,
- struct tv_range *tv)
+ struct tv_range *tv,
+ ulong jiffies,
+ int eflag)
{
int i, t;
int count, tdx;
@@ -8577,6 +8598,9 @@ do_timer_list(ulong vec_kvaddr,
expires = ULONG(timer_list_buf +
OFFSET(timer_list_expires));
+ if (eflag && expires >= jiffies)
+ continue;
+
function = ULONG(timer_list_buf +
OFFSET(timer_list_function));
@@ -8641,6 +8665,9 @@ new_timer_list_format:
expires = ULONG(timer_list_buf +
OFFSET(timer_list_expires));
+ if (eflag && expires >= jiffies)
+ continue;
+
function = ULONG(timer_list_buf +
OFFSET(timer_list_function));
@@ -8666,7 +8693,9 @@ do_timer_list_v3(ulong vec_kvaddr,
int size,
ulong *vec,
void *option,
- ulong *highest)
+ ulong *highest,
+ ulong jiffies,
+ int eflag)
{
int i, t;
int count, tdx;
@@ -8725,6 +8754,9 @@ do_timer_list_v3(ulong vec_kvaddr,
expires = ULONG(timer_list_buf +
OFFSET(timer_list_expires));
+ if (eflag && expires >= jiffies)
+ continue;
+
function = ULONG(timer_list_buf +
OFFSET(timer_list_function));
@@ -8755,7 +8787,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 eflag)
{
int i, t, timer_cnt, found;
struct list_data list_data, *ld;
@@ -8810,6 +8842,9 @@ do_timer_list_v4(struct timer_bases_data *data)
continue;
expires = ULONG(timer_list_buf + OFFSET(timer_list_expires));
+ if (eflag && expires >= jiffies)
+ continue;
+
function = ULONG(timer_list_buf + OFFSET(timer_list_function));
data->timers[data->cnt].address = timer_list[t];
@@ -8839,7 +8874,7 @@ do_timer_list_v4(struct timer_bases_data *data)
* Linux 4.8 timers use new timer_bases[][]
*/
static void
-dump_timer_data_timer_bases(const ulong *cpus)
+dump_timer_data_timer_bases(const ulong *cpus, int eflag)
{
int i, cpu, flen, base, nr_bases, found, display, j = 0;
struct syment *sp;
@@ -8901,7 +8936,7 @@ next_base:
data.cnt = 0;
data.timer_base = timer_base;
- found = do_timer_list_v4(&data);
+ found = do_timer_list_v4(&data, jiffies, eflag);
qsort(data.timers, found, sizeof(struct timer_data), compare_timer_data);
--
2.22.0
5 years, 5 months
[PATCH] crash: dis: introduce count in reverse and forward mode
by Aaron Tomlin
The purpose of this patch is to add support for a count value in reverse
or forward mode, during disassembly:
'dis [-r|-f] [symbol|address] [count]'
For example:
crash> dis -f list_del+0x16 4
0xffffffff812b3346 <list_del+22>: jne 0xffffffff812b3381 <list_del+81>
0xffffffff812b3348 <list_del+24>: mov (%rbx),%rax
0xffffffff812b334b <list_del+27>: mov 0x8(%rax),%r8
0xffffffff812b334f <list_del+31>: cmp %r8,%rbx
crash> dis -r list_del+0x16 4
0xffffffff812b333c <list_del+12>: mov 0x8(%rdi),%rax
0xffffffff812b3340 <list_del+16>: mov (%rax),%r8
0xffffffff812b3343 <list_del+19>: cmp %r8,%rdi
0xffffffff812b3346 <list_del+22>: jne 0xffffffff812b3381 <list_del+81>
To support this feature, I have essentially incorported GDB commit
bb556f1facb ("Add negative repeat count to 'x' command"), with some
additional changes to maintain default behaviour i.e. always display the
target instruction with the examine command.
Signed-off-by: Aaron Tomlin <atomlin(a)redhat.com>
---
gdb-7.6.patch | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel.c | 33 ++---
2 files changed, 349 insertions(+), 15 deletions(-)
diff --git a/gdb-7.6.patch b/gdb-7.6.patch
index cd75dcf..96cdef4 100644
--- a/gdb-7.6.patch
+++ b/gdb-7.6.patch
@@ -2447,3 +2447,334 @@ diff -up gdb-7.6/opcodes/configure.orig gdb-7.6/opcodes/configure
#else
# error "!__i386__ && !__x86_64__"
#endif
+--- gdb-7.6/gdb/printcmd.c.orig
++++ gdb-7.6/gdb/printcmd.c
+@@ -201,8 +201,13 @@ decode_format (char **string_ptr, int oformat, int osize)
+ val.count = 1;
+ val.raw = 0;
+
++ if (*p == '-')
++ {
++ val.count = -1;
++ p++;
++ }
+ if (*p >= '0' && *p <= '9')
+- val.count = atoi (p);
++ val.count *= atoi (p);
+ while (*p >= '0' && *p <= '9')
+ p++;
+
+@@ -795,6 +800,232 @@ print_address_demangle (const struct value_print_options *opts,
+ }
+
+
++/* Find the address of the instruction that is INST_COUNT instructions before
++ the instruction at ADDR.
++ Since some architectures have variable-length instructions, we can't just
++ simply subtract INST_COUNT * INSN_LEN from ADDR. Instead, we use line
++ number information to locate the nearest known instruction boundary,
++ and disassemble forward from there. If we go out of the symbol range
++ during disassembling, we return the lowest address we've got so far and
++ set the number of instructions read to INST_READ. */
++
++static CORE_ADDR
++find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
++ int inst_count, int *inst_read)
++{
++ /* The vector PCS is used to store instruction addresses within
++ a pc range. */
++ CORE_ADDR loop_start, loop_end, p, func_addr;
++ VEC (CORE_ADDR) *pcs = NULL;
++ struct symtab_and_line sal;
++ struct cleanup *cleanup = make_cleanup (VEC_cleanup (CORE_ADDR), &pcs);
++ int actual_count = 0;
++
++ *inst_read = 0;
++ inst_count--;
++ loop_start = loop_end = addr;
++
++ find_pc_partial_function (addr, NULL, &func_addr, NULL);
++ for (p = func_addr; p != addr;)
++ {
++ p += gdb_insn_length (gdbarch, p);
++ actual_count++;
++ }
++ if (inst_count > actual_count)
++ inst_count = actual_count;
++
++ /* In each iteration of the outer loop, we get a pc range that ends before
++ LOOP_START, then we count and store every instruction address of the range
++ iterated in the loop.
++ If the number of instructions counted reaches INST_COUNT, return the
++ stored address that is located INST_COUNT instructions back from ADDR.
++ If INST_COUNT is not reached, we subtract the number of counted
++ instructions from INST_COUNT, and go to the next iteration. */
++ do
++ {
++ VEC_truncate (CORE_ADDR, pcs, 0);
++ sal = find_pc_sect_line (loop_start, NULL, 1);
++ if (sal.line <= 0)
++ {
++ /* We reach here when line info is not available. In this case,
++ we print a message and just exit the loop. The return value
++ is calculated after the loop. */
++ printf_filtered (_("No line number information available "
++ "for address "));
++ wrap_here (" ");
++ print_address (gdbarch, loop_start - 1, gdb_stdout);
++ printf_filtered ("\n");
++ break;
++ }
++
++ loop_end = loop_start;
++ loop_start = sal.pc;
++
++ /* This loop pushes instruction addresses in the range from
++ LOOP_START to LOOP_END. */
++ for (p = loop_start; p < loop_end;)
++ {
++ VEC_safe_push (CORE_ADDR, pcs, p);
++ p += gdb_insn_length (gdbarch, p);
++ }
++
++ inst_count -= VEC_length (CORE_ADDR, pcs);
++ *inst_read += VEC_length (CORE_ADDR, pcs);
++ }
++ while (inst_count > 0);
++
++ /* After the loop, the vector PCS has instruction addresses of the last
++ source line we processed, and INST_COUNT has a negative value.
++ We return the address at the index of -INST_COUNT in the vector for
++ the reason below.
++ Let's assume the following instruction addresses and run 'x/-4i 0x400e'.
++ Line X of File
++ 0x4000
++ 0x4001
++ 0x4005
++ Line Y of File
++ 0x4009
++ 0x400c
++ => 0x400e
++ 0x4011
++ find_instruction_backward is called with INST_COUNT = 4 and expected to
++ return 0x4001. When we reach here, INST_COUNT is set to -1 because
++ it was subtracted by 2 (from Line Y) and 3 (from Line X). The value
++ 4001 is located at the index 1 of the last iterated line (= Line X),
++ which is simply calculated by -INST_COUNT.
++ The case when the length of PCS is 0 means that we reached an area for
++ which line info is not available. In such case, we return LOOP_START,
++ which was the lowest instruction address that had line info. */
++ p = VEC_length (CORE_ADDR, pcs) > 0
++ ? VEC_index (CORE_ADDR, pcs, -inst_count)
++ : loop_start;
++
++ /* INST_READ includes all instruction addresses in a pc range. Need to
++ exclude the beginning part up to the address we're returning. That
++ is, exclude {0x4000} in the example above. */
++ if (inst_count < 0)
++ *inst_read += inst_count;
++
++ do_cleanups (cleanup);
++ return p;
++}
++
++/* Backward read LEN bytes of target memory from address MEMADDR + LEN,
++ placing the results in GDB's memory from MYADDR + LEN. Returns
++ a count of the bytes actually read. */
++
++static int
++read_memory_backward (struct gdbarch *gdbarch,
++ CORE_ADDR memaddr, gdb_byte *myaddr, int len)
++{
++ int errcode;
++ int nread; /* Number of bytes actually read. */
++
++ /* First try a complete read. */
++ errcode = target_read_memory (memaddr, myaddr, len);
++ if (errcode == 0)
++ {
++ /* Got it all. */
++ nread = len;
++ }
++ else
++ {
++ /* Loop, reading one byte at a time until we get as much as we can. */
++ memaddr += len;
++ myaddr += len;
++ for (nread = 0; nread < len; ++nread)
++ {
++ errcode = target_read_memory (--memaddr, --myaddr, 1);
++ if (errcode != 0)
++ {
++ /* The read was unsuccessful, so exit the loop. */
++ printf_filtered (_("Cannot access memory at address %s\n"),
++ paddress (gdbarch, memaddr));
++ break;
++ }
++ }
++ }
++ return nread;
++}
++
++/* Returns true if X (which is LEN bytes wide) is the number zero. */
++
++static int
++integer_is_zero (const gdb_byte *x, int len)
++{
++ int i = 0;
++
++ while (i < len && x[i] == 0)
++ ++i;
++ return (i == len);
++}
++
++/* Find the start address of a string in which ADDR is included.
++ Basically we search for '\0' and return the next address,
++ but if OPTIONS->PRINT_MAX is smaller than the length of a string,
++ we stop searching and return the address to print characters as many as
++ PRINT_MAX from the string. */
++
++static CORE_ADDR
++find_string_backward (struct gdbarch *gdbarch,
++ CORE_ADDR addr, int count, int char_size,
++ const struct value_print_options *options,
++ int *strings_counted)
++{
++ const int chunk_size = 0x20;
++ gdb_byte *buffer = NULL;
++ struct cleanup *cleanup = NULL;
++ int read_error = 0;
++ int chars_read = 0;
++ int chars_to_read = chunk_size;
++ int chars_counted = 0;
++ int count_original = count;
++ CORE_ADDR string_start_addr = addr;
++
++ gdb_assert (char_size == 1 || char_size == 2 || char_size == 4);
++ buffer = (gdb_byte *) xmalloc (chars_to_read * char_size);
++ cleanup = make_cleanup (xfree, buffer);
++ while (count > 0 && read_error == 0)
++ {
++ int i;
++
++ addr -= chars_to_read * char_size;
++ chars_read = read_memory_backward (gdbarch, addr, buffer,
++ chars_to_read * char_size);
++ chars_read /= char_size;
++ read_error = (chars_read == chars_to_read) ? 0 : 1;
++ /* Searching for '\0' from the end of buffer in backward direction. */
++ for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
++ {
++ int offset = (chars_to_read - i - 1) * char_size;
++
++ if (integer_is_zero (buffer + offset, char_size)
++ || chars_counted == options->print_max)
++ {
++ /* Found '\0' or reached print_max. As OFFSET is the offset to
++ '\0', we add CHAR_SIZE to return the start address of
++ a string. */
++ --count;
++ string_start_addr = addr + offset + char_size;
++ chars_counted = 0;
++ }
++ }
++ }
++
++ /* Update STRINGS_COUNTED with the actual number of loaded strings. */
++ *strings_counted = count_original - count;
++
++ if (read_error != 0)
++ {
++ /* In error case, STRING_START_ADDR is pointing to the string that
++ was last successfully loaded. Rewind the partially loaded string. */
++ string_start_addr -= chars_counted * char_size;
++ }
++
++ do_cleanups (cleanup);
++ return string_start_addr;
++}
++
+ /* Examine data at address ADDR in format FMT.
+ Fetch it from memory and print on gdb_stdout. */
+
+@@ -808,12 +1039,16 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
+ int i;
+ int maxelts;
+ struct value_print_options opts;
++ int need_to_update_next_address = 0;
++ CORE_ADDR addr_rewound = 0;
++ int is_backward;
+
+ format = fmt.format;
+ size = fmt.size;
+ count = fmt.count;
+ next_gdbarch = gdbarch;
+ next_address = addr;
++ is_backward = count < 0;
+
+ /* Instruction format implies fetch single bytes
+ regardless of the specified size.
+@@ -878,9 +1113,43 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
+
+ get_formatted_print_options (&opts, format);
+
++ if (is_backward)
++ {
++ /* This is the negative repeat count case.
++ We rewind the address based on the given repeat count and format,
++ then examine memory from there in forward direction. */
++
++ count = -count;
++ if (format == 'i')
++ {
++ next_address = find_instruction_backward (gdbarch, addr, count,
++ &count);
++ }
++ else if (format == 's')
++ {
++ next_address = find_string_backward (gdbarch, addr, count,
++ TYPE_LENGTH (val_type),
++ &opts, &count);
++ }
++ else
++ {
++ next_address = addr - count * TYPE_LENGTH (val_type);
++ }
++
++ /* The following call to print_formatted updates next_address in every
++ iteration. In backward case, we store the start address here
++ and update next_address with it before exiting the function. */
++ addr_rewound = (format == 's'
++ ? next_address - TYPE_LENGTH (val_type)
++ : next_address);
++ need_to_update_next_address = 1;
++ }
++
+ /* Print as many objects as specified in COUNT, at most maxelts per line,
+ with the address of the next one at the start of each line. */
+
++ if (is_backward)
++ count++;
+ while (count > 0)
+ {
+ QUIT;
+@@ -923,6 +1192,9 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
+ printf_filtered ("\n");
+ gdb_flush (gdb_stdout);
+ }
++
++ if (need_to_update_next_address)
++ next_address = addr_rewound;
+ }
+
+ static void
+@@ -2535,7 +2807,8 @@ Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
+ t(binary), f(float), a(address), i(instruction), c(char) and s(string).\n\
+ Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
+ The specified number of objects of the specified size are printed\n\
+-according to the format.\n\n\
++according to the format. If a negative number is specified, memory is\n\
++examined backward from the address.\n\n\
+ Defaults for format and size letters are those previously used.\n\
+ Default count is 1. Default address is following last thing printed\n\
+ with this command or \"print\"."));
diff --git a/kernel.c b/kernel.c
index 22909d2..f5960fc 100644
--- a/kernel.c
+++ b/kernel.c
@@ -1931,16 +1931,10 @@ cmd_dis(void)
}
if (args[++optind]) {
- if (reverse || forward) {
- error(INFO,
- "count argument ignored with -%s option\n",
- reverse ? "r" : "f");
- } else {
- req->count = stol(args[optind],
+ req->count = stol(args[optind],
FAULT_ON_ERROR, NULL);
- req->flags &= ~GNU_FUNCTION_ONLY;
- count_entered++;
- }
+ req->flags &= ~GNU_FUNCTION_ONLY;
+ count_entered++;
}
if (sources) {
@@ -1992,6 +1986,10 @@ cmd_dis(void)
}
}
+ if (reverse || forward)
+ if (count_entered && req->count == 1)
+ reverse = forward = 0;
+
if (reverse || forward) {
target = req->addr;
if ((sp = value_search(target, NULL)) == NULL)
@@ -2006,14 +2004,19 @@ cmd_dis(void)
do_machdep_filter = machdep->dis_filter(req->addr, NULL, radix);
open_tmpfile();
- if (reverse)
- sprintf(buf5, "x/%ldi 0x%lx",
- (target - req->addr) ? target - req->addr : 1,
- req->addr);
- else
+ if (reverse || forward) {
+ if (count_entered && req->count)
+ sprintf(buf5, "x/%s%ldi 0x%lx", reverse ? "-" : "",
+ req->count, target);
+ else
+ sprintf(buf5, "x/%ldi 0x%lx",
+ forward ? req->addr2 - req->addr :
+ (target - req->addr) ? target - req->addr : 1,
+ forward ? target : req->addr);
+ } else
sprintf(buf5, "x/%ldi 0x%lx",
count_entered && req->count ? req->count :
- forward || req->flags & GNU_FUNCTION_ONLY ?
+ req->flags & GNU_FUNCTION_ONLY ?
req->addr2 - req->addr : 1,
req->addr);
gdb_pass_through(buf5, NULL, GNU_RETURN_ON_ERROR);
--
2.20.1
5 years, 5 months
[PATCH] crash: bt: Introduce -p option
by Aaron Tomlin
The purpose of this patch is to provide a -p option to generate a stack
trace of the panic task, if available. It might be useful if the current
context is changed with the 'set' command.
This option is not supported under a live system or live dump.
Signed-off-by: Aaron Tomlin <atomlin(a)redhat.com>
---
help.c | 4 +++-
kernel.c | 19 +++++++++++++++----
2 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/help.c b/help.c
index c0c750f..581e616 100644
--- a/help.c
+++ b/help.c
@@ -1789,13 +1789,15 @@ NULL
char *help_bt[] = {
"bt",
"backtrace",
-"[-a|-c cpu(s)|-g|-r|-t|-T|-l|-e|-E|-f|-F|-o|-O|-v] [-R ref] [-s [-x|d]]"
+"[-a|-c cpu(s)|-g|-r|-t|-T|-l|-e|-E|-f|-F|-o|-O|-v|-p] [-R ref] [-s [-x|d]]"
"\n [-I ip] [-S sp] [pid | task]",
" Display a kernel stack backtrace. If no arguments are given, the stack",
" trace of the current context will be displayed.\n",
" -a displays the stack traces of the active task on each CPU.",
" (only applicable to crash dumps)",
" -A same as -a, but also displays vector registers (S390X only).",
+" -p display the stack trace of the panic task only.",
+" (only applicable to crash dumps)",
" -c cpu display the stack trace of the active task on one or more CPUs,",
" which can be specified using the format \"3\", \"1,8,9\", \"1-23\",",
" or \"1,8,9-14\". (only applicable to crash dumps)",
diff --git a/kernel.c b/kernel.c
index f5960fc..e1f0b7e 100644
--- a/kernel.c
+++ b/kernel.c
@@ -2402,7 +2402,7 @@ cmd_bt(void)
int i, c;
ulong value, *cpus;
struct task_context *tc;
- int subsequent, active;
+ int subsequent, active, panic;
struct stack_hook hook;
struct bt_info bt_info, bt_setup, *bt;
struct reference reference;
@@ -2412,7 +2412,7 @@ cmd_bt(void)
tc = NULL;
cpus = NULL;
- subsequent = active = 0;
+ subsequent = active = panic = 0;
hook.eip = hook.esp = 0;
refptr = 0;
bt = &bt_info;
@@ -2421,7 +2421,7 @@ cmd_bt(void)
if (kt->flags & USE_OPT_BT)
bt->flags |= BT_OPT_BACK_TRACE;
- while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:Ov")) != EOF) {
+ while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:Ovp")) != EOF) {
switch (c)
{
case 'f':
@@ -2609,6 +2609,14 @@ cmd_bt(void)
option_not_supported(c);
check_stack_overflow();
return;
+ case 'p':
+ if (LIVE())
+ error(FATAL,
+ "-p option not supported on a live system or live dump\n");
+ if (!tt->panic_task)
+ error(FATAL, "no panic task found!\n");
+ panic++;
+ break;
default:
argerrs++;
@@ -2748,7 +2756,10 @@ cmd_bt(void)
tgid = task_tgid(CURRENT_TASK());
DO_THREAD_GROUP_BACKTRACE();
} else {
- tc = CURRENT_CONTEXT();
+ if (panic)
+ tc = task_to_context(tt->panic_task);
+ else
+ tc = CURRENT_CONTEXT();
DO_TASK_BACKTRACE();
}
return;
--
2.20.1
5 years, 5 months
Re: [Crash-utility] [PATCH] Fix unsigned signed comparison causing segfault for small VMCOREINFO notes
by Dave Anderson
Hi Nuno,
Your patch is queued for crash-7.2.7:
https://github.com/crash-utility/crash/commit/0687bd12d288d2e056a6e46426f...
Thanks,
Dave
----- Original Message -----
> Thanks for that extra info Dave - it's super helpful!
>
> Cheers,
> Nuno
>
> > -----Original Message-----
> > From: Dave Anderson <anderson(a)redhat.com>
> > Sent: Thursday, June 6, 2019 7:22 AM
> > To: Nuno Das Neves <Nuno.Das(a)microsoft.com>
> > Cc: crash-utility(a)redhat.com
> > Subject: Re: [Crash-utility] [PATCH] Fix unsigned signed comparison causing
> > segfault for small VMCOREINFO notes
> >
> > ----- Original Message -----
> > > > -----Original Message-----
> > > > From: Dave Anderson <anderson(a)redhat.com>
> > > > Sent: Wednesday, June 5, 2019 2:56 PM
> > > > To: Nuno Das Neves <Nuno.Das(a)microsoft.com>
> > > > Cc: Nuno Das Neves <Nuno.Das(a)microsoft.com>
> > > > Subject: Fwd: [Crash-utility] [PATCH] Fix unsigned signed comparison
> > > > causing segfault for small VMCOREINFO notes
> > > >
> > > >
> > > >
> > > > ----- Forwarded Message -----
> > > > From: "Dave Anderson" <anderson(a)redhat.com>
> > > > To: "Discussion list for crash utility usage, maintenance and
> > > > development"
> > > > <crash-utility(a)redhat.com>
> > > > Sent: Wednesday, June 5, 2019 4:18:02 PM
> > > > Subject: Re: [Crash-utility] [PATCH] Fix unsigned signed comparison
> > > > causing
> > > > segfault for small VMCOREINFO notes
> > > >
> > > >
> > > >
> > > > ----- Original Message -----
> > > > > Hi,
> > > > >
> > > > > This is a fix for a signed/unsigned comparison bug in
> > > > > vmcoreinfo_read_string.
> > > > > The bug causes a segmentation fault if size_vmcoreinfo + 1 is smaller
> > > > > than
> > > > > the length of the key string passed in.
> > > >
> > > > I suppose that's true, but can you describe the instance where that
> > > > actually happened?
> > >
> > > I'm debugging some issues with vm2core
> > > (github dot com slash Azure slash azure-linux-utils), a tool for
> > > converting Hyper-V snapshots to elf core files that can be analyzed with
> > > Crash.
> > > There was a problem where versions of Crash newer than 7.1.5 crashed with
> > > elf
> > > files generated by vm2core due to this issue.
> > >
> > > > Can you show the actual note contents as shown by "help -D"?
> > >
> > > Elf64_Nhdr:
> > > n_namesz: 11 ("VMCOREINFO")
> > > n_descsz: 10
> > > n_type: 0 (unused)
> > > VMCOREINFO
> > >
> > > So it seems the note doesn't really contain valid data as defined in
> > > Documentation/kdump/vmcoreinfo.txt.
> > >
> > > I've done some additional testing and it appears this note isn't needed
> > > by
> > > Crash at all - I can simply patch vm2core so it doesn't add the note.
> > > So, I suppose this fix isn't required to solve my particular issue.
> >
> > I don't think you should avoid adding the VMCOREINFO note contents from the
> > kernel.
> >
> > While the vast majority of VMCOREINFO items are only used by
> > makedumpfile(8),
> > several items are used by the crash utility. For example, the x86_64
> > requires
> > things like the kernel's phys_offset value (at least if it's non-zero),
> > either
> > the relocated "_stext" symbol value or KERNELOFFSET value for KASLR
> > kernels,
> > possibly the KERNEL_IMAGE_SIZE, and to be able to recognize 5-level page
> > tables.
> >
> > >
> > > However, could the VMCOREINFO note legitimately contain a single field
> > > e.g. "PAGE_SIZE=4096"?
> > > If so, this is just 14 characters; 14 + 1 <
> > > strlen("NUMBER(KERNEL_IMAGE_SIZE)") - this string is used as the argument
> > > to
> > > vmcoreinfo_read_string on line 202 of x86_64.c.
> >
> > No, it would never contains a single field. Here's a typical dump on a
> > 5.0.0 x86_64
> > kernel:
> >
> > crash> help -D
> > ... [ cut ] ...
> >
> > n_namesz: 11 ("VMCOREINFO")
> > n_descsz: 1980
> > n_type: 0 (unused)
> > OSRELEASE=5.0.0+
> > PAGESIZE=4096
> > SYMBOL(init_uts_ns)=ffffffffa9615540
> > SYMBOL(node_online_map)=ffffffffa976e068
> > SYMBOL(swapper_pg_dir)=ffffffffa960e000
> > SYMBOL(_stext)=ffffffffa8200000
> > SYMBOL(vmap_area_list)=ffffffffa9666230
> > SYMBOL(mem_section)=ffff90adbbff8000
> > LENGTH(mem_section)=2048
> > SIZE(mem_section)=16
> > OFFSET(mem_section.section_mem_map)=0
> > SIZE(page)=64
> > SIZE(pglist_data)=14016
> > SIZE(zone)=1280
> > SIZE(free_area)=72
> > SIZE(list_head)=16
> > SIZE(nodemask_t)=8
> > OFFSET(page.flags)=0
> > OFFSET(page._refcount)=52
> > OFFSET(page.mapping)=24
> > OFFSET(page.lru)=8
> > OFFSET(page._mapcount)=48
> > OFFSET(page.private)=40
> > OFFSET(page.compound_dtor)=16
> > OFFSET(page.compound_order)=17
> > OFFSET(page.compound_head)=8
> > OFFSET(pglist_data.node_zones)=0
> > OFFSET(pglist_data.nr_zones)=13344
> > OFFSET(pglist_data.node_start_pfn)=13352
> > OFFSET(pglist_data.node_spanned_pages)=13368
> > OFFSET(pglist_data.node_id)=13376
> > OFFSET(zone.free_area)=192
> > OFFSET(zone.vm_stat)=1088
> > OFFSET(zone.spanned_pages)=112
> > OFFSET(free_area.free_list)=0
> > OFFSET(list_head.next)=0
> > OFFSET(list_head.prev)=8
> > OFFSET(vmap_area.va_start)=0
> > OFFSET(vmap_area.list)=48
> > LENGTH(zone.free_area)=11
> > SYMBOL(log_buf)=ffffffffa964b250
> > SYMBOL(log_buf_len)=ffffffffa964b24c
> > SYMBOL(log_first_idx)=ffffffffa9d71668
> > SYMBOL(clear_idx)=ffffffffa9d71634
> > SYMBOL(log_next_idx)=ffffffffa9d71658
> > SIZE(printk_log)=16
> > OFFSET(printk_log.ts_nsec)=0
> > OFFSET(printk_log.len)=8
> > OFFSET(printk_log.text_len)=10
> > OFFSET(printk_log.dict_len)=12
> > LENGTH(free_area.free_list)=4
> > NUMBER(NR_FREE_PAGES)=0
> > NUMBER(PG_lru)=4
> > NUMBER(PG_private)=13
> > NUMBER(PG_swapcache)=10
> > NUMBER(PG_swapbacked)=19
> > NUMBER(PG_slab)=9
> > NUMBER(PG_head_mask)=65536
> > NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE)=-129
> > NUMBER(HUGETLB_PAGE_DTOR)=2
> > NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE)=-257
> > NUMBER(phys_base)=-497025024
> > SYMBOL(init_top_pgt)=ffffffffa960e000
> > NUMBER(pgtable_l5_enabled)=0
> > SYMBOL(node_data)=ffffffffa976d680
> > LENGTH(node_data)=64
> > KERNELOFFSET=27200000
> > NUMBER(KERNEL_IMAGE_SIZE)=1073741824
> > NUMBER(sme_mask)=0
> > CRASHTIME=1555324290
> >
> > I would guess the problem is how much awareness your vm2core facility has
> > concerning the target kernel, e.g., can it can find the physical
> > memory containing the VMCOREINFO data. On the target system,
> > the physical address and a page-granularity size can be located here:
> >
> > $ cat /sys/kernel/vmcoreinfo
> > 2a21197c0 1000
> > $
> >
> > The kernel symbols of the data is "vmcoreinfo_data", and the actual
> > size of the note is "vmcoreinfo_size". Or in 4.19 and later kernels,
> > /proc/kcore now has the VMCOREINFO note appended.
> >
> > The KVM developers went through the same process as you are, and they
> > eventually implemented a mechanism where they pass the vmcoreinfo data from
> > the target kernel to the KVM host that is doing a "virsh dump".
> >
> > In any case, even though it's theoretically impossible to have a
> > zero-length
> > or very small VMCOREINFO note, your patch does makes logical sense, so I
> > will
> > apply it.
> >
> > Thanks,
> > Dave
> >
> > > Regards,
> > > Nuno
> > >
> > > >
> > > > Thanks,
> > > > Dave
> > > >
> > > >
> > > > >
> > > > > Signed-off-by: Nuno Das Neves <nudasnev(a)microsoft.com>
> > > > > ---
> > > > > netdump.c | 2 +-
> > > > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/netdump.c b/netdump.c
> > > > > index 40f9cde..d257ecd 100644
> > > > > --- a/netdump.c
> > > > > +++ b/netdump.c
> > > > > @@ -1838,7 +1838,7 @@ vmcoreinfo_read_string(const char *key)
> > > > > return NULL;
> > > > >
> > > > > /* the '+ 1' is the equal sign */
> > > > > - for (i = 0; i < (size_vmcoreinfo - key_length + 1); i++) {
> > > > > + for (i = 0; i < (int)(size_vmcoreinfo - key_length + 1); i++) {
> > > > > /*
> > > > > * We must also check if we're at the beginning of VMCOREINFO
> > > > > * or the separating newline is there, and of course if we
> > > > > --
> > > > > 1.8.3.1
> > > > >
> > > > > --
> > > > > Crash-utility mailing list
> > > > > Crash-utility(a)redhat.com
> > > > > https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.red...
> > > >
> > utility&data=02%7C01%7CNuno.Das%40microsoft.com%7C3a91428c54b34ed7855d08d6ea008afe%7C72f988bf86f141af91ab2d7cd0
> > > > 11db47%7C1%7C0%7C636953685382744097&sdata=BVID2b0QYkDmBip0bzPjX%2Fl4vltJtf78JrTE7pnqSIM%3D&reserved=0
> > > > >
> > >
>
5 years, 5 months
[PATCH] Fix unsigned signed comparison causing segfault for small VMCOREINFO notes
by Nuno Das Neves
Hi,
This is a fix for a signed/unsigned comparison bug in vmcoreinfo_read_string.
The bug causes a segmentation fault if size_vmcoreinfo + 1 is smaller than the length of the key string passed in.
Signed-off-by: Nuno Das Neves <nudasnev(a)microsoft.com>
---
netdump.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/netdump.c b/netdump.c
index 40f9cde..d257ecd 100644
--- a/netdump.c
+++ b/netdump.c
@@ -1838,7 +1838,7 @@ vmcoreinfo_read_string(const char *key)
return NULL;
/* the '+ 1' is the equal sign */
- for (i = 0; i < (size_vmcoreinfo - key_length + 1); i++) {
+ for (i = 0; i < (int)(size_vmcoreinfo - key_length + 1); i++) {
/*
* We must also check if we're at the beginning of VMCOREINFO
* or the separating newline is there, and of course if we
--
1.8.3.1
5 years, 5 months