blob: dcb6d267c21d5f157b65b99b561c7809d1251a46 [file] [log] [blame]
Alexey Ivanov777e8022019-01-03 13:46:38 -08001#!/usr/bin/env 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
gurugioc5a448a2019-01-04 17:54:39 +010070int trace_req_done(struct pt_regs *ctx, struct request *req)
Brendan Gregg9fa15622015-09-21 15:51:11 -070071{
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:
Jerome Marchand74e25ed2018-12-10 08:54:50 +0100119 if BPF.get_kprobe_functions(b'blk_start_request'):
120 b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start")
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800121 b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start")
gurugioc5a448a2019-01-04 17:54:39 +0100122b.attach_kprobe(event="blk_account_io_done",
123 fn_name="trace_req_done")
Brendan Gregg9fa15622015-09-21 15:51:11 -0700124
125print("Tracing block device I/O... Hit Ctrl-C to end.")
126
127# output
128exiting = 0 if args.interval else 1
129dist = b.get_table("dist")
130while (1):
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800131 try:
132 sleep(int(args.interval))
133 except KeyboardInterrupt:
134 exiting = 1
Brendan Gregg9fa15622015-09-21 15:51:11 -0700135
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800136 print()
137 if args.timestamp:
138 print("%-8s\n" % strftime("%H:%M:%S"), end="")
Brendan Gregg9fa15622015-09-21 15:51:11 -0700139
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800140 dist.print_log2_hist(label, "disk")
141 dist.clear()
Brendan Gregg9fa15622015-09-21 15:51:11 -0700142
Alexei Starovoitova79da0a2016-01-09 12:06:40 -0800143 countdown -= 1
144 if exiting or countdown == 0:
145 exit()