| # GDB debugging support | |
| # | |
| # Copyright 2017 Linaro Ltd | |
| # | |
| # Author: Alex Bennée <alex.bennee@linaro.org> | |
| # | |
| # This work is licensed under the terms of the GNU GPL, version 2 or later. | |
| # See the COPYING file in the top-level directory. | |
| # | |
| # SPDX-License-Identifier: GPL-2.0-or-later | |
| # 'qemu timers' -- display the current timerlists | |
| import gdb | |
| class TimersCommand(gdb.Command): | |
| '''Display the current QEMU timers''' | |
| def __init__(self): | |
| 'Register the class as a gdb command' | |
| gdb.Command.__init__(self, 'qemu timers', gdb.COMMAND_DATA, | |
| gdb.COMPLETE_NONE) | |
| def _format_expire_time(self, expire_time, scale): | |
| "Return human-readable expiry time (ns) with scale info." | |
| secs = expire_time / 1e9 | |
| # Select unit and compute value | |
| if secs < 1: | |
| val, unit = secs * 1000, "ms" | |
| elif secs < 60: | |
| val, unit = secs, "s" | |
| elif secs < 3600: | |
| val, unit = secs / 60, "min" | |
| elif secs < 86400: | |
| val, unit = secs / 3600, "hrs" | |
| else: | |
| val, unit = secs / 86400, "days" | |
| scale_map = {1: "ns", 1000: "us", 1000000: "ms", | |
| 1000000000: "s"} | |
| scale_str = scale_map.get(scale, f"scale={scale}") | |
| return f"{val:.2f} {unit} [{scale_str}]" | |
| def _format_attribute(self, attr): | |
| "Given QEMUTimer attributes value, return a human-readable string" | |
| # From include/qemu/timer.h | |
| if attr == 0: | |
| value = 'NONE' | |
| elif attr == 1 << 0: | |
| value = 'ATTR_EXTERNAL' | |
| elif attr == int(0xffffffff): | |
| value = 'ATTR_ALL' | |
| else: | |
| value = 'UNKNOWN' | |
| return f'{attr} <{value}>' | |
| def dump_timers(self, timer): | |
| "Follow a timer and recursively dump each one in the list." | |
| # timer should be of type QemuTimer | |
| scale = int(timer['scale']) | |
| expire_time = int(timer['expire_time']) | |
| attributes = int(timer['attributes']) | |
| time_str = self._format_expire_time(expire_time, scale) | |
| attr_str = self._format_attribute(attributes) | |
| gdb.write(f" timer at {time_str} (attr:{attr_str}, " | |
| f"cb:{timer['cb']}, opq:{timer['opaque']})\n") | |
| if int(timer['next']) > 0: | |
| self.dump_timers(timer['next']) | |
| def process_timerlist(self, tlist, ttype): | |
| gdb.write("Processing %s timers\n" % (ttype)) | |
| gdb.write(" clock %s is enabled:%s\n" % ( | |
| tlist['clock']['type'], | |
| tlist['clock']['enabled'])) | |
| if int(tlist['active_timers']) > 0: | |
| self.dump_timers(tlist['active_timers']) | |
| def invoke(self, arg, from_tty): | |
| 'Run the command' | |
| main_timers = gdb.parse_and_eval("main_loop_tlg") | |
| # This will break if QEMUClockType in timer.h is redfined | |
| self.process_timerlist(main_timers['tl'][0], "Realtime") | |
| self.process_timerlist(main_timers['tl'][1], "Virtual") | |
| self.process_timerlist(main_timers['tl'][2], "Host") | |
| self.process_timerlist(main_timers['tl'][3], "Virtual RT") |