xref: /aosp_15_r20/external/bcc/tools/llcstat.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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