xref: /aosp_15_r20/external/bcc/tools/biopattern.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python
2*387f9dfdSAndroid Build Coastguard Worker# @lint-avoid-python-3-compatibility-imports
3*387f9dfdSAndroid Build Coastguard Worker#
4*387f9dfdSAndroid Build Coastguard Worker# biopattern - Identify random/sequential disk access patterns.
5*387f9dfdSAndroid Build Coastguard Worker#              For Linux, uses BCC, eBPF.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2022 Rocky Xing.
8*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
9*387f9dfdSAndroid Build Coastguard Worker#
10*387f9dfdSAndroid Build Coastguard Worker# 21-Feb-2022   Rocky Xing   Created this.
11*387f9dfdSAndroid Build Coastguard Worker
12*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
13*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
14*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime
15*387f9dfdSAndroid Build Coastguard Workerimport argparse
16*387f9dfdSAndroid Build Coastguard Workerimport os
17*387f9dfdSAndroid Build Coastguard Worker
18*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
19*387f9dfdSAndroid Build Coastguard Worker    ./biopattern            # show block device I/O pattern.
20*387f9dfdSAndroid Build Coastguard Worker    ./biopattern 1 10       # print 1 second summaries, 10 times
21*387f9dfdSAndroid Build Coastguard Worker    ./biopattern -d sdb     # show sdb only
22*387f9dfdSAndroid Build Coastguard Worker"""
23*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
24*387f9dfdSAndroid Build Coastguard Worker    description="Show block device I/O pattern.",
25*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
26*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
27*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-d", "--disk", type=str,
28*387f9dfdSAndroid Build Coastguard Worker    help="Trace this disk only")
29*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=99999999,
30*387f9dfdSAndroid Build Coastguard Worker    help="Output interval in seconds")
31*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999,
32*387f9dfdSAndroid Build Coastguard Worker    help="Number of outputs")
33*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
34*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count)
35*387f9dfdSAndroid Build Coastguard Worker
36*387f9dfdSAndroid Build Coastguard Workerbpf_text="""
37*387f9dfdSAndroid Build Coastguard Workerstruct counter {
38*387f9dfdSAndroid Build Coastguard Worker    u64 last_sector;
39*387f9dfdSAndroid Build Coastguard Worker    u64 bytes;
40*387f9dfdSAndroid Build Coastguard Worker    u32 sequential;
41*387f9dfdSAndroid Build Coastguard Worker    u32 random;
42*387f9dfdSAndroid Build Coastguard Worker};
43*387f9dfdSAndroid Build Coastguard Worker
44*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(counters, u32, struct counter);
45*387f9dfdSAndroid Build Coastguard Worker
46*387f9dfdSAndroid Build Coastguard WorkerTRACEPOINT_PROBE(block, block_rq_complete)
47*387f9dfdSAndroid Build Coastguard Worker{
48*387f9dfdSAndroid Build Coastguard Worker    struct counter *counterp;
49*387f9dfdSAndroid Build Coastguard Worker    struct counter zero = {};
50*387f9dfdSAndroid Build Coastguard Worker    u32 dev = args->dev;
51*387f9dfdSAndroid Build Coastguard Worker    u64 sector = args->sector;
52*387f9dfdSAndroid Build Coastguard Worker    u32 nr_sector = args->nr_sector;
53*387f9dfdSAndroid Build Coastguard Worker
54*387f9dfdSAndroid Build Coastguard Worker    DISK_FILTER
55*387f9dfdSAndroid Build Coastguard Worker
56*387f9dfdSAndroid Build Coastguard Worker    counterp = counters.lookup_or_try_init(&dev, &zero);
57*387f9dfdSAndroid Build Coastguard Worker    if (counterp == 0) {
58*387f9dfdSAndroid Build Coastguard Worker        return 0;
59*387f9dfdSAndroid Build Coastguard Worker    }
60*387f9dfdSAndroid Build Coastguard Worker
61*387f9dfdSAndroid Build Coastguard Worker    if (counterp->last_sector) {
62*387f9dfdSAndroid Build Coastguard Worker        if (counterp->last_sector == sector) {
63*387f9dfdSAndroid Build Coastguard Worker            __sync_fetch_and_add(&counterp->sequential, 1);
64*387f9dfdSAndroid Build Coastguard Worker        } else {
65*387f9dfdSAndroid Build Coastguard Worker            __sync_fetch_and_add(&counterp->random, 1);
66*387f9dfdSAndroid Build Coastguard Worker        }
67*387f9dfdSAndroid Build Coastguard Worker        __sync_fetch_and_add(&counterp->bytes, nr_sector * 512);
68*387f9dfdSAndroid Build Coastguard Worker    }
69*387f9dfdSAndroid Build Coastguard Worker    counterp->last_sector = sector + nr_sector;
70*387f9dfdSAndroid Build Coastguard Worker
71*387f9dfdSAndroid Build Coastguard Worker    return 0;
72*387f9dfdSAndroid Build Coastguard Worker}
73*387f9dfdSAndroid Build Coastguard Worker"""
74*387f9dfdSAndroid Build Coastguard Worker
75*387f9dfdSAndroid Build Coastguard Workerdev_minor_bits = 20
76*387f9dfdSAndroid Build Coastguard Worker
77*387f9dfdSAndroid Build Coastguard Workerdef mkdev(major, minor):
78*387f9dfdSAndroid Build Coastguard Worker   return (major << dev_minor_bits) | minor
79*387f9dfdSAndroid Build Coastguard Worker
80*387f9dfdSAndroid Build Coastguard Worker
81*387f9dfdSAndroid Build Coastguard Workerpartitions = {}
82*387f9dfdSAndroid Build Coastguard Worker
83*387f9dfdSAndroid Build Coastguard Workerwith open("/proc/partitions", 'r') as f:
84*387f9dfdSAndroid Build Coastguard Worker    lines = f.readlines()
85*387f9dfdSAndroid Build Coastguard Worker    for line in lines[2:]:
86*387f9dfdSAndroid Build Coastguard Worker        words = line.strip().split()
87*387f9dfdSAndroid Build Coastguard Worker        major = int(words[0])
88*387f9dfdSAndroid Build Coastguard Worker        minor = int(words[1])
89*387f9dfdSAndroid Build Coastguard Worker        part_name = words[3]
90*387f9dfdSAndroid Build Coastguard Worker        partitions[mkdev(major, minor)] = part_name
91*387f9dfdSAndroid Build Coastguard Worker
92*387f9dfdSAndroid Build Coastguard Workerif args.disk is not None:
93*387f9dfdSAndroid Build Coastguard Worker    disk_path = os.path.join('/dev', args.disk)
94*387f9dfdSAndroid Build Coastguard Worker    if os.path.exists(disk_path) == False:
95*387f9dfdSAndroid Build Coastguard Worker        print("no such disk '%s'" % args.disk)
96*387f9dfdSAndroid Build Coastguard Worker        exit(1)
97*387f9dfdSAndroid Build Coastguard Worker
98*387f9dfdSAndroid Build Coastguard Worker    stat_info = os.stat(disk_path)
99*387f9dfdSAndroid Build Coastguard Worker    major = os.major(stat_info.st_rdev)
100*387f9dfdSAndroid Build Coastguard Worker    minor = os.minor(stat_info.st_rdev)
101*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('DISK_FILTER',
102*387f9dfdSAndroid Build Coastguard Worker                                'if (dev != %s) { return 0; }' % mkdev(major, minor))
103*387f9dfdSAndroid Build Coastguard Workerelse:
104*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('DISK_FILTER', '')
105*387f9dfdSAndroid Build Coastguard Worker
106*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text)
107*387f9dfdSAndroid Build Coastguard Worker
108*387f9dfdSAndroid Build Coastguard Workerexiting = 0 if args.interval else 1
109*387f9dfdSAndroid Build Coastguard Workercounters = b.get_table("counters")
110*387f9dfdSAndroid Build Coastguard Worker
111*387f9dfdSAndroid Build Coastguard Workerprint("%-9s %-7s %5s %5s %8s %10s" %
112*387f9dfdSAndroid Build Coastguard Worker    ("TIME", "DISK", "%RND", "%SEQ", "COUNT", "KBYTES"))
113*387f9dfdSAndroid Build Coastguard Worker
114*387f9dfdSAndroid Build Coastguard Workerwhile True:
115*387f9dfdSAndroid Build Coastguard Worker    try:
116*387f9dfdSAndroid Build Coastguard Worker        sleep(int(args.interval))
117*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
118*387f9dfdSAndroid Build Coastguard Worker        exiting = 1
119*387f9dfdSAndroid Build Coastguard Worker
120*387f9dfdSAndroid Build Coastguard Worker    for k, v in counters.items():
121*387f9dfdSAndroid Build Coastguard Worker        total = v.random + v.sequential
122*387f9dfdSAndroid Build Coastguard Worker        if total == 0:
123*387f9dfdSAndroid Build Coastguard Worker            continue
124*387f9dfdSAndroid Build Coastguard Worker
125*387f9dfdSAndroid Build Coastguard Worker        part_name = partitions.get(k.value, "Unknown")
126*387f9dfdSAndroid Build Coastguard Worker        random_percent = int(round(v.random * 100 / total))
127*387f9dfdSAndroid Build Coastguard Worker
128*387f9dfdSAndroid Build Coastguard Worker        print("%-9s %-7s %5d %5d %8d %10d" % (
129*387f9dfdSAndroid Build Coastguard Worker            strftime("%H:%M:%S"),
130*387f9dfdSAndroid Build Coastguard Worker            part_name,
131*387f9dfdSAndroid Build Coastguard Worker            random_percent,
132*387f9dfdSAndroid Build Coastguard Worker            100 - random_percent,
133*387f9dfdSAndroid Build Coastguard Worker            total,
134*387f9dfdSAndroid Build Coastguard Worker            v.bytes / 1024))
135*387f9dfdSAndroid Build Coastguard Worker
136*387f9dfdSAndroid Build Coastguard Worker    counters.clear()
137*387f9dfdSAndroid Build Coastguard Worker
138*387f9dfdSAndroid Build Coastguard Worker    countdown -= 1
139*387f9dfdSAndroid Build Coastguard Worker    if exiting or countdown == 0:
140*387f9dfdSAndroid Build Coastguard Worker        exit()
141*387f9dfdSAndroid Build Coastguard Worker
142