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