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(str(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(
192            max_clock_bases.type.fields()[max_clock_bases].enumval)
193        text += "now at {} nsecs\n".format(ktime_get())
194
195        for cpu in cpus.each_online_cpu():
196            text += print_cpu(hrtimer_bases, cpu, max_clock_bases)
197
198        if constants.LX_CONFIG_GENERIC_CLOCKEVENTS:
199            if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:
200                bc_dev = gdb.parse_and_eval("&tick_broadcast_device")
201                text += print_tickdevice(bc_dev, -1)
202                text += "\n"
203                mask = gdb.parse_and_eval("tick_broadcast_mask")
204                mask = pr_cpumask(mask)
205                text += "tick_broadcast_mask: {}\n".format(mask)
206                if constants.LX_CONFIG_TICK_ONESHOT:
207                    mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
208                    mask = pr_cpumask(mask)
209                    text += "tick_broadcast_oneshot_mask: {}\n".format(mask)
210                text += "\n"
211
212            tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
213            for cpu in cpus.each_online_cpu():
214                tick_dev = cpus.per_cpu(tick_cpu_devices, cpu)
215                text += print_tickdevice(tick_dev, cpu)
216                text += "\n"
217
218        gdb.write(text)
219
220
221LxTimerList()
222