Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/usr/bin/python3
2
3#
4# trace-cmd record -e hrtimer_start -e hrtimer_cancel -e hrtimer_expire_entry -- $cmd
5#
6
7from tracecmd import *
8
9def load_kallsyms(file_path='/proc/kallsyms'):
10 """
11 Parses /proc/kallsyms into a dictionary.
12 Returns: { address_int: symbol_name }
13 """
14 kallsyms_map = {}
15
16 try:
17 with open(file_path, 'r') as f:
18 for line in f:
19 # The format is: [address] [type] [name] [module]
20 parts = line.split()
21 if len(parts) < 3:
22 continue
23
24 addr = int(parts[0], 16)
25 name = parts[2]
26
27 kallsyms_map[addr] = name
28
29 except PermissionError:
30 print(f"Error: Permission denied reading {file_path}. Try running with sudo.")
31 except FileNotFoundError:
32 print(f"Error: {file_path} not found.")
33
34 return kallsyms_map
35
36ksyms = load_kallsyms()
37
38# pending[timer_ptr] = {'ts': timestamp, 'comm': comm}
39pending = {}
40
41# histograms[comm][bucket] = count
42histograms = {}
43
44class OnlineHarmonicMean:
45 def __init__(self):
46 self.n = 0 # Count of elements
47 self.S = 0.0 # Cumulative sum of reciprocals
48
49 def update(self, x):
50 if x == 0:
51 raise ValueError("Harmonic mean is undefined for zero.")
52
53 self.n += 1
54 self.S += 1.0 / x
55 return self.n / self.S
56
57 @property
58 def mean(self):
59 return self.n / self.S if self.n > 0 else 0
60
61ohms = {}
62
63def handle_start(record):
64 func_name = ksyms[record.num_field("function")]
65 if "rseq_slice_expired" in func_name:
66 timer_ptr = record.num_field("hrtimer")
67 pending[timer_ptr] = {
68 'ts': record.ts,
69 'comm': record.comm
70 }
71 return None
72
73def handle_cancel(record):
74 timer_ptr = record.num_field("hrtimer")
75
76 if timer_ptr in pending:
77 start_data = pending.pop(timer_ptr)
78 duration_ns = record.ts - start_data['ts']
79 duration_us = duration_ns // 1000
80
81 comm = start_data['comm']
82
83 if comm not in ohms:
84 ohms[comm] = OnlineHarmonicMean()
85
86 ohms[comm].update(duration_ns)
87
88 if comm not in histograms:
89 histograms[comm] = {}
90
91 histograms[comm][duration_us] = histograms[comm].get(duration_us, 0) + 1
92 return None
93
94def handle_expire(record):
95 timer_ptr = record.num_field("hrtimer")
96
97 if timer_ptr in pending:
98 start_data = pending.pop(timer_ptr)
99 comm = start_data['comm']
100
101 if comm not in histograms:
102 histograms[comm] = {}
103
104 # Record -1 bucket for expired (failed to cancel)
105 histograms[comm][-1] = histograms[comm].get(-1, 0) + 1
106 return None
107
108if __name__ == "__main__":
109 t = Trace("trace.dat")
110 for cpu in range(0, t.cpus):
111 ev = t.read_event(cpu)
112 while ev:
113 if "hrtimer_start" in ev.name:
114 handle_start(ev)
115 if "hrtimer_cancel" in ev.name:
116 handle_cancel(ev)
117 if "hrtimer_expire_entry" in ev.name:
118 handle_expire(ev)
119
120 ev = t.read_event(cpu)
121
122 print("\n" + "="*40)
123 print("RSEQ SLICE HISTOGRAM (us)")
124 print("="*40)
125 for comm, buckets in histograms.items():
126 print(f"\nTask: {comm} Mean: {ohms[comm].mean:.3f} ns")
127 print(f" {'Latency (us)':<15} | {'Count'}")
128 print(f" {'-'*30}")
129 # Sort buckets numerically, putting -1 at the top
130 for bucket in sorted(buckets.keys()):
131 label = "EXPIRED" if bucket == -1 else f"{bucket} us"
132 print(f" {label:<15} | {buckets[bucket]}")