Skip to content

Commit 2ee0658

Browse files
committed
Cleanup query_cache script
1 parent c50f297 commit 2ee0658

File tree

1 file changed

+95
-69
lines changed

1 file changed

+95
-69
lines changed

query_cache.py

Lines changed: 95 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,18 @@
55
# uapi/linux/perf_event.h, it varies to different architecture.
66
# On x86-64, they mean LLC references and LLC misses. Postgres
77
# backend provides a query string. Based on llstat.py from bcc.
8-
#
98
# For Linux, uses BCC, eBPF.
109
#
1110
# SEE ALSO: perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm
1211
#
1312
# 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.
1913

2014
from __future__ import print_function
2115
import argparse
2216
from bcc import BPF, PerfType, PerfHWConfig
2317
import signal
2418
from time import sleep
2519

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()
4020

4121
# load BPF program
4222
bpf_text="""
@@ -121,52 +101,98 @@
121101
}
122102
"""
123103

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

Comments
 (0)