xref: /linux-6.15/scripts/gdb/linux/timerlist.py (revision 747cd84f)
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 xrange(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
161    chunks = []
162    i = num_bytes
163    while i > 0:
164        i -= 1
165        start = i * 2
166        end = start + 2
167        chunks.append(buf[start:end])
168        if i != 0 and i % 4 == 0:
169            chunks.append(',')
170
171    extra = nr_cpu_ids % 8
172    if 0 < extra <= 4:
173        chunks[0] = chunks[0][0]  # Cut off the first 0
174
175    return "".join(chunks)
176
177
178class LxTimerList(gdb.Command):
179    """Print /proc/timer_list"""
180
181    def __init__(self):
182        super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA)
183
184    def invoke(self, arg, from_tty):
185        hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases")
186        max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES")
187
188        text = "Timer List Version: gdb scripts\n"
189        text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(max_clock_bases)
190        text += "now at {} nsecs\n".format(ktime_get())
191
192        for cpu in cpus.each_online_cpu():
193            text += print_cpu(hrtimer_bases, cpu, max_clock_bases)
194
195        if constants.LX_CONFIG_GENERIC_CLOCKEVENTS:
196            if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:
197                bc_dev = gdb.parse_and_eval("&tick_broadcast_device")
198                text += print_tickdevice(bc_dev, -1)
199                text += "\n"
200                mask = gdb.parse_and_eval("tick_broadcast_mask")
201                mask = pr_cpumask(mask)
202                text += "tick_broadcast_mask: {}\n".format(mask)
203                if constants.LX_CONFIG_TICK_ONESHOT:
204                    mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
205                    mask = pr_cpumask(mask)
206                    text += "tick_broadcast_oneshot_mask: {}\n".format(mask)
207                text += "\n"
208
209            tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
210            for cpu in cpus.each_online_cpu():
211                tick_dev = cpus.per_cpu(tick_cpu_devices, cpu)
212                text += print_tickdevice(tick_dev, cpu)
213                text += "\n"
214
215        gdb.write(text)
216
217
218LxTimerList()
219