xref: /linux/scripts/gdb/linux/timerlist.py (revision c5448d46b3995c0b477f6bb04f313af3d57665c4)
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    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_jiffies':15s}: {ts['idle_jiffies']}\n"
94            text += f"  .{'idle_calls':15s}: {ts['idle_calls']}\n"
95            text += f"  .{'idle_sleeps':15s}: {ts['idle_sleeps']}\n"
96            text += f"  .{'idle_entrytime':15s}: {ts['idle_entrytime']} nsecs\n"
97            text += f"  .{'idle_waketime':15s}: {ts['idle_waketime']} nsecs\n"
98            text += f"  .{'idle_exittime':15s}: {ts['idle_exittime']} nsecs\n"
99            text += f"  .{'idle_sleeptime':15s}: {ts['idle_sleeptime']} nsecs\n"
100            text += f"  .{'iowait_sleeptime':15s}: {ts['iowait_sleeptime']} nsecs\n"
101            text += f"  .{'last_jiffies':15s}: {ts['last_jiffies']}\n"
102            text += f"  .{'next_timer':15s}: {ts['next_timer']}\n"
103            text += f"  .{'idle_expires':15s}: {ts['idle_expires']} nsecs\n"
104            text += "\njiffies: {}\n".format(jiffies)
105
106        text += "\n"
107
108    return text
109
110
111def print_tickdevice(td, cpu):
112    dev = td['evtdev']
113    text = "Tick Device: mode:     {}\n".format(td['mode'])
114    if cpu < 0:
115            text += "Broadcast device\n"
116    else:
117            text += "Per CPU device: {}\n".format(cpu)
118
119    text += "Clock Event Device: "
120    if dev == 0:
121            text += "<NULL>\n"
122            return text
123
124    text += "{}\n".format(dev['name'])
125    text += " max_delta_ns:   {}\n".format(dev['max_delta_ns'])
126    text += " min_delta_ns:   {}\n".format(dev['min_delta_ns'])
127    text += " mult:           {}\n".format(dev['mult'])
128    text += " shift:          {}\n".format(dev['shift'])
129    text += " mode:           {}\n".format(dev['state_use_accessors'])
130    text += " next_event:     {} nsecs\n".format(dev['next_event'])
131
132    text += " set_next_event: {}\n".format(dev['set_next_event'])
133
134    members = [('set_state_shutdown', " shutdown: {}\n"),
135               ('set_state_periodic', " periodic: {}\n"),
136               ('set_state_oneshot', " oneshot:  {}\n"),
137               ('set_state_oneshot_stopped', " oneshot stopped: {}\n"),
138               ('tick_resume', " resume:   {}\n")]
139    for member, fmt in members:
140        if dev[member]:
141            text += fmt.format(dev[member])
142
143    text += " event_handler:  {}\n".format(dev['event_handler'])
144    text += " retries:        {}\n".format(dev['retries'])
145
146    return text
147
148
149def pr_cpumask(mask):
150    nr_cpu_ids = 1
151    if constants.LX_NR_CPUS > 1:
152        nr_cpu_ids = gdb.parse_and_eval("nr_cpu_ids")
153
154    inf = gdb.inferiors()[0]
155    bits = mask['bits']
156    num_bytes = (nr_cpu_ids + 7) / 8
157    buf = utils.read_memoryview(inf, bits, num_bytes).tobytes()
158    buf = binascii.b2a_hex(buf)
159    if type(buf) is not str:
160        buf=buf.decode()
161
162    chunks = []
163    i = num_bytes
164    while i > 0:
165        i -= 1
166        start = i * 2
167        end = start + 2
168        chunks.append(buf[start:end])
169        if i != 0 and i % 4 == 0:
170            chunks.append(',')
171
172    extra = nr_cpu_ids % 8
173    if 0 < extra <= 4:
174        chunks[0] = chunks[0][0]  # Cut off the first 0
175
176    return "".join(str(chunks))
177
178
179class LxTimerList(gdb.Command):
180    """Print /proc/timer_list"""
181
182    def __init__(self):
183        super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA)
184
185    def invoke(self, arg, from_tty):
186        hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases")
187        max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES")
188
189        text = "Timer List Version: gdb scripts\n"
190        text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(
191            max_clock_bases.type.fields()[max_clock_bases].enumval)
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