1#!/usr/bin/env python 2 3from __future__ import print_function 4from bcc import BPF 5from ctypes import * 6import argparse 7import os 8from time import sleep,time,localtime,asctime 9 10# pre defines ------------------------------- 11ROOT_PATH = "/sys/class/net" 12IFNAMSIZ = 16 13COL_WIDTH = 10 14MAX_QUEUE_NUM = 1024 15EBPF_FILE = "netqtop.c" 16 17# structure for network interface name array 18class Devname(Structure): 19 _fields_=[ 20 ('name', c_char*IFNAMSIZ) 21 ] 22 23################## printer for results ################### 24def to_str(num): 25 s = "" 26 if num > 1000000: 27 return str(round(num/(1024*1024.0), 2)) + 'M' 28 elif num > 1000: 29 return str(round(num/1024.0, 2)) + 'K' 30 else: 31 if isinstance(num, float): 32 return str(round(num, 2)) 33 else: 34 return str(num) 35 36def print_table(table, qnum): 37 global print_interval 38 39 # ---- print headers ---------------- 40 headers = [ 41 "QueueID", 42 "avg_size", 43 "[0, 64)", 44 "[64, 512)", 45 "[512, 2K)", 46 "[2K, 16K)", 47 "[16K, 64K)" 48 ] 49 if args.throughput: 50 headers.append("BPS") 51 headers.append("PPS") 52 53 print(" ", end="") 54 for hd in headers: 55 print( "%-11s" % hd, end="") 56 print() 57 58 # ------- calculates -------------- 59 qids=[] 60 tBPS = 0 61 tPPS = 0 62 tAVG = 0 63 tGroup = [0,0,0,0,0] 64 tpkt = 0 65 tlen = 0 66 for k, v in table.items(): 67 qids += [k.value] 68 tlen += v.total_pkt_len 69 tpkt += v.num_pkt 70 tGroup[0] += v.size_64B 71 tGroup[1] += v.size_512B 72 tGroup[2] += v.size_2K 73 tGroup[3] += v.size_16K 74 tGroup[4] += v.size_64K 75 tBPS = tlen / print_interval 76 tPPS = tpkt / print_interval 77 if tpkt != 0: 78 tAVG = tlen / tpkt 79 80 # -------- print table -------------- 81 for k in range(qnum): 82 if k in qids: 83 item = table[c_ushort(k)] 84 data = [ 85 k, 86 item.total_pkt_len, 87 item.num_pkt, 88 item.size_64B, 89 item.size_512B, 90 item.size_2K, 91 item.size_16K, 92 item.size_64K 93 ] 94 else: 95 data = [k,0,0,0,0,0,0,0] 96 97 # print a line per queue 98 avg = 0 99 if data[2] != 0: 100 avg = data[1] / data[2] 101 print(" %-11d%-11s%-11s%-11s%-11s%-11s%-11s" % ( 102 data[0], 103 to_str(avg), 104 to_str(data[3]), 105 to_str(data[4]), 106 to_str(data[5]), 107 to_str(data[6]), 108 to_str(data[7]) 109 ), end="") 110 if args.throughput: 111 BPS = data[1] / print_interval 112 PPS = data[2] / print_interval 113 print("%-11s%-11s" % ( 114 to_str(BPS), 115 to_str(PPS) 116 )) 117 else: 118 print() 119 120 # ------- print total -------------- 121 print(" Total %-11s%-11s%-11s%-11s%-11s%-11s" % ( 122 to_str(tAVG), 123 to_str(tGroup[0]), 124 to_str(tGroup[1]), 125 to_str(tGroup[2]), 126 to_str(tGroup[3]), 127 to_str(tGroup[4]) 128 ), end="") 129 130 if args.throughput: 131 print("%-11s%-11s" % ( 132 to_str(tBPS), 133 to_str(tPPS) 134 )) 135 else: 136 print() 137 138 139def print_result(b): 140 # --------- print tx queues --------------- 141 print(asctime(localtime(time()))) 142 print("TX") 143 table = b['tx_q'] 144 print_table(table, tx_num) 145 b['tx_q'].clear() 146 147 # --------- print rx queues --------------- 148 print("") 149 print("RX") 150 table = b['rx_q'] 151 print_table(table, rx_num) 152 b['rx_q'].clear() 153 if args.throughput: 154 print("-"*95) 155 else: 156 print("-"*77) 157 158############## specify network interface ################# 159parser = argparse.ArgumentParser(description="") 160parser.add_argument("--name", "-n", type=str, default="") 161parser.add_argument("--interval", "-i", type=float, default=1) 162parser.add_argument("--throughput", "-t", action="store_true") 163parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) 164args = parser.parse_args() 165 166if args.ebpf: 167 with open(EBPF_FILE) as fileobj: 168 progtxt = fileobj.read() 169 print(progtxt) 170 exit() 171 172if args.name == "": 173 print ("Please specify a network interface.") 174 exit() 175else: 176 dev_name = args.name 177 178if len(dev_name) > IFNAMSIZ-1: 179 print ("NIC name too long") 180 exit() 181 182print_interval = args.interval + 0.0 183if print_interval == 0: 184 print ("print interval must be non-zero") 185 exit() 186 187################ get number of queues ##################### 188tx_num = 0 189rx_num = 0 190path = ROOT_PATH + "/" + dev_name + "/queues" 191if not os.path.exists(path): 192 print ("Net interface", dev_name, "does not exits.") 193 exit() 194 195list = os.listdir(path) 196for s in list: 197 if s[0] == 'r': 198 rx_num += 1 199 if s[0] == 't': 200 tx_num += 1 201 202if tx_num > MAX_QUEUE_NUM or rx_num > MAX_QUEUE_NUM: 203 print ("number of queues over 1024 is not supported.") 204 exit() 205 206################## start tracing ################## 207b = BPF(src_file = EBPF_FILE) 208# --------- set hash array -------- 209devname_map = b['name_map'] 210_name = Devname() 211_name.name = dev_name.encode() 212devname_map[0] = _name 213 214while 1: 215 try: 216 sleep(print_interval) 217 print_result(b) 218 except KeyboardInterrupt: 219 exit() 220