• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/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
19from __future__ import print_function
20import argparse
21from bcc import BPF, PerfType, PerfHWConfig
22import signal
23from time import sleep
24
25parser = argparse.ArgumentParser(
26    description="Summarize cache references and misses by PID",
27    formatter_class=argparse.RawDescriptionHelpFormatter)
28parser.add_argument(
29    "-c", "--sample_period", type=int, default=100,
30    help="Sample one in this many number of cache reference / miss events")
31parser.add_argument(
32    "duration", nargs="?", default=10, help="Duration, in seconds, to run")
33parser.add_argument("--ebpf", action="store_true",
34    help=argparse.SUPPRESS)
35args = parser.parse_args()
36
37# load BPF program
38bpf_text="""
39#include <linux/ptrace.h>
40#include <uapi/linux/bpf_perf_event.h>
41
42struct key_t {
43    int cpu;
44    int pid;
45    char name[TASK_COMM_LEN];
46};
47
48BPF_HASH(ref_count, struct key_t);
49BPF_HASH(miss_count, struct key_t);
50
51static inline __attribute__((always_inline)) void get_key(struct key_t* key) {
52    key->cpu = bpf_get_smp_processor_id();
53    key->pid = bpf_get_current_pid_tgid();
54    bpf_get_current_comm(&(key->name), sizeof(key->name));
55}
56
57int on_cache_miss(struct bpf_perf_event_data *ctx) {
58    struct key_t key = {};
59    get_key(&key);
60
61    miss_count.increment(key, ctx->sample_period);
62
63    return 0;
64}
65
66int on_cache_ref(struct bpf_perf_event_data *ctx) {
67    struct key_t key = {};
68    get_key(&key);
69
70    ref_count.increment(key, ctx->sample_period);
71
72    return 0;
73}
74"""
75
76if args.ebpf:
77    print(bpf_text)
78    exit()
79
80b = BPF(text=bpf_text)
81try:
82    b.attach_perf_event(
83        ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_MISSES,
84        fn_name="on_cache_miss", sample_period=args.sample_period)
85    b.attach_perf_event(
86        ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_REFERENCES,
87        fn_name="on_cache_ref", sample_period=args.sample_period)
88except:
89    print("Failed to attach to a hardware event. Is this a virtual machine?")
90    exit()
91
92print("Running for {} seconds or hit Ctrl-C to end.".format(args.duration))
93
94try:
95    sleep(float(args.duration))
96except KeyboardInterrupt:
97    signal.signal(signal.SIGINT, lambda signal, frame: print())
98
99miss_count = {}
100for (k, v) in b.get_table('miss_count').items():
101    miss_count[(k.pid, k.cpu, k.name)] = v.value
102
103print('PID      NAME             CPU     REFERENCE         MISS    HIT%')
104tot_ref = 0
105tot_miss = 0
106for (k, v) in b.get_table('ref_count').items():
107    try:
108        miss = miss_count[(k.pid, k.cpu, k.name)]
109    except KeyError:
110        miss = 0
111    tot_ref += v.value
112    tot_miss += miss
113    # This happens on some PIDs due to missed counts caused by sampling
114    hit = (v.value - miss) if (v.value >= miss) else 0
115    print('{:<8d} {:<16s} {:<4d} {:>12d} {:>12d} {:>6.2f}%'.format(
116        k.pid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss,
117        (float(hit) / float(v.value)) * 100.0))
118print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format(
119    tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0))
120