blob: 3879af1141d6c1445ea21f78c4a17611cfa5dcb1 [file] [log] [blame]
Brendan Gregg9fa15622015-09-21 15:51:11 -07001#!/usr/bin/python
Alexei Starovoitova79da0a2016-01-09 12:06:40 -08002# @lint-avoid-python-3-compatibility-imports
Brendan Gregg9fa15622015-09-21 15:51:11 -07003#
Alexei Starovoitova79da0a2016-01-09 12:06:40 -08004# biolatency Summarize block device I/O latency as a histogram.
5# For Linux, uses BCC, eBPF.
Brendan Gregg9fa15622015-09-21 15:51:11 -07006#
Nathan Scottf5fb9af2018-01-17 09:39:59 +11007# USAGE: biolatency [-h] [-T] [-Q] [-m] [-D] [interval] [count]
Brendan Gregg9fa15622015-09-21 15:51:11 -07008#
9# Copyright (c) 2015 Brendan Gregg.
10# Licensed under the Apache License, Version 2.0 (the "License")
11#
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080012# 20-Sep-2015 Brendan Gregg Created this.
Brendan Gregg9fa15622015-09-21 15:51:11 -070013
14from __future__ import print_function
15from bcc import BPF
16from time import sleep, strftime
17import argparse
18
19# arguments
20examples = """examples:
21 ./biolatency # summarize block I/O latency as a histogram
22 ./biolatency 1 10 # print 1 second summaries, 10 times
23 ./biolatency -mT 1 # 1s summaries, milliseconds, and timestamps
24 ./biolatency -Q # include OS queued time in I/O time
Brendan Gregg44e68772015-09-25 13:20:16 -070025 ./biolatency -D # show each disk device separately
Brendan Gregg9fa15622015-09-21 15:51:11 -070026"""
27parser = argparse.ArgumentParser(
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080028 description="Summarize block device I/O latency as a histogram",
29 formatter_class=argparse.RawDescriptionHelpFormatter,
Nathan Scott1a197db2018-01-21 09:14:27 +110030 epilog=examples)
Brendan Gregg9fa15622015-09-21 15:51:11 -070031parser.add_argument("-T", "--timestamp", action="store_true",
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080032 help="include timestamp on output")
Brendan Gregg9fa15622015-09-21 15:51:11 -070033parser.add_argument("-Q", "--queued", action="store_true",
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080034 help="include OS queued time in I/O time")
Brendan Gregg9fa15622015-09-21 15:51:11 -070035parser.add_argument("-m", "--milliseconds", action="store_true",
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080036 help="millisecond histogram")
Brendan Gregg44e68772015-09-25 13:20:16 -070037parser.add_argument("-D", "--disks", action="store_true",
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080038 help="print a histogram per disk device")
Brendan Gregg9fa15622015-09-21 15:51:11 -070039parser.add_argument("interval", nargs="?", default=99999999,
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080040 help="output interval, in seconds")
Brendan Gregg9fa15622015-09-21 15:51:11 -070041parser.add_argument("count", nargs="?", default=99999999,
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080042 help="number of outputs")
Nathan Scottf5fb9af2018-01-17 09:39:59 +110043parser.add_argument("--ebpf", action="store_true",
44 help=argparse.SUPPRESS)
Brendan Gregg9fa15622015-09-21 15:51:11 -070045args = parser.parse_args()
46countdown = int(args.count)
47debug = 0
48
Brendan Gregg44e68772015-09-25 13:20:16 -070049# define BPF program
Brendan Gregg9fa15622015-09-21 15:51:11 -070050bpf_text = """
51#include <uapi/linux/ptrace.h>
52#include <linux/blkdev.h>
53
Brendan Gregg44e68772015-09-25 13:20:16 -070054typedef struct disk_key {
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080055 char disk[DISK_NAME_LEN];
56 u64 slot;
Brendan Gregg44e68772015-09-25 13:20:16 -070057} disk_key_t;
Brendan Gregg9fa15622015-09-21 15:51:11 -070058BPF_HASH(start, struct request *);
Brendan Gregg44e68772015-09-25 13:20:16 -070059STORAGE
Brendan Gregg9fa15622015-09-21 15:51:11 -070060
61// time block I/O
62int trace_req_start(struct pt_regs *ctx, struct request *req)
63{
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080064 u64 ts = bpf_ktime_get_ns();
65 start.update(&req, &ts);
66 return 0;
Brendan Gregg9fa15622015-09-21 15:51:11 -070067}
68
69// output
70int trace_req_completion(struct pt_regs *ctx, struct request *req)
71{
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080072 u64 *tsp, delta;
Brendan Gregg9fa15622015-09-21 15:51:11 -070073
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080074 // fetch timestamp and calculate delta
75 tsp = start.lookup(&req);
76 if (tsp == 0) {
77 return 0; // missed issue
78 }
79 delta = bpf_ktime_get_ns() - *tsp;
80 FACTOR
Brendan Gregg9fa15622015-09-21 15:51:11 -070081
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080082 // store as histogram
83 STORE
Brendan Gregg9fa15622015-09-21 15:51:11 -070084
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080085 start.delete(&req);
86 return 0;
Brendan Gregg9fa15622015-09-21 15:51:11 -070087}
88"""
Brendan Gregg44e68772015-09-25 13:20:16 -070089
90# code substitutions
Brendan Gregg9fa15622015-09-21 15:51:11 -070091if args.milliseconds:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080092 bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;')
93 label = "msecs"
Brendan Gregg9fa15622015-09-21 15:51:11 -070094else:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080095 bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;')
96 label = "usecs"
Brendan Gregg44e68772015-09-25 13:20:16 -070097if args.disks:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -080098 bpf_text = bpf_text.replace('STORAGE',
99 'BPF_HISTOGRAM(dist, disk_key_t);')
100 bpf_text = bpf_text.replace('STORE',
101 'disk_key_t key = {.slot = bpf_log2l(delta)}; ' +
Mark Draytonfc245df2018-09-19 16:41:49 -0700102 'void *__tmp = (void *)req->rq_disk->disk_name; ' +
103 'bpf_probe_read(&key.disk, sizeof(key.disk), __tmp); ' +
104 'dist.increment(key);')
Brendan Gregg44e68772015-09-25 13:20:16 -0700105else:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800106 bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);')
107 bpf_text = bpf_text.replace('STORE',
108 'dist.increment(bpf_log2l(delta));')
Nathan Scottca4ba552018-01-16 11:02:58 +1100109if debug or args.ebpf:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800110 print(bpf_text)
Nathan Scottca4ba552018-01-16 11:02:58 +1100111 if args.ebpf:
112 exit()
Brendan Gregg9fa15622015-09-21 15:51:11 -0700113
114# load BPF program
115b = BPF(text=bpf_text)
116if args.queued:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800117 b.attach_kprobe(event="blk_account_io_start", fn_name="trace_req_start")
Brendan Gregg9fa15622015-09-21 15:51:11 -0700118else:
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800119 b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start")
120 b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start")
Brendan Gregg9fa15622015-09-21 15:51:11 -0700121b.attach_kprobe(event="blk_account_io_completion",
122 fn_name="trace_req_completion")
123
124print("Tracing block device I/O... Hit Ctrl-C to end.")
125
126# output
127exiting = 0 if args.interval else 1
128dist = b.get_table("dist")
129while (1):
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800130 try:
131 sleep(int(args.interval))
132 except KeyboardInterrupt:
133 exiting = 1
Brendan Gregg9fa15622015-09-21 15:51:11 -0700134
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800135 print()
136 if args.timestamp:
137 print("%-8s\n" % strftime("%H:%M:%S"), end="")
Brendan Gregg9fa15622015-09-21 15:51:11 -0700138
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800139 dist.print_log2_hist(label, "disk")
140 dist.clear()
Brendan Gregg9fa15622015-09-21 15:51:11 -0700141
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800142 countdown -= 1
143 if exiting or countdown == 0:
144 exit()