1#!/usr/bin/env python 2# 3# llcstat.py Summarize cache references and cache misses by PID. 4# Cache reference and cache miss are corresponding events defined in 5# uapi/linux/perf_event.h, it varies to different architecture. 6# On x86-64, they mean LLC references and LLC misses. 7# 8# For Linux, uses BCC, eBPF. Embedded C. 9# 10# SEE ALSO: perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm 11# 12# REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). 13# 14# Copyright (c) 2016 Facebook, Inc. 15# Licensed under the Apache License, Version 2.0 (the "License") 16# 17# 19-Oct-2016 Teng Qin Created this. 18# 20-Jun-2022 YeZhengMao Added tid info. 19 20from __future__ import print_function 21import argparse 22from bcc import BPF, PerfType, PerfHWConfig 23import signal 24from time import sleep 25 26parser = argparse.ArgumentParser( 27 description="Summarize cache references and misses by PID", 28 formatter_class=argparse.RawDescriptionHelpFormatter) 29parser.add_argument( 30 "-c", "--sample_period", type=int, default=100, 31 help="Sample one in this many number of cache reference / miss events") 32parser.add_argument( 33 "duration", nargs="?", default=10, help="Duration, in seconds, to run") 34parser.add_argument( 35 "-t", "--tid", action="store_true", 36 help="Summarize cache references and misses by PID/TID" 37) 38parser.add_argument("--ebpf", action="store_true", 39 help=argparse.SUPPRESS) 40args = parser.parse_args() 41 42# load BPF program 43bpf_text=""" 44#include <linux/ptrace.h> 45#include <uapi/linux/bpf_perf_event.h> 46 47struct key_t { 48 int cpu; 49 u32 pid; 50 u32 tid; 51 char name[TASK_COMM_LEN]; 52}; 53 54BPF_HASH(ref_count, struct key_t); 55BPF_HASH(miss_count, struct key_t); 56 57static inline __attribute__((always_inline)) void get_key(struct key_t* key) { 58 u64 pid_tgid = bpf_get_current_pid_tgid(); 59 key->cpu = bpf_get_smp_processor_id(); 60 key->pid = pid_tgid >> 32; 61 key->tid = GET_TID ? (u32)pid_tgid : key->pid; 62 bpf_get_current_comm(&(key->name), sizeof(key->name)); 63} 64 65int on_cache_miss(struct bpf_perf_event_data *ctx) { 66 struct key_t key = {}; 67 get_key(&key); 68 69 miss_count.increment(key, ctx->sample_period); 70 71 return 0; 72} 73 74int on_cache_ref(struct bpf_perf_event_data *ctx) { 75 struct key_t key = {}; 76 get_key(&key); 77 78 ref_count.increment(key, ctx->sample_period); 79 80 return 0; 81} 82""" 83 84bpf_text = bpf_text.replace("GET_TID", "1" if args.tid else "0") 85 86if args.ebpf: 87 print(bpf_text) 88 exit() 89 90b = BPF(text=bpf_text) 91try: 92 b.attach_perf_event( 93 ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_MISSES, 94 fn_name="on_cache_miss", sample_period=args.sample_period) 95 b.attach_perf_event( 96 ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_REFERENCES, 97 fn_name="on_cache_ref", sample_period=args.sample_period) 98except Exception: 99 print("Failed to attach to a hardware event. Is this a virtual machine?") 100 exit() 101 102print("Running for {} seconds or hit Ctrl-C to end.".format(args.duration)) 103 104try: 105 sleep(float(args.duration)) 106except KeyboardInterrupt: 107 signal.signal(signal.SIGINT, lambda signal, frame: print()) 108 109miss_count = {} 110for (k, v) in b.get_table('miss_count').items(): 111 if args.tid: 112 miss_count[(k.pid, k.tid, k.cpu, k.name)] = v.value 113 else: 114 miss_count[(k.pid, k.cpu, k.name)] = v.value 115 116header_text = 'PID ' 117format_text = '{:<8d} ' 118if args.tid: 119 header_text += 'TID ' 120 format_text += '{:<8d} ' 121 122header_text += 'NAME CPU REFERENCE MISS HIT%' 123format_text += '{:<16s} {:<4d} {:>12d} {:>12d} {:>6.2f}%' 124 125print(header_text) 126tot_ref = 0 127tot_miss = 0 128for (k, v) in b.get_table('ref_count').items(): 129 try: 130 if args.tid: 131 miss = miss_count[(k.pid, k.tid, k.cpu, k.name)] 132 else: 133 miss = miss_count[(k.pid, k.cpu, k.name)] 134 except KeyError: 135 miss = 0 136 tot_ref += v.value 137 tot_miss += miss 138 # This happens on some PIDs due to missed counts caused by sampling 139 hit = (v.value - miss) if (v.value >= miss) else 0 140 if args.tid: 141 print(format_text.format( 142 k.pid, k.tid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss, 143 (float(hit) / float(v.value)) * 100.0)) 144 else: 145 print(format_text.format( 146 k.pid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss, 147 (float(hit) / float(v.value)) * 100.0)) 148print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format( 149 tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0)) 150