mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
selftests/rseq: Add rseq slice histogram script
A script that processes trace-cmd data and generates a histogram of rseq slice_ext durations for the recorded workload. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20260121143208.340549136@infradead.org
This commit is contained in:
parent
5d6446f409
commit
bb332a9e5a
2 changed files with 135 additions and 0 deletions
|
|
@ -83,6 +83,9 @@ determined by debugfs:rseq/slice_ext_nsec. The default value is 5 usec; which
|
|||
is the minimum value. It can be incremented to 50 usecs, however doing so
|
||||
can/will affect the minimum scheduling latency.
|
||||
|
||||
Any proposed changes to this default will have to come with a selftest and
|
||||
rseq-slice-hist.py output that shows the new value has merrit.
|
||||
|
||||
The kernel indicates the grant by clearing rseq::slice_ctrl::request and
|
||||
setting rseq::slice_ctrl::granted to 1. If there is a reschedule of the
|
||||
thread after granting the extension, the kernel clears the granted bit to
|
||||
|
|
|
|||
132
tools/testing/selftests/rseq/rseq-slice-hist.py
Normal file
132
tools/testing/selftests/rseq/rseq-slice-hist.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
#
|
||||
# trace-cmd record -e hrtimer_start -e hrtimer_cancel -e hrtimer_expire_entry -- $cmd
|
||||
#
|
||||
|
||||
from tracecmd import *
|
||||
|
||||
def load_kallsyms(file_path='/proc/kallsyms'):
|
||||
"""
|
||||
Parses /proc/kallsyms into a dictionary.
|
||||
Returns: { address_int: symbol_name }
|
||||
"""
|
||||
kallsyms_map = {}
|
||||
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
for line in f:
|
||||
# The format is: [address] [type] [name] [module]
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
|
||||
addr = int(parts[0], 16)
|
||||
name = parts[2]
|
||||
|
||||
kallsyms_map[addr] = name
|
||||
|
||||
except PermissionError:
|
||||
print(f"Error: Permission denied reading {file_path}. Try running with sudo.")
|
||||
except FileNotFoundError:
|
||||
print(f"Error: {file_path} not found.")
|
||||
|
||||
return kallsyms_map
|
||||
|
||||
ksyms = load_kallsyms()
|
||||
|
||||
# pending[timer_ptr] = {'ts': timestamp, 'comm': comm}
|
||||
pending = {}
|
||||
|
||||
# histograms[comm][bucket] = count
|
||||
histograms = {}
|
||||
|
||||
class OnlineHarmonicMean:
|
||||
def __init__(self):
|
||||
self.n = 0 # Count of elements
|
||||
self.S = 0.0 # Cumulative sum of reciprocals
|
||||
|
||||
def update(self, x):
|
||||
if x == 0:
|
||||
raise ValueError("Harmonic mean is undefined for zero.")
|
||||
|
||||
self.n += 1
|
||||
self.S += 1.0 / x
|
||||
return self.n / self.S
|
||||
|
||||
@property
|
||||
def mean(self):
|
||||
return self.n / self.S if self.n > 0 else 0
|
||||
|
||||
ohms = {}
|
||||
|
||||
def handle_start(record):
|
||||
func_name = ksyms[record.num_field("function")]
|
||||
if "rseq_slice_expired" in func_name:
|
||||
timer_ptr = record.num_field("hrtimer")
|
||||
pending[timer_ptr] = {
|
||||
'ts': record.ts,
|
||||
'comm': record.comm
|
||||
}
|
||||
return None
|
||||
|
||||
def handle_cancel(record):
|
||||
timer_ptr = record.num_field("hrtimer")
|
||||
|
||||
if timer_ptr in pending:
|
||||
start_data = pending.pop(timer_ptr)
|
||||
duration_ns = record.ts - start_data['ts']
|
||||
duration_us = duration_ns // 1000
|
||||
|
||||
comm = start_data['comm']
|
||||
|
||||
if comm not in ohms:
|
||||
ohms[comm] = OnlineHarmonicMean()
|
||||
|
||||
ohms[comm].update(duration_ns)
|
||||
|
||||
if comm not in histograms:
|
||||
histograms[comm] = {}
|
||||
|
||||
histograms[comm][duration_us] = histograms[comm].get(duration_us, 0) + 1
|
||||
return None
|
||||
|
||||
def handle_expire(record):
|
||||
timer_ptr = record.num_field("hrtimer")
|
||||
|
||||
if timer_ptr in pending:
|
||||
start_data = pending.pop(timer_ptr)
|
||||
comm = start_data['comm']
|
||||
|
||||
if comm not in histograms:
|
||||
histograms[comm] = {}
|
||||
|
||||
# Record -1 bucket for expired (failed to cancel)
|
||||
histograms[comm][-1] = histograms[comm].get(-1, 0) + 1
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
t = Trace("trace.dat")
|
||||
for cpu in range(0, t.cpus):
|
||||
ev = t.read_event(cpu)
|
||||
while ev:
|
||||
if "hrtimer_start" in ev.name:
|
||||
handle_start(ev)
|
||||
if "hrtimer_cancel" in ev.name:
|
||||
handle_cancel(ev)
|
||||
if "hrtimer_expire_entry" in ev.name:
|
||||
handle_expire(ev)
|
||||
|
||||
ev = t.read_event(cpu)
|
||||
|
||||
print("\n" + "="*40)
|
||||
print("RSEQ SLICE HISTOGRAM (us)")
|
||||
print("="*40)
|
||||
for comm, buckets in histograms.items():
|
||||
print(f"\nTask: {comm} Mean: {ohms[comm].mean:.3f} ns")
|
||||
print(f" {'Latency (us)':<15} | {'Count'}")
|
||||
print(f" {'-'*30}")
|
||||
# Sort buckets numerically, putting -1 at the top
|
||||
for bucket in sorted(buckets.keys()):
|
||||
label = "EXPIRED" if bucket == -1 else f"{bucket} us"
|
||||
print(f" {label:<15} | {buckets[bucket]}")
|
||||
Loading…
Add table
Add a link
Reference in a new issue