forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
7.5 KiB
219 lines
7.5 KiB
# SPDX-License-Identifier: GPL-2.0 |
|
# |
|
# Copyright 2019 Google LLC. |
|
|
|
import binascii |
|
import gdb |
|
|
|
from linux import constants |
|
from linux import cpus |
|
from linux import rbtree |
|
from linux import utils |
|
|
|
timerqueue_node_type = utils.CachedType("struct timerqueue_node").get_type() |
|
hrtimer_type = utils.CachedType("struct hrtimer").get_type() |
|
|
|
|
|
def ktime_get(): |
|
"""Returns the current time, but not very accurately |
|
|
|
We can't read the hardware timer itself to add any nanoseconds |
|
that need to be added since we last stored the time in the |
|
timekeeper. But this is probably good enough for debug purposes.""" |
|
tk_core = gdb.parse_and_eval("&tk_core") |
|
|
|
return tk_core['timekeeper']['tkr_mono']['base'] |
|
|
|
|
|
def print_timer(rb_node, idx): |
|
timerqueue = utils.container_of(rb_node, timerqueue_node_type.pointer(), |
|
"node") |
|
timer = utils.container_of(timerqueue, hrtimer_type.pointer(), "node") |
|
|
|
function = str(timer['function']).split(" ")[1].strip("<>") |
|
softexpires = timer['_softexpires'] |
|
expires = timer['node']['expires'] |
|
now = ktime_get() |
|
|
|
text = " #{}: <{}>, {}, ".format(idx, timer, function) |
|
text += "S:{:02x}\n".format(int(timer['state'])) |
|
text += " # expires at {}-{} nsecs [in {} to {} nsecs]\n".format( |
|
softexpires, expires, softexpires - now, expires - now) |
|
return text |
|
|
|
|
|
def print_active_timers(base): |
|
curr = base['active']['next']['node'] |
|
curr = curr.address.cast(rbtree.rb_node_type.get_type().pointer()) |
|
idx = 0 |
|
while curr: |
|
yield print_timer(curr, idx) |
|
curr = rbtree.rb_next(curr) |
|
idx += 1 |
|
|
|
|
|
def print_base(base): |
|
text = " .base: {}\n".format(base.address) |
|
text += " .index: {}\n".format(base['index']) |
|
|
|
text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution) |
|
|
|
text += " .get_time: {}\n".format(base['get_time']) |
|
if constants.LX_CONFIG_HIGH_RES_TIMERS: |
|
text += " .offset: {} nsecs\n".format(base['offset']) |
|
text += "active timers:\n" |
|
text += "".join([x for x in print_active_timers(base)]) |
|
return text |
|
|
|
|
|
def print_cpu(hrtimer_bases, cpu, max_clock_bases): |
|
cpu_base = cpus.per_cpu(hrtimer_bases, cpu) |
|
jiffies = gdb.parse_and_eval("jiffies_64") |
|
tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched") |
|
ts = cpus.per_cpu(tick_sched_ptr, cpu) |
|
|
|
text = "cpu: {}\n".format(cpu) |
|
for i in xrange(max_clock_bases): |
|
text += " clock {}:\n".format(i) |
|
text += print_base(cpu_base['clock_base'][i]) |
|
|
|
if constants.LX_CONFIG_HIGH_RES_TIMERS: |
|
fmts = [(" .{} : {} nsecs", 'expires_next'), |
|
(" .{} : {}", 'hres_active'), |
|
(" .{} : {}", 'nr_events'), |
|
(" .{} : {}", 'nr_retries'), |
|
(" .{} : {}", 'nr_hangs'), |
|
(" .{} : {}", 'max_hang_time')] |
|
text += "\n".join([s.format(f, cpu_base[f]) for s, f in fmts]) |
|
text += "\n" |
|
|
|
if constants.LX_CONFIG_TICK_ONESHOT: |
|
fmts = [(" .{} : {}", 'nohz_mode'), |
|
(" .{} : {} nsecs", 'last_tick'), |
|
(" .{} : {}", 'tick_stopped'), |
|
(" .{} : {}", 'idle_jiffies'), |
|
(" .{} : {}", 'idle_calls'), |
|
(" .{} : {}", 'idle_sleeps'), |
|
(" .{} : {} nsecs", 'idle_entrytime'), |
|
(" .{} : {} nsecs", 'idle_waketime'), |
|
(" .{} : {} nsecs", 'idle_exittime'), |
|
(" .{} : {} nsecs", 'idle_sleeptime'), |
|
(" .{}: {} nsecs", 'iowait_sleeptime'), |
|
(" .{} : {}", 'last_jiffies'), |
|
(" .{} : {}", 'next_timer'), |
|
(" .{} : {} nsecs", 'idle_expires')] |
|
text += "\n".join([s.format(f, ts[f]) for s, f in fmts]) |
|
text += "\njiffies: {}\n".format(jiffies) |
|
|
|
text += "\n" |
|
|
|
return text |
|
|
|
|
|
def print_tickdevice(td, cpu): |
|
dev = td['evtdev'] |
|
text = "Tick Device: mode: {}\n".format(td['mode']) |
|
if cpu < 0: |
|
text += "Broadcast device\n" |
|
else: |
|
text += "Per CPU device: {}\n".format(cpu) |
|
|
|
text += "Clock Event Device: " |
|
if dev == 0: |
|
text += "<NULL>\n" |
|
return text |
|
|
|
text += "{}\n".format(dev['name']) |
|
text += " max_delta_ns: {}\n".format(dev['max_delta_ns']) |
|
text += " min_delta_ns: {}\n".format(dev['min_delta_ns']) |
|
text += " mult: {}\n".format(dev['mult']) |
|
text += " shift: {}\n".format(dev['shift']) |
|
text += " mode: {}\n".format(dev['state_use_accessors']) |
|
text += " next_event: {} nsecs\n".format(dev['next_event']) |
|
|
|
text += " set_next_event: {}\n".format(dev['set_next_event']) |
|
|
|
members = [('set_state_shutdown', " shutdown: {}\n"), |
|
('set_state_periodic', " periodic: {}\n"), |
|
('set_state_oneshot', " oneshot: {}\n"), |
|
('set_state_oneshot_stopped', " oneshot stopped: {}\n"), |
|
('tick_resume', " resume: {}\n")] |
|
for member, fmt in members: |
|
if dev[member]: |
|
text += fmt.format(dev[member]) |
|
|
|
text += " event_handler: {}\n".format(dev['event_handler']) |
|
text += " retries: {}\n".format(dev['retries']) |
|
|
|
return text |
|
|
|
|
|
def pr_cpumask(mask): |
|
nr_cpu_ids = 1 |
|
if constants.LX_NR_CPUS > 1: |
|
nr_cpu_ids = gdb.parse_and_eval("nr_cpu_ids") |
|
|
|
inf = gdb.inferiors()[0] |
|
bits = mask['bits'] |
|
num_bytes = (nr_cpu_ids + 7) / 8 |
|
buf = utils.read_memoryview(inf, bits, num_bytes).tobytes() |
|
buf = binascii.b2a_hex(buf) |
|
|
|
chunks = [] |
|
i = num_bytes |
|
while i > 0: |
|
i -= 1 |
|
start = i * 2 |
|
end = start + 2 |
|
chunks.append(buf[start:end]) |
|
if i != 0 and i % 4 == 0: |
|
chunks.append(',') |
|
|
|
extra = nr_cpu_ids % 8 |
|
if 0 < extra <= 4: |
|
chunks[0] = chunks[0][0] # Cut off the first 0 |
|
|
|
return "".join(chunks) |
|
|
|
|
|
class LxTimerList(gdb.Command): |
|
"""Print /proc/timer_list""" |
|
|
|
def __init__(self): |
|
super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA) |
|
|
|
def invoke(self, arg, from_tty): |
|
hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases") |
|
max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES") |
|
|
|
text = "Timer List Version: gdb scripts\n" |
|
text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(max_clock_bases) |
|
text += "now at {} nsecs\n".format(ktime_get()) |
|
|
|
for cpu in cpus.each_online_cpu(): |
|
text += print_cpu(hrtimer_bases, cpu, max_clock_bases) |
|
|
|
if constants.LX_CONFIG_GENERIC_CLOCKEVENTS: |
|
if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST: |
|
bc_dev = gdb.parse_and_eval("&tick_broadcast_device") |
|
text += print_tickdevice(bc_dev, -1) |
|
text += "\n" |
|
mask = gdb.parse_and_eval("tick_broadcast_mask") |
|
mask = pr_cpumask(mask) |
|
text += "tick_broadcast_mask: {}\n".format(mask) |
|
if constants.LX_CONFIG_TICK_ONESHOT: |
|
mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask") |
|
mask = pr_cpumask(mask) |
|
text += "tick_broadcast_oneshot_mask: {}\n".format(mask) |
|
text += "\n" |
|
|
|
tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device") |
|
for cpu in cpus.each_online_cpu(): |
|
tick_dev = cpus.per_cpu(tick_cpu_devices, cpu) |
|
text += print_tickdevice(tick_dev, cpu) |
|
text += "\n" |
|
|
|
gdb.write(text) |
|
|
|
|
|
LxTimerList()
|
|
|