xref: /linux-6.15/scripts/gdb/linux/timerlist.py (revision 7362042f)
1# SPDX-License-Identifier: GPL-2.0
2#
3# Copyright 2019 Google LLC.
4
5import binascii
6import gdb
7
8from linux import constants
9from linux import cpus
10from linux import rbtree
11from linux import utils
12
13timerqueue_node_type = utils.CachedType("struct timerqueue_node").get_type()
14hrtimer_type = utils.CachedType("struct hrtimer").get_type()
15
16
17def ktime_get():
18    """Returns the current time, but not very accurately
19
20    We can't read the hardware timer itself to add any nanoseconds
21    that need to be added since we last stored the time in the
22    timekeeper. But this is probably good enough for debug purposes."""
23    tk_core = gdb.parse_and_eval("&tk_core")
24
25    return tk_core['timekeeper']['tkr_mono']['base']
26
27
28def print_timer(rb_node, idx):
29    timerqueue = utils.container_of(rb_node, timerqueue_node_type.pointer(),
30                                    "node")
31    timer = utils.container_of(timerqueue, hrtimer_type.pointer(), "node")
32
33    function = str(timer['function']).split(" ")[1].strip("<>")
34    softexpires = timer['_softexpires']
35    expires = timer['node']['expires']
36    now = ktime_get()
37
38    text = " #{}: <{}>, {}, ".format(idx, timer, function)
39    text += "S:{:02x}\n".format(int(timer['state']))
40    text += " # expires at {}-{} nsecs [in {} to {} nsecs]\n".format(
41            softexpires, expires, softexpires - now, expires - now)
42    return text
43
44
45def print_active_timers(base):
46    curr = base['active']['rb_root']['rb_leftmost']
47    idx = 0
48    while curr:
49        yield print_timer(curr, idx)
50        curr = rbtree.rb_next(curr)
51        idx += 1
52
53
54def print_base(base):
55    text = " .base:       {}\n".format(base.address)
56    text += " .index:      {}\n".format(base['index'])
57
58    text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution)
59
60    text += " .get_time:   {}\n".format(base['get_time'])
61    if constants.LX_CONFIG_HIGH_RES_TIMERS:
62        text += "  .offset:     {} nsecs\n".format(base['offset'])
63    text += "active timers:\n"
64    text += "".join([x for x in print_active_timers(base)])
65    return text
66
67
68def print_cpu(hrtimer_bases, cpu, max_clock_bases):
69    cpu_base = cpus.per_cpu(hrtimer_bases, cpu)
70    jiffies = gdb.parse_and_eval("jiffies_64")
71    tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched")
72    ts = cpus.per_cpu(tick_sched_ptr, cpu)
73
74    text = "cpu: {}\n".format(cpu)
75    for i in range(max_clock_bases):
76        text += " clock {}:\n".format(i)
77        text += print_base(cpu_base['clock_base'][i])
78
79        if constants.LX_CONFIG_HIGH_RES_TIMERS:
80            fmts = [("  .{}   : {} nsecs", 'expires_next'),
81                    ("  .{}    : {}", 'hres_active'),
82                    ("  .{}      : {}", 'nr_events'),
83                    ("  .{}     : {}", 'nr_retries'),
84                    ("  .{}       : {}", 'nr_hangs'),
85                    ("  .{}  : {}", 'max_hang_time')]
86            text += "\n".join([s.format(f, cpu_base[f]) for s, f in fmts])
87            text += "\n"
88
89        if constants.LX_CONFIG_TICK_ONESHOT:
90            fmts = [("  .{}      : {}", 'nohz_mode'),
91                    ("  .{}      : {} nsecs", 'last_tick'),
92                    ("  .{}   : {}", 'tick_stopped'),
93                    ("  .{}   : {}", 'idle_jiffies'),
94                    ("  .{}     : {}", 'idle_calls'),
95                    ("  .{}    : {}", 'idle_sleeps'),
96                    ("  .{} : {} nsecs", 'idle_entrytime'),
97                    ("  .{}  : {} nsecs", 'idle_waketime'),
98                    ("  .{}  : {} nsecs", 'idle_exittime'),
99                    ("  .{} : {} nsecs", 'idle_sleeptime'),
100                    ("  .{}: {} nsecs", 'iowait_sleeptime'),
101                    ("  .{}   : {}", 'last_jiffies'),
102                    ("  .{}     : {}", 'next_timer'),
103                    ("  .{}   : {} nsecs", 'idle_expires')]
104            text += "\n".join([s.format(f, ts[f]) for s, f in fmts])
105            text += "\njiffies: {}\n".format(jiffies)
106
107        text += "\n"
108
109    return text
110
111
112def print_tickdevice(td, cpu):
113    dev = td['evtdev']
114    text = "Tick Device: mode:     {}\n".format(td['mode'])
115    if cpu < 0:
116            text += "Broadcast device\n"
117    else:
118            text += "Per CPU device: {}\n".format(cpu)
119
120    text += "Clock Event Device: "
121    if dev == 0:
122            text += "<NULL>\n"
123            return text
124
125    text += "{}\n".format(dev['name'])
126    text += " max_delta_ns:   {}\n".format(dev['max_delta_ns'])
127    text += " min_delta_ns:   {}\n".format(dev['min_delta_ns'])
128    text += " mult:           {}\n".format(dev['mult'])
129    text += " shift:          {}\n".format(dev['shift'])
130    text += " mode:           {}\n".format(dev['state_use_accessors'])
131    text += " next_event:     {} nsecs\n".format(dev['next_event'])
132
133    text += " set_next_event: {}\n".format(dev['set_next_event'])
134
135    members = [('set_state_shutdown', " shutdown: {}\n"),
136               ('set_state_periodic', " periodic: {}\n"),
137               ('set_state_oneshot', " oneshot:  {}\n"),
138               ('set_state_oneshot_stopped', " oneshot stopped: {}\n"),
139               ('tick_resume', " resume:   {}\n")]
140    for member, fmt in members:
141        if dev[member]:
142            text += fmt.format(dev[member])
143
144    text += " event_handler:  {}\n".format(dev['event_handler'])
145    text += " retries:        {}\n".format(dev['retries'])
146
147    return text
148
149
150def pr_cpumask(mask):
151    nr_cpu_ids = 1
152    if constants.LX_NR_CPUS > 1:
153        nr_cpu_ids = gdb.parse_and_eval("nr_cpu_ids")
154
155    inf = gdb.inferiors()[0]
156    bits = mask['bits']
157    num_bytes = (nr_cpu_ids + 7) / 8
158    buf = utils.read_memoryview(inf, bits, num_bytes).tobytes()
159    buf = binascii.b2a_hex(buf)
160    if type(buf) is not str:
161        buf=buf.decode()
162
163    chunks = []
164    i = num_bytes
165    while i > 0:
166        i -= 1
167        start = i * 2
168        end = start + 2
169        chunks.append(buf[start:end])
170        if i != 0 and i % 4 == 0:
171            chunks.append(',')
172
173    extra = nr_cpu_ids % 8
174    if 0 < extra <= 4:
175        chunks[0] = chunks[0][0]  # Cut off the first 0
176
177    return "".join(chunks)
178
179
180class LxTimerList(gdb.Command):
181    """Print /proc/timer_list"""
182
183    def __init__(self):
184        super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA)
185
186    def invoke(self, arg, from_tty):
187        hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases")
188        max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES")
189
190        text = "Timer List Version: gdb scripts\n"
191        text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(max_clock_bases)
192        text += "now at {} nsecs\n".format(ktime_get())
193
194        for cpu in cpus.each_online_cpu():
195            text += print_cpu(hrtimer_bases, cpu, max_clock_bases)
196
197        if constants.LX_CONFIG_GENERIC_CLOCKEVENTS:
198            if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:
199                bc_dev = gdb.parse_and_eval("&tick_broadcast_device")
200                text += print_tickdevice(bc_dev, -1)
201                text += "\n"
202                mask = gdb.parse_and_eval("tick_broadcast_mask")
203                mask = pr_cpumask(mask)
204                text += "tick_broadcast_mask: {}\n".format(mask)
205                if constants.LX_CONFIG_TICK_ONESHOT:
206                    mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
207                    mask = pr_cpumask(mask)
208                    text += "tick_broadcast_oneshot_mask: {}\n".format(mask)
209                text += "\n"
210
211            tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
212            for cpu in cpus.each_online_cpu():
213                tick_dev = cpus.per_cpu(tick_cpu_devices, cpu)
214                text += print_tickdevice(tick_dev, cpu)
215                text += "\n"
216
217        gdb.write(text)
218
219
220LxTimerList()
221