|
5 | 5 | # uapi/linux/perf_event.h, it varies to different architecture.
|
6 | 6 | # On x86-64, they mean LLC references and LLC misses. Postgres
|
7 | 7 | # backend provides a query string. Based on llstat.py from bcc.
|
8 |
| -# |
9 | 8 | # For Linux, uses BCC, eBPF.
|
10 | 9 | #
|
11 | 10 | # SEE ALSO: perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm
|
12 | 11 | #
|
13 | 12 | # REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support).
|
14 |
| -# |
15 |
| -# Copyright (c) 2016 Facebook, Inc. |
16 |
| -# Licensed under the Apache License, Version 2.0 (the "License") |
17 |
| -# |
18 |
| -# 19-Oct-2016 Teng Qin Created this. |
19 | 13 |
|
20 | 14 | from __future__ import print_function
|
21 | 15 | import argparse
|
22 | 16 | from bcc import BPF, PerfType, PerfHWConfig
|
23 | 17 | import signal
|
24 | 18 | from time import sleep
|
25 | 19 |
|
26 |
| -parser = argparse.ArgumentParser( |
27 |
| - description="Summarize cache references and misses by postgres backend", |
28 |
| - formatter_class=argparse.RawDescriptionHelpFormatter) |
29 |
| -parser.add_argument( |
30 |
| - "-c", "--sample_period", type=int, default=100, |
31 |
| - help="Sample one in this many number of cache reference / miss events") |
32 |
| -parser.add_argument( |
33 |
| - "-p", "--postgres_path", type=str, |
34 |
| - help="Path to the postgres binary") |
35 |
| -parser.add_argument( |
36 |
| - "duration", nargs="?", default=10, help="Duration, in seconds, to run") |
37 |
| -parser.add_argument("--ebpf", action="store_true", |
38 |
| - help=argparse.SUPPRESS) |
39 |
| -args = parser.parse_args() |
40 | 20 |
|
41 | 21 | # load BPF program
|
42 | 22 | bpf_text="""
|
|
121 | 101 | }
|
122 | 102 | """
|
123 | 103 |
|
124 |
| -if args.ebpf: |
125 |
| - print(bpf_text) |
126 |
| - exit() |
127 |
| - |
128 |
| -b = BPF(text=bpf_text) |
129 |
| -b.attach_uprobe( |
130 |
| - name=args.postgres_path, |
131 |
| - sym="exec_simple_query", |
132 |
| - fn_name="probe_exec_simple_query") |
133 |
| -b.attach_uretprobe( |
134 |
| - name=args.postgres_path, |
135 |
| - sym="exec_simple_query", |
136 |
| - fn_name="probe_exec_simple_query_finish") |
137 |
| -b.attach_perf_event( |
138 |
| - ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_MISSES, |
139 |
| - fn_name="on_cache_miss", sample_period=args.sample_period) |
140 |
| -b.attach_perf_event( |
141 |
| - ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_REFERENCES, |
142 |
| - fn_name="on_cache_ref", sample_period=args.sample_period) |
143 |
| - |
144 |
| -print("Running for {} seconds or hit Ctrl-C to end.".format(args.duration)) |
145 |
| - |
146 |
| -try: |
147 |
| - sleep(float(args.duration)) |
148 |
| -except KeyboardInterrupt: |
149 |
| - signal.signal(signal.SIGINT, lambda signal, frame: print()) |
150 |
| - |
151 |
| -miss_count = {} |
152 |
| -for (k, v) in b.get_table('miss_count').items(): |
153 |
| - miss_count[(k.pid, k.cpu, k.name)] = v.value |
154 |
| - |
155 |
| -print('PID NAME QUERY CPU REFERENCE MISS HIT%') |
156 |
| -tot_ref = 0 |
157 |
| -tot_miss = 0 |
158 |
| -for (k, v) in b.get_table('ref_count').items(): |
159 |
| - try: |
160 |
| - miss = miss_count[(k.pid, k.cpu, k.name)] |
161 |
| - except KeyError: |
162 |
| - miss = 0 |
163 |
| - tot_ref += v.value |
164 |
| - tot_miss += miss |
165 |
| - # This happens on some PIDs due to missed counts caused by sampling |
166 |
| - hit = (v.value - miss) if (v.value >= miss) else 0 |
167 |
| - print('{:<8d} {:<16s} {:<100s} {:<4d} {:>12d} {:>12d} {:>6.2f}%'.format( |
168 |
| - k.pid, k.name.decode(), k.query.decode(), k.cpu, v.value, miss, |
169 |
| - (float(hit) / float(v.value)) * 100.0)) |
170 |
| -if tot_ref != 0: |
171 |
| - print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format( |
172 |
| - tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0)) |
| 104 | + |
| 105 | +# signal handler |
| 106 | +def signal_ignore(signal, frame): |
| 107 | + print() |
| 108 | + |
| 109 | + |
| 110 | +def attach(bpf, args): |
| 111 | + bpf.attach_uprobe( |
| 112 | + name=args.path, |
| 113 | + sym="exec_simple_query", |
| 114 | + fn_name="probe_exec_simple_query") |
| 115 | + bpf.attach_uretprobe( |
| 116 | + name=args.path, |
| 117 | + sym="exec_simple_query", |
| 118 | + fn_name="probe_exec_simple_query_finish") |
| 119 | + bpf.attach_perf_event( |
| 120 | + ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_MISSES, |
| 121 | + fn_name="on_cache_miss", sample_period=args.sample_period) |
| 122 | + bpf.attach_perf_event( |
| 123 | + ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_REFERENCES, |
| 124 | + fn_name="on_cache_ref", sample_period=args.sample_period) |
| 125 | + |
| 126 | + |
| 127 | +def run(args): |
| 128 | + print("Attaching...") |
| 129 | + debug = 4 if args.debug else 0 |
| 130 | + bpf = BPF(text=bpf_text, debug=debug) |
| 131 | + attach(bpf, args) |
| 132 | + exiting = False |
| 133 | + |
| 134 | + if args.debug: |
| 135 | + bpf["events"].open_perf_buffer(print_event) |
| 136 | + |
| 137 | + print("Listening...") |
| 138 | + while True: |
| 139 | + try: |
| 140 | + sleep(1) |
| 141 | + if args.debug: |
| 142 | + bpf.perf_buffer_poll() |
| 143 | + except KeyboardInterrupt: |
| 144 | + exiting = True |
| 145 | + # as cleanup can take many seconds, trap Ctrl-C: |
| 146 | + signal.signal(signal.SIGINT, signal_ignore) |
| 147 | + |
| 148 | + if exiting: |
| 149 | + print() |
| 150 | + print("Detaching...") |
| 151 | + print() |
| 152 | + break |
| 153 | + |
| 154 | + miss_count = {} |
| 155 | + for (k, v) in bpf.get_table('miss_count').items(): |
| 156 | + miss_count[(k.pid, k.cpu, k.name)] = v.value |
| 157 | + |
| 158 | + print('{:<8} {:<16} {:<100} {:<4} {:>12} {:>12} {:>6}%'.format( |
| 159 | + "PID", "NAME", "QUERY", "CPU", "REFERENCE", "MISS", "HIT")) |
| 160 | + tot_ref = 0 |
| 161 | + tot_miss = 0 |
| 162 | + for (k, v) in bpf.get_table('ref_count').items(): |
| 163 | + try: |
| 164 | + miss = miss_count[(k.pid, k.cpu, k.name)] |
| 165 | + except KeyError: |
| 166 | + miss = 0 |
| 167 | + tot_ref += v.value |
| 168 | + tot_miss += miss |
| 169 | + # This happens on some PIDs due to missed counts caused by sampling |
| 170 | + hit = (v.value - miss) if (v.value >= miss) else 0 |
| 171 | + print('{:<8d} {:<16s} {:<100s} {:<4d} {:>12d} {:>12d} {:>6.2f}%'.format( |
| 172 | + k.pid, k.name.decode(), k.query.decode(), k.cpu, v.value, miss, |
| 173 | + (float(hit) / float(v.value)) * 100.0)) |
| 174 | + |
| 175 | + if tot_ref != 0: |
| 176 | + print() |
| 177 | + print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format( |
| 178 | + tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0)) |
| 179 | + |
| 180 | + |
| 181 | +def parse_args(): |
| 182 | + parser = argparse.ArgumentParser( |
| 183 | + description="Summarize cache references and misses by postgres backend", |
| 184 | + formatter_class=argparse.RawDescriptionHelpFormatter) |
| 185 | + parser.add_argument("path", type=str, help="path to PostgreSQL binary") |
| 186 | + parser.add_argument( |
| 187 | + "-c", "--sample_period", type=int, default=100, |
| 188 | + help="Sample one in this many number of cache reference / miss events") |
| 189 | + parser.add_argument("--ebpf", action="store_true", |
| 190 | + help=argparse.SUPPRESS) |
| 191 | + parser.add_argument("-d", "--debug", action='store_true', default=False, |
| 192 | + help="debug mode") |
| 193 | + |
| 194 | + return parser.parse_args() |
| 195 | + |
| 196 | + |
| 197 | +if __name__ == "__main__": |
| 198 | + run(parse_args()) |
0 commit comments