150 lines
4.5 KiB
Python
Executable File
150 lines
4.5 KiB
Python
Executable File
#! /usr/bin/python3
|
|
#
|
|
# llcstat.py Summarize cache references and cache misses by PID.
|
|
# Cache reference and cache miss are corresponding events defined in
|
|
# uapi/linux/perf_event.h, it varies to different architecture.
|
|
# On x86-64, they mean LLC references and LLC misses.
|
|
#
|
|
# For Linux, uses BCC, eBPF. Embedded C.
|
|
#
|
|
# SEE ALSO: perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm
|
|
#
|
|
# REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support).
|
|
#
|
|
# Copyright (c) 2016 Facebook, Inc.
|
|
# Licensed under the Apache License, Version 2.0 (the "License")
|
|
#
|
|
# 19-Oct-2016 Teng Qin Created this.
|
|
# 20-Jun-2022 YeZhengMao Added tid info.
|
|
|
|
from __future__ import print_function
|
|
import argparse
|
|
from bcc import BPF, PerfType, PerfHWConfig
|
|
import signal
|
|
from time import sleep
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Summarize cache references and misses by PID",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
parser.add_argument(
|
|
"-c", "--sample_period", type=int, default=100,
|
|
help="Sample one in this many number of cache reference / miss events")
|
|
parser.add_argument(
|
|
"duration", nargs="?", default=10, help="Duration, in seconds, to run")
|
|
parser.add_argument(
|
|
"-t", "--tid", action="store_true",
|
|
help="Summarize cache references and misses by PID/TID"
|
|
)
|
|
parser.add_argument("--ebpf", action="store_true",
|
|
help=argparse.SUPPRESS)
|
|
args = parser.parse_args()
|
|
|
|
# load BPF program
|
|
bpf_text="""
|
|
#include <linux/ptrace.h>
|
|
#include <uapi/linux/bpf_perf_event.h>
|
|
|
|
struct key_t {
|
|
int cpu;
|
|
u32 pid;
|
|
u32 tid;
|
|
char name[TASK_COMM_LEN];
|
|
};
|
|
|
|
BPF_HASH(ref_count, struct key_t);
|
|
BPF_HASH(miss_count, struct key_t);
|
|
|
|
static inline __attribute__((always_inline)) void get_key(struct key_t* key) {
|
|
u64 pid_tgid = bpf_get_current_pid_tgid();
|
|
key->cpu = bpf_get_smp_processor_id();
|
|
key->pid = pid_tgid >> 32;
|
|
key->tid = GET_TID ? (u32)pid_tgid : key->pid;
|
|
bpf_get_current_comm(&(key->name), sizeof(key->name));
|
|
}
|
|
|
|
int on_cache_miss(struct bpf_perf_event_data *ctx) {
|
|
struct key_t key = {};
|
|
get_key(&key);
|
|
|
|
miss_count.increment(key, ctx->sample_period);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int on_cache_ref(struct bpf_perf_event_data *ctx) {
|
|
struct key_t key = {};
|
|
get_key(&key);
|
|
|
|
ref_count.increment(key, ctx->sample_period);
|
|
|
|
return 0;
|
|
}
|
|
"""
|
|
|
|
bpf_text = bpf_text.replace("GET_TID", "1" if args.tid else "0")
|
|
|
|
if args.ebpf:
|
|
print(bpf_text)
|
|
exit()
|
|
|
|
b = BPF(text=bpf_text)
|
|
try:
|
|
b.attach_perf_event(
|
|
ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_MISSES,
|
|
fn_name="on_cache_miss", sample_period=args.sample_period)
|
|
b.attach_perf_event(
|
|
ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_REFERENCES,
|
|
fn_name="on_cache_ref", sample_period=args.sample_period)
|
|
except Exception:
|
|
print("Failed to attach to a hardware event. Is this a virtual machine?")
|
|
exit()
|
|
|
|
print("Running for {} seconds or hit Ctrl-C to end.".format(args.duration))
|
|
|
|
try:
|
|
sleep(float(args.duration))
|
|
except KeyboardInterrupt:
|
|
signal.signal(signal.SIGINT, lambda signal, frame: print())
|
|
|
|
miss_count = {}
|
|
for (k, v) in b.get_table('miss_count').items():
|
|
if args.tid:
|
|
miss_count[(k.pid, k.tid, k.cpu, k.name)] = v.value
|
|
else:
|
|
miss_count[(k.pid, k.cpu, k.name)] = v.value
|
|
|
|
header_text = 'PID '
|
|
format_text = '{:<8d} '
|
|
if args.tid:
|
|
header_text += 'TID '
|
|
format_text += '{:<8d} '
|
|
|
|
header_text += 'NAME CPU REFERENCE MISS HIT%'
|
|
format_text += '{:<16s} {:<4d} {:>12d} {:>12d} {:>6.2f}%'
|
|
|
|
print(header_text)
|
|
tot_ref = 0
|
|
tot_miss = 0
|
|
for (k, v) in b.get_table('ref_count').items():
|
|
try:
|
|
if args.tid:
|
|
miss = miss_count[(k.pid, k.tid, k.cpu, k.name)]
|
|
else:
|
|
miss = miss_count[(k.pid, k.cpu, k.name)]
|
|
except KeyError:
|
|
miss = 0
|
|
tot_ref += v.value
|
|
tot_miss += miss
|
|
# This happens on some PIDs due to missed counts caused by sampling
|
|
hit = (v.value - miss) if (v.value >= miss) else 0
|
|
if args.tid:
|
|
print(format_text.format(
|
|
k.pid, k.tid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss,
|
|
(float(hit) / float(v.value)) * 100.0))
|
|
else:
|
|
print(format_text.format(
|
|
k.pid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss,
|
|
(float(hit) / float(v.value)) * 100.0))
|
|
print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format(
|
|
tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0))
|