xref: /linux/scripts/gdb/linux/timerlist.py (revision 29807c524d66e27762cdc8992c2cac89b4c3fda9)
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("&timekeeper_data[TIMEKEEPER_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    if constants.LX_CONFIG_HIGH_RES_TIMERS:
60        text += "  .offset:     {} nsecs\n".format(base['offset'])
61    text += "active timers:\n"
62    text += "".join([x for x in print_active_timers(base)])
63    return text
64
65
66def print_cpu(hrtimer_bases, cpu, max_clock_bases):
67    cpu_base = cpus.per_cpu(hrtimer_bases, cpu)
68    jiffies = gdb.parse_and_eval("jiffies_64")
69    tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched")
70    ts = cpus.per_cpu(tick_sched_ptr, cpu)
71
72    text = "cpu: {}\n".format(cpu)
73    for i in range(max_clock_bases):
74        text += " clock {}:\n".format(i)
75        text += print_base(cpu_base['clock_base'][i])
76
77        if constants.LX_CONFIG_HIGH_RES_TIMERS:
78            fmts = [("  .{}   : {} nsecs", 'expires_next'),
79                    ("  .{}    : {}", 'hres_active'),
80                    ("  .{}      : {}", 'nr_events'),
81                    ("  .{}     : {}", 'nr_retries'),
82                    ("  .{}       : {}", 'nr_hangs'),
83                    ("  .{}  : {}", 'max_hang_time')]
84            text += "\n".join([s.format(f, cpu_base[f]) for s, f in fmts])
85            text += "\n"
86
87        if constants.LX_CONFIG_TICK_ONESHOT:
88            TS_FLAG_STOPPED = 1 << 1
89            TS_FLAG_NOHZ = 1 << 4
90            text += f"  .{'nohz':15s}: {int(bool(ts['flags'] & TS_FLAG_NOHZ))}\n"
91            text += f"  .{'last_tick':15s}: {ts['last_tick']}\n"
92            text += f"  .{'tick_stopped':15s}: {int(bool(ts['flags'] & TS_FLAG_STOPPED))}\n"
93            text += f"  .{'idle_calls':15s}: {ts['idle_calls']}\n"
94            text += f"  .{'idle_sleeps':15s}: {ts['idle_sleeps']}\n"
95            text += f"  .{'idle_entrytime':15s}: {ts['idle_entrytime']} nsecs\n"
96            text += f"  .{'idle_waketime':15s}: {ts['idle_waketime']} nsecs\n"
97            text += f"  .{'last_jiffies':15s}: {ts['last_jiffies']}\n"
98            text += f"  .{'next_timer':15s}: {ts['next_timer']}\n"
99            text += f"  .{'idle_expires':15s}: {ts['idle_expires']} nsecs\n"
100            text += "\njiffies: {}\n".format(jiffies)
101
102        text += "\n"
103
104    return text
105
106
107def print_tickdevice(td, cpu):
108    dev = td['evtdev']
109    text = "Tick Device: mode:     {}\n".format(td['mode'])
110    if cpu < 0:
111            text += "Broadcast device\n"
112    else:
113            text += "Per CPU device: {}\n".format(cpu)
114
115    text += "Clock Event Device: "
116    if dev == 0:
117            text += "<NULL>\n"
118            return text
119
120    text += "{}\n".format(dev['name'])
121    text += " max_delta_ns:   {}\n".format(dev['max_delta_ns'])
122    text += " min_delta_ns:   {}\n".format(dev['min_delta_ns'])
123    text += " mult:           {}\n".format(dev['mult'])
124    text += " shift:          {}\n".format(dev['shift'])
125    text += " mode:           {}\n".format(dev['state_use_accessors'])
126    text += " next_event:     {} nsecs\n".format(dev['next_event'])
127
128    text += " set_next_event: {}\n".format(dev['set_next_event'])
129
130    members = [('set_state_shutdown', " shutdown: {}\n"),
131               ('set_state_periodic', " periodic: {}\n"),
132               ('set_state_oneshot', " oneshot:  {}\n"),
133               ('set_state_oneshot_stopped', " oneshot stopped: {}\n"),
134               ('tick_resume', " resume:   {}\n")]
135    for member, fmt in members:
136        if dev[member]:
137            text += fmt.format(dev[member])
138
139    text += " event_handler:  {}\n".format(dev['event_handler'])
140    text += " retries:        {}\n".format(dev['retries'])
141
142    return text
143
144
145def pr_cpumask(mask):
146    nr_cpu_ids = 1
147    if constants.LX_NR_CPUS > 1:
148        nr_cpu_ids = gdb.parse_and_eval("nr_cpu_ids")
149
150    inf = gdb.inferiors()[0]
151    bits = mask['bits']
152    num_bytes = (nr_cpu_ids + 7) / 8
153    buf = utils.read_memoryview(inf, bits, num_bytes).tobytes()
154    buf = binascii.b2a_hex(buf)
155    if type(buf) is not str:
156        buf=buf.decode()
157
158    chunks = []
159    i = num_bytes
160    while i > 0:
161        i -= 1
162        start = i * 2
163        end = start + 2
164        chunks.append(buf[start:end])
165        if i != 0 and i % 4 == 0:
166            chunks.append(',')
167
168    extra = nr_cpu_ids % 8
169    if 0 < extra <= 4:
170        chunks[0] = chunks[0][0]  # Cut off the first 0
171
172    return "".join(str(chunks))
173
174
175class LxTimerList(gdb.Command):
176    """Print /proc/timer_list"""
177
178    def __init__(self):
179        super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA)
180
181    def invoke(self, arg, from_tty):
182        hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases")
183        max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES")
184
185        text = "Timer List Version: gdb scripts\n"
186        text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(
187            max_clock_bases.type.fields()[max_clock_bases].enumval)
188        text += "now at {} nsecs\n".format(ktime_get())
189
190        for cpu in cpus.each_online_cpu():
191            text += print_cpu(hrtimer_bases, cpu, max_clock_bases)
192
193        if constants.LX_CONFIG_GENERIC_CLOCKEVENTS:
194            if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:
195                bc_dev = gdb.parse_and_eval("&tick_broadcast_device")
196                text += print_tickdevice(bc_dev, -1)
197                text += "\n"
198                mask = gdb.parse_and_eval("tick_broadcast_mask")
199                mask = pr_cpumask(mask)
200                text += "tick_broadcast_mask: {}\n".format(mask)
201                if constants.LX_CONFIG_TICK_ONESHOT:
202                    mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
203                    mask = pr_cpumask(mask)
204                    text += "tick_broadcast_oneshot_mask: {}\n".format(mask)
205                text += "\n"
206
207            tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
208            for cpu in cpus.each_online_cpu():
209                tick_dev = cpus.per_cpu(tick_cpu_devices, cpu)
210                text += print_tickdevice(tick_dev, cpu)
211                text += "\n"
212
213        gdb.write(text)
214
215
216LxTimerList()
217