from dataclasses import dataclass
from typing import Optional
import gdb
from . import utils
CONFIG_SYSTEM_CRITMONITOR = utils.lookup_type("struct critmon_state_s") is not None
@dataclass
class CriticalData:
"""Data class to hold critical data information"""
pid: Optional[int] = None
name: Optional[str] = None
premp_max: Optional[int] = None
premp_max_caller: Optional[int] = None
crit_max: Optional[int] = None
crit_max_caller: Optional[int] = None
busywait_start: Optional[int] = None
busywait_caller: Optional[int] = None
busywait_max: Optional[int] = None
busywait_max_caller: Optional[int] = None
busywait_total: Optional[int] = None
run_max: Optional[int] = None
run_time: Optional[int] = None
def __str__(self):
"""Format the information for output"""
def fmt(field, hex: bool = False):
val = getattr(self, field)
if val is None:
return "-"
if hex and isinstance(val, int):
return hex(val)
return str(val)
formatter = (
"{:<3} {:<18} "
"{:<10} {:<9} "
"{:<11} {:<9} "
"{:<13} {:<6} "
"{:<11} {:<9} "
"{:<11} "
"{:<12} {}"
)
return formatter.format(
fmt("pid"),
fmt("name"),
fmt("premp_max"),
fmt("premp_max_caller", hex=True),
fmt("crit_max"),
fmt("crit_max_caller", hex=True),
fmt("busywait_start"),
fmt("busywait_caller", hex=True),
fmt("busywait_max"),
fmt("busywait_max_caller", hex=True),
fmt("busywait_total"),
fmt("run_max"),
fmt("run_time"),
)
class Critmon(gdb.Command):
"""Dump critical resource busy-wait time"""
def __init__(self):
if not CONFIG_SYSTEM_CRITMONITOR:
print("Critmon is not enabled in the current configuration")
return
super().__init__("critmon", gdb.COMMAND_USER)
def process_task_critical_data(self, tcb):
"""Generate information for a single task's critical section"""
try:
data = CriticalData()
for name in utils.get_fieldnames("struct tcb_s"):
if hasattr(CriticalData, name):
value = getattr(tcb, name)
if name == "name":
value = value.cast(gdb.lookup_type("char").pointer()).string()
setattr(data, name, value)
return data
except Exception as e:
print(f"Error processing task critical section information: {str(e)}")
return None
def process_global_critical_data(self):
"""Generate information for global critical sections"""
try:
output = []
for cpu, data in enumerate(
[CriticalData() for _ in range(utils.get_ncpus())]
):
setattr(data, "name", f"CPU{cpu}")
for key in ("premp_max", "crit_max", "busywait_max", "busywait_total"):
value = utils.gdb_eval_or_none(f"g_{key}")
if not value:
continue
setattr(data, key, int(value[cpu]))
output.append(data)
return output
except Exception as e:
print(f"Error processing global critical section information: {str(e)}")
return []
@utils.dont_repeat_decorator
def invoke(self, args, from_tty):
print(
"PID NAME "
"Preemption MaxCaller "
"CritSection MaxCaller "
"BusywaitStart Caller "
"BusywaitMax MaxCaller "
"BusywaitAll "
"ThreadRunMax ThreadRunAll"
)
try:
for data in self.process_global_critical_data():
print(data)
for tcb in utils.get_tcbs():
if not (data := self.process_task_critical_data(tcb)):
continue
print(data)
except Exception as e:
print(f"Error invoking critmon command: {str(e)}")
def diagnose(self, *args, **kwargs):
return {
"title": "Critmon Report",
"summary": "Critical resource busy-wait time dump",
"result": "info",
"command": "critmon",
"category": utils.DiagnoseCategory.sched,
"data": gdb.execute("critmon", to_string=True),
}