Created
September 22, 2015 20:14
biolatency using eBPF/bcc, Sep-2015 version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# | |
# biolatency Summarize block device I/O latency as a histogram. | |
# For Linux, uses BCC, eBPF. | |
# | |
# USAGE: biolatency [-h] [-T] [-Q] [-m] [interval] [count] | |
# | |
# Copyright (c) 2015 Brendan Gregg. | |
# Licensed under the Apache License, Version 2.0 (the "License") | |
# | |
# 20-Sep-2015 Brendan Gregg Created this. | |
from __future__ import print_function | |
from bcc import BPF | |
from time import sleep, strftime | |
import argparse | |
# arguments | |
examples = """examples: | |
./biolatency # summarize block I/O latency as a histogram | |
./biolatency 1 10 # print 1 second summaries, 10 times | |
./biolatency -mT 1 # 1s summaries, milliseconds, and timestamps | |
./biolatency -Q # include OS queued time in I/O time | |
""" | |
parser = argparse.ArgumentParser( | |
description="Summarize block device I/O latency as a histogram", | |
formatter_class=argparse.RawDescriptionHelpFormatter, | |
epilog=examples) | |
parser.add_argument("-T", "--timestamp", action="store_true", | |
help="include timestamp on output") | |
parser.add_argument("-Q", "--queued", action="store_true", | |
help="include OS queued time in I/O time") | |
parser.add_argument("-m", "--milliseconds", action="store_true", | |
help="millisecond histogram") | |
parser.add_argument("interval", nargs="?", default=99999999, | |
help="output interval, in seconds") | |
parser.add_argument("count", nargs="?", default=99999999, | |
help="number of outputs") | |
args = parser.parse_args() | |
countdown = int(args.count) | |
debug = 0 | |
# load BPF program | |
bpf_text = """ | |
#include <uapi/linux/ptrace.h> | |
#include <linux/blkdev.h> | |
BPF_TABLE(\"array\", int, u64, dist, 64); | |
BPF_HASH(start, struct request *); | |
// time block I/O | |
int trace_req_start(struct pt_regs *ctx, struct request *req) | |
{ | |
u64 ts = bpf_ktime_get_ns(); | |
start.update(&req, &ts); | |
return 0; | |
} | |
// output | |
int trace_req_completion(struct pt_regs *ctx, struct request *req) | |
{ | |
u64 *tsp, delta; | |
// fetch timestamp and calculate delta | |
tsp = start.lookup(&req); | |
if (tsp == 0) { | |
return 0; // missed issue | |
} | |
delta = bpf_ktime_get_ns() - *tsp; | |
FACTOR | |
// store as histogram | |
int index = bpf_log2l(delta); | |
u64 *leaf = dist.lookup(&index); | |
if (leaf) (*leaf)++; | |
start.delete(&req); | |
return 0; | |
} | |
""" | |
if args.milliseconds: | |
bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;') | |
label = "msecs" | |
else: | |
bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;') | |
label = "usecs" | |
if debug: | |
print(bpf_text) | |
# load BPF program | |
b = BPF(text=bpf_text) | |
if args.queued: | |
b.attach_kprobe(event="blk_account_io_start", fn_name="trace_req_start") | |
else: | |
b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") | |
b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") | |
b.attach_kprobe(event="blk_account_io_completion", | |
fn_name="trace_req_completion") | |
print("Tracing block device I/O... Hit Ctrl-C to end.") | |
# output | |
exiting = 0 if args.interval else 1 | |
dist = b.get_table("dist") | |
while (1): | |
try: | |
sleep(int(args.interval)) | |
except KeyboardInterrupt: | |
exiting=1 | |
print() | |
if args.timestamp: | |
print("%-8s\n" % strftime("%H:%M:%S"), end="") | |
dist.print_log2_hist(label) | |
dist.clear() | |
countdown -= 1 | |
if exiting or countdown == 0: | |
exit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment