| #!/usr/bin/env python |
| |
| from __future__ import print_function |
| from bcc import BPF |
| from ctypes import * |
| import argparse |
| import os |
| from time import sleep,time,localtime,asctime |
| |
| # pre defines ------------------------------- |
| ROOT_PATH = "/sys/class/net" |
| IFNAMSIZ = 16 |
| COL_WIDTH = 10 |
| MAX_QUEUE_NUM = 1024 |
| EBPF_FILE = "netqtop.c" |
| |
| # structure for network interface name array |
| class Devname(Structure): |
| _fields_=[ |
| ('name', c_char*IFNAMSIZ) |
| ] |
| |
| ################## printer for results ################### |
| def to_str(num): |
| s = "" |
| if num > 1000000: |
| return str(round(num/(1024*1024.0), 2)) + 'M' |
| elif num > 1000: |
| return str(round(num/1024.0, 2)) + 'K' |
| else: |
| if isinstance(num, float): |
| return str(round(num, 2)) |
| else: |
| return str(num) |
| |
| def print_table(table, qnum): |
| global print_interval |
| |
| # ---- print headers ---------------- |
| headers = [ |
| "QueueID", |
| "avg_size", |
| "[0, 64)", |
| "[64, 512)", |
| "[512, 2K)", |
| "[2K, 16K)", |
| "[16K, 64K)" |
| ] |
| if args.throughput: |
| headers.append("BPS") |
| headers.append("PPS") |
| |
| print(" ", end="") |
| for hd in headers: |
| print( "%-11s" % hd, end="") |
| print() |
| |
| # ------- calculates -------------- |
| qids=[] |
| tBPS = 0 |
| tPPS = 0 |
| tAVG = 0 |
| tGroup = [0,0,0,0,0] |
| tpkt = 0 |
| tlen = 0 |
| for k, v in table.items(): |
| qids += [k.value] |
| tlen += v.total_pkt_len |
| tpkt += v.num_pkt |
| tGroup[0] += v.size_64B |
| tGroup[1] += v.size_512B |
| tGroup[2] += v.size_2K |
| tGroup[3] += v.size_16K |
| tGroup[4] += v.size_64K |
| tBPS = tlen / print_interval |
| tPPS = tpkt / print_interval |
| if tpkt != 0: |
| tAVG = tlen / tpkt |
| |
| # -------- print table -------------- |
| for k in range(qnum): |
| if k in qids: |
| item = table[c_ushort(k)] |
| data = [ |
| k, |
| item.total_pkt_len, |
| item.num_pkt, |
| item.size_64B, |
| item.size_512B, |
| item.size_2K, |
| item.size_16K, |
| item.size_64K |
| ] |
| else: |
| data = [k,0,0,0,0,0,0,0] |
| |
| # print a line per queue |
| avg = 0 |
| if data[2] != 0: |
| avg = data[1] / data[2] |
| print(" %-11d%-11s%-11s%-11s%-11s%-11s%-11s" % ( |
| data[0], |
| to_str(avg), |
| to_str(data[3]), |
| to_str(data[4]), |
| to_str(data[5]), |
| to_str(data[6]), |
| to_str(data[7]) |
| ), end="") |
| if args.throughput: |
| BPS = data[1] / print_interval |
| PPS = data[2] / print_interval |
| print("%-11s%-11s" % ( |
| to_str(BPS), |
| to_str(PPS) |
| )) |
| else: |
| print() |
| |
| # ------- print total -------------- |
| print(" Total %-11s%-11s%-11s%-11s%-11s%-11s" % ( |
| to_str(tAVG), |
| to_str(tGroup[0]), |
| to_str(tGroup[1]), |
| to_str(tGroup[2]), |
| to_str(tGroup[3]), |
| to_str(tGroup[4]) |
| ), end="") |
| |
| if args.throughput: |
| print("%-11s%-11s" % ( |
| to_str(tBPS), |
| to_str(tPPS) |
| )) |
| else: |
| print() |
| |
| |
| def print_result(b): |
| # --------- print tx queues --------------- |
| print(asctime(localtime(time()))) |
| print("TX") |
| table = b['tx_q'] |
| print_table(table, tx_num) |
| b['tx_q'].clear() |
| |
| # --------- print rx queues --------------- |
| print("") |
| print("RX") |
| table = b['rx_q'] |
| print_table(table, rx_num) |
| b['rx_q'].clear() |
| if args.throughput: |
| print("-"*95) |
| else: |
| print("-"*77) |
| |
| ############## specify network interface ################# |
| parser = argparse.ArgumentParser(description="") |
| parser.add_argument("--name", "-n", type=str, default="") |
| parser.add_argument("--interval", "-i", type=float, default=1) |
| parser.add_argument("--throughput", "-t", action="store_true") |
| parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) |
| args = parser.parse_args() |
| |
| if args.ebpf: |
| with open(EBPF_FILE) as fileobj: |
| progtxt = fileobj.read() |
| print(progtxt) |
| exit() |
| |
| if args.name == "": |
| print ("Please specify a network interface.") |
| exit() |
| else: |
| dev_name = args.name |
| |
| if len(dev_name) > IFNAMSIZ-1: |
| print ("NIC name too long") |
| exit() |
| |
| print_interval = args.interval + 0.0 |
| if print_interval == 0: |
| print ("print interval must be non-zero") |
| exit() |
| |
| ################ get number of queues ##################### |
| tx_num = 0 |
| rx_num = 0 |
| path = ROOT_PATH + "/" + dev_name + "/queues" |
| if not os.path.exists(path): |
| print ("Net interface", dev_name, "does not exits.") |
| exit() |
| |
| list = os.listdir(path) |
| for s in list: |
| if s[0] == 'r': |
| rx_num += 1 |
| if s[0] == 't': |
| tx_num += 1 |
| |
| if tx_num > MAX_QUEUE_NUM or rx_num > MAX_QUEUE_NUM: |
| print ("number of queues over 1024 is not supported.") |
| exit() |
| |
| ################## start tracing ################## |
| b = BPF(src_file = EBPF_FILE) |
| # --------- set hash array -------- |
| devname_map = b['name_map'] |
| _name = Devname() |
| _name.name = dev_name.encode() |
| devname_map[0] = _name |
| |
| while 1: |
| try: |
| sleep(print_interval) |
| print_result(b) |
| except KeyboardInterrupt: |
| exit() |