| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- #!/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]}")
|