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']['next']['node']
47    curr = curr.address.cast(rbtree.rb_node_type.get_type().pointer())
48    idx = 0
49    while curr:
50        yield print_timer(curr, idx)
51        curr = rbtree.rb_next(curr)
52        idx += 1
53
54
55def print_base(base):
56    text = " .base:       {}\n".format(base.address)
57    text += " .index:      {}\n".format(base['index'])
58
59    text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution)
60
61    text += " .get_time:   {}\n".format(base['get_time'])
62    if constants.LX_CONFIG_HIGH_RES_TIMERS:
63        text += "  .offset:     {} nsecs\n".format(base['offset'])
64    text += "active timers:\n"
65    text += "".join([x for x in print_active_timers(base)])
66    return text
67
68
69def print_cpu(hrtimer_bases, cpu, max_clock_bases):
70    cpu_base = cpus.per_cpu(hrtimer_bases, cpu)
71    jiffies = gdb.parse_and_eval("jiffies_64")
72    tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched")
73    ts = cpus.per_cpu(tick_sched_ptr, cpu)
74
75    text = "cpu: {}\n".format(cpu)
76    for i in xrange(max_clock_bases):
77        text += " clock {}:\n".format(i)
78        text += print_base(cpu_base['clock_base'][i])
79
80        if constants.LX_CONFIG_HIGH_RES_TIMERS:
81            fmts = [("  .{}   : {} nsecs", 'expires_next'),
82                    ("  .{}    : {}", 'hres_active'),
83                    ("  .{}      : {}", 'nr_events'),
84                    ("  .{}     : {}", 'nr_retries'),
85                    ("  .{}       : {}", 'nr_hangs'),
86                    ("  .{}  : {}", 'max_hang_time')]
87            text += "\n".join([s.format(f, cpu_base[f]) for s, f in fmts])
88            text += "\n"
89
90        if constants.LX_CONFIG_TICK_ONESHOT:
91            fmts = [("  .{}      : {}", 'nohz_mode'),
92                    ("  .{}      : {} nsecs", 'last_tick'),
93                    ("  .{}   : {}", 'tick_stopped'),
94                    ("  .{}   : {}", 'idle_jiffies'),
95                    ("  .{}     : {}", 'idle_calls'),
96                    ("  .{}    : {}", 'idle_sleeps'),
97                    ("  .{} : {} nsecs", 'idle_entrytime'),
98                    ("  .{}  : {} nsecs", 'idle_waketime'),
99                    ("  .{}  : {} nsecs", 'idle_exittime'),
100                    ("  .{} : {} nsecs", 'idle_sleeptime'),
101                    ("  .{}: {} nsecs", 'iowait_sleeptime'),
102                    ("  .{}   : {}", 'last_jiffies'),
103                    ("  .{}     : {}", 'next_timer'),
104                    ("  .{}   : {} nsecs", 'idle_expires')]
105            text += "\n".join([s.format(f, ts[f]) for s, f in fmts])
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
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(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(max_clock_bases)
191        text += "now at {} nsecs\n".format(ktime_get())
192
193        for cpu in cpus.each_online_cpu():
194            text += print_cpu(hrtimer_bases, cpu, max_clock_bases)
195
196        if constants.LX_CONFIG_GENERIC_CLOCKEVENTS:
197            if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:
198                bc_dev = gdb.parse_and_eval("&tick_broadcast_device")
199                text += print_tickdevice(bc_dev, -1)
200                text += "\n"
201                mask = gdb.parse_and_eval("tick_broadcast_mask")
202                mask = pr_cpumask(mask)
203                text += "tick_broadcast_mask: {}\n".format(mask)
204                if constants.LX_CONFIG_TICK_ONESHOT:
205                    mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
206                    mask = pr_cpumask(mask)
207                    text += "tick_broadcast_oneshot_mask: {}\n".format(mask)
208                text += "\n"
209
210            tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
211            for cpu in cpus.each_online_cpu():
212                tick_dev = cpus.per_cpu(tick_cpu_devices, cpu)
213                text += print_tickdevice(tick_dev, cpu)
214                text += "\n"
215
216        gdb.write(text)
217
218
219LxTimerList()
220