The patch titled Subject: scripts/gdb: add a timer list command has been added to the -mm tree. Its filename is scripts-gdb-add-a-timer-list-command.patch This patch should soon appear at http://ozlabs.org/~akpm/mmots/broken-out/scripts-gdb-add-a-timer-list-command.patch and later at http://ozlabs.org/~akpm/mmotm/broken-out/scripts-gdb-add-a-timer-list-command.patch Before you just go and hit "reply", please: a) Consider who else should be cc'ed b) Prefer to cc a suitable mailing list as well c) Ideally: find the original patch on the mailing list and do a reply-to-all to that, adding suitable additional cc's *** Remember to use Documentation/process/submit-checklist.rst when testing your code *** The -mm tree is included into linux-next and is updated there every 3-4 working days ------------------------------------------------------ From: Stephen Boyd <swboyd@xxxxxxxxxxxx> Subject: scripts/gdb: add a timer list command Implement a command to print the timer list, much like how /proc/timer_list is implemented. This can be used to look at the pending timers on a crashed system. Link: http://lkml.kernel.org/r/20190325184522.260535-5-swboyd@xxxxxxxxxxxx Signed-off-by: Stephen Boyd <swboyd@xxxxxxxxxxxx> Cc: Douglas Anderson <dianders@xxxxxxxxxxxx> Cc: Nikolay Borisov <n.borisov.lkml@xxxxxxxxx> Cc: Kieran Bingham <kbingham@xxxxxxxxxx> Cc: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> Cc: Jackie Liu <liuyun01@xxxxxxxxxx> Cc: Jason Wessel <jason.wessel@xxxxxxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> --- scripts/gdb/linux/constants.py.in | 8 + scripts/gdb/linux/timerlist.py | 194 ++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 3 files changed, 203 insertions(+) --- a/scripts/gdb/linux/constants.py.in~scripts-gdb-add-a-timer-list-command +++ a/scripts/gdb/linux/constants.py.in @@ -15,6 +15,7 @@ #include <linux/fs.h> #include <linux/mount.h> #include <linux/of_fdt.h> +#include <linux/hrtimer.h> /* We need to stringify expanded macros so that they can be parsed */ @@ -44,6 +45,9 @@ LX_VALUE(SB_DIRSYNC) LX_VALUE(SB_NOATIME) LX_VALUE(SB_NODIRATIME) +/* linux/htimer.h */ +LX_GDBPARSED(hrtimer_resolution) + /* linux/mount.h */ LX_VALUE(MNT_NOSUID) LX_VALUE(MNT_NODEV) @@ -56,4 +60,8 @@ LX_VALUE(MNT_RELATIME) LX_VALUE(OF_DT_HEADER) /* Kernel Configs */ +LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS) +LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) +LX_CONFIG(CONFIG_HIGH_RES_TIMERS) LX_CONFIG(CONFIG_OF) +LX_CONFIG(CONFIG_TICK_ONESHOT) --- /dev/null +++ a/scripts/gdb/linux/timerlist.py @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2019 Google LLC. + +import gdb + +from linux import constants +from linux import cpus +from linux import rbtree +from linux import utils + +timerqueue_node_type = utils.CachedType("struct timerqueue_node") +hrtimer_type = utils.CachedType("struct hrtimer") + + +def ktime_get(): + """Returns the current time, but not very accurately + + We can't read the hardware timer itself to add any nanoseconds + that need to be added since we last stored the time in the + timekeeper. But this is probably good enough for debug purposes.""" + tk_core = gdb.parse_and_eval("&tk_core") + + return tk_core['timekeeper']['tkr_mono']['base'] + +def print_timer(rb_node, idx): + timerqueue = utils.container_of(rb_node, timerqueue_node_type.get_type().pointer(), "node") + timer = utils.container_of(timerqueue, hrtimer_type.get_type().pointer(), "node") + + function = str(timer['function']).split(" ")[1].strip("<>") + softexpires = timer['_softexpires'] + expires = timer['node']['expires'] + now = ktime_get() + + text = " #{}: <{}>, {}, ".format(idx, timer, function) + text += "S:{:02x}\n".format(int(timer['state'])) + text += " # expires at {}-{} nsecs [in {} to {} nsecs]\n".format(softexpires, + expires, softexpires - now, expires - now) + return text + +def print_active_timers(base): + curr = base['active']['next']['node'] + curr = curr.address.cast(rbtree.rb_node_type.get_type().pointer()) + idx = 0 + while curr: + yield print_timer(curr, idx) + curr = rbtree.rb_next(curr) + idx += 1 + +def print_base(base): + text = " .base: {}\n".format(base.address) + text += " .index: {}\n".format(base['index']) + + text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution) + + text += " .get_time: {}\n".format(base['get_time']) + if constants.LX_CONFIG_HIGH_RES_TIMERS: + text += " .offset: {} nsecs\n".format(base['offset']) + text += "active timers:\n" + text += "".join([x for x in print_active_timers(base)]) + return text + +def print_cpu(hrtimer_bases, cpu, max_clock_bases): + cpu_base = cpus.per_cpu(hrtimer_bases, cpu) + jiffies = gdb.parse_and_eval("jiffies") + tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched") + ts = cpus.per_cpu(tick_sched_ptr, cpu) + + text = "cpu: {}\n".format(cpu) + for i in xrange(max_clock_bases): + text += " clock {}:\n".format(i) + text += print_base(cpu_base['clock_base'][i]) + if constants.LX_CONFIG_HIGH_RES_TIMERS: + text += """ .expires_next : {} nsecs + .hres_active : {} + .nr_events : {} + .nr_retries : {} + .nr_hangs : {} + .max_hang_time : {} +""".format(cpu_base['expires_next'], + cpu_base['hres_active'], + cpu_base['nr_events'], + cpu_base['nr_retries'], + cpu_base['nr_hangs'], + cpu_base['max_hang_time']) + if constants.LX_CONFIG_TICK_ONESHOT: + text += """ .nohz_mode : {} + .last_tick : {} nsecs + .tick_stopped : {} + .idle_jiffies : {} + .idle_calls : {} + .idle_sleeps : {} + .idle_entrytime : {} nsecs + .idle_waketime : {} nsecs + .idle_exittime : {} nsecs + .idle_sleeptime : {} nsecs + .iowait_sleeptime: {} nsecs + .last_jiffies : {} + .next_timer : {} + .idle_expires : {} nsecs +jiffies: {} +""".format(ts['nohz_mode'], + ts['last_tick'], + ts['tick_stopped'], + ts['idle_jiffies'], + ts['idle_calls'], + ts['idle_sleeps'], + ts['idle_entrytime'], + ts['idle_waketime'], + ts['idle_exittime'], + ts['idle_sleeptime'], + ts['iowait_sleeptime'], + ts['last_jiffies'], + ts['next_timer'], + ts['idle_expires'], + jiffies) + + text += "\n" + + return text + +def print_tickdevice(td, cpu): + dev = td['evtdev'] + text = "Tick Device: mode: {}\n".format(td['mode']) + if cpu < 0: + text += "Broadcast device\n" + else: + text += "Per CPU device: {}\n".format(cpu) + + text += "Clock Event Device: " + if dev == 0: + text += "<NULL>\n" + return text + + text += "{}\n".format(dev['name']) + text += " max_delta_ns: {}\n".format(dev['max_delta_ns']) + text += " min_delta_ns: {}\n".format(dev['min_delta_ns']) + text += " mult: {}\n".format(dev['mult']) + text += " shift: {}\n".format(dev['shift']) + text += " mode: {}\n".format(dev['state_use_accessors']) + text += " next_event: {} nsecs\n".format(dev['next_event']) + + text += " set_next_event: {}\n".format(dev['set_next_event']) + + members = [('set_state_shutdown', " shutdown: {}\n"), + ('set_state_periodic', " periodic: {}\n"), + ('set_state_oneshot', " oneshot: {}\n"), + ('set_state_oneshot_stopped', " oneshot stopped: {}\n"), + ('tick_resume', " resume: {}\n"), + ] + for (member, fmt) in members: + if dev[member]: + text += fmt.format(dev[member]) + + text += " event_handler: {}\n".format(dev['event_handler']) + text += " retries: {}\n".format(dev['retries']) + + return text + +class LxTimerList(gdb.Command): + """Print /proc/timer_list""" + + def __init__(self): + super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases") + max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES") + + text = "Timer List Version: gdb scripts\n" + text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(max_clock_bases) + text += "now at {} nsecs\n".format(ktime_get()) + + for cpu in cpus.each_online_cpu(): + text += print_cpu(hrtimer_bases, cpu, max_clock_bases) + + if constants.LX_CONFIG_GENERIC_CLOCKEVENTS: + if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST: + bc_dev = gdb.parse_and_eval("&tick_broadcast_device") + text += print_tickdevice(bc_dev, -1) + text += "\n" + mask = gdb.parse_and_eval("tick_broadcast_mask") + text += "tick_broadcast_mask: {}\n".format(mask) # TODO: format properly + if constants.LX_CONFIG_TICK_ONESHOT: + mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask") + text += "tick_broadcast_oneshot_mask: {}\n".format(mask) # TODO: format properly + text += "\n" + + tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device") + text += "\n".join([print_tickdevice(cpus.per_cpu(tick_cpu_devices, cpu), cpu) for cpu in cpus.each_online_cpu()]) + + gdb.write(text) + +LxTimerList() --- a/scripts/gdb/vmlinux-gdb.py~scripts-gdb-add-a-timer-list-command +++ a/scripts/gdb/vmlinux-gdb.py @@ -33,3 +33,4 @@ else: import linux.rbtree import linux.proc import linux.constants + import linux.timerlist _ Patches currently in -mm which might be from swboyd@xxxxxxxxxxxx are scripts-gdb-find-vmlinux-where-it-was-before.patch scripts-gdb-add-kernel-config-dumping-command.patch scripts-gdb-add-rb-tree-iterating-utilities.patch scripts-gdb-add-a-timer-list-command.patch