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