Alexey Ivanov | 777e802 | 2019-01-03 13:46:38 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 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 | # |
Ivan Babrou | 41d462b | 2018-07-08 13:09:35 -0700 | [diff] [blame] | 10 | # SEE ALSO: perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm |
| 11 | # |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 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 | |
| 19 | from __future__ import print_function |
| 20 | import argparse |
| 21 | from bcc import BPF, PerfType, PerfHWConfig |
| 22 | import signal |
| 23 | from time import sleep |
| 24 | |
| 25 | parser = argparse.ArgumentParser( |
| 26 | description="Summarize cache references and misses by PID", |
| 27 | formatter_class=argparse.RawDescriptionHelpFormatter) |
| 28 | parser.add_argument( |
| 29 | "-c", "--sample_period", type=int, default=100, |
| 30 | help="Sample one in this many number of cache reference / miss events") |
| 31 | parser.add_argument( |
| 32 | "duration", nargs="?", default=10, help="Duration, in seconds, to run") |
Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 33 | parser.add_argument("--ebpf", action="store_true", |
| 34 | help=argparse.SUPPRESS) |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 35 | args = parser.parse_args() |
| 36 | |
| 37 | # load BPF program |
Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 38 | bpf_text=""" |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 39 | #include <linux/ptrace.h> |
| 40 | #include <uapi/linux/bpf_perf_event.h> |
| 41 | |
| 42 | struct key_t { |
| 43 | int cpu; |
| 44 | int pid; |
| 45 | char name[TASK_COMM_LEN]; |
| 46 | }; |
| 47 | |
| 48 | BPF_HASH(ref_count, struct key_t); |
| 49 | BPF_HASH(miss_count, struct key_t); |
| 50 | |
| 51 | static 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 | |
| 57 | int on_cache_miss(struct bpf_perf_event_data *ctx) { |
| 58 | struct key_t key = {}; |
| 59 | get_key(&key); |
| 60 | |
Javier Honduvilla Coto | 64bf965 | 2018-08-01 06:50:19 +0200 | [diff] [blame] | 61 | miss_count.increment(key, ctx->sample_period); |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 62 | |
| 63 | return 0; |
| 64 | } |
| 65 | |
| 66 | int on_cache_ref(struct bpf_perf_event_data *ctx) { |
| 67 | struct key_t key = {}; |
| 68 | get_key(&key); |
| 69 | |
Javier Honduvilla Coto | 64bf965 | 2018-08-01 06:50:19 +0200 | [diff] [blame] | 70 | ref_count.increment(key, ctx->sample_period); |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 71 | |
| 72 | return 0; |
| 73 | } |
Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 74 | """ |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 75 | |
Nathan Scott | cf0792f | 2018-02-02 16:56:50 +1100 | [diff] [blame] | 76 | if args.ebpf: |
| 77 | print(bpf_text) |
| 78 | exit() |
| 79 | |
| 80 | b = BPF(text=bpf_text) |
Jerome Marchand | d274b66 | 2018-08-07 17:24:31 +0200 | [diff] [blame] | 81 | try: |
| 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) |
| 88 | except: |
| 89 | print("Failed to attach to a hardware event. Is this a virtual machine?") |
| 90 | exit() |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 91 | |
| 92 | print("Running for {} seconds or hit Ctrl-C to end.".format(args.duration)) |
| 93 | |
| 94 | try: |
| 95 | sleep(float(args.duration)) |
| 96 | except KeyboardInterrupt: |
| 97 | signal.signal(signal.SIGINT, lambda signal, frame: print()) |
| 98 | |
| 99 | miss_count = {} |
| 100 | for (k, v) in b.get_table('miss_count').items(): |
| 101 | miss_count[(k.pid, k.cpu, k.name)] = v.value |
| 102 | |
| 103 | print('PID NAME CPU REFERENCE MISS HIT%') |
| 104 | tot_ref = 0 |
| 105 | tot_miss = 0 |
| 106 | for (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( |
jeromemarchand | b96ebcd | 2018-10-10 01:58:15 +0200 | [diff] [blame] | 116 | k.pid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss, |
Teng Qin | e960eb7 | 2016-10-18 17:22:27 -0700 | [diff] [blame] | 117 | (float(hit) / float(v.value)) * 100.0)) |
| 118 | print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format( |
| 119 | tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0)) |