#!/usr/bin/env python

"""
Copyright (c) 2015-2018 Cray Inc. All Rights Reserved.
Utility to display rpc stats for a client_obd
"""

from pykdump.API import *
import argparse

import lustrelib as ll
from crashlib.input import toint

description_short = 'Dumps the rpc stats for a given client_obd'

OBD_HIST_MAX = 32

def get_cli_obd(client_obd):
    cli = None
    try:
        cli = readSU('struct client_obd', client_obd)
    except Exception as e:
        for dev in readSymbol('obd_devs'):
            try:
                if ll.obd2str(dev, 4) == client_obd:
                    cli = dev.u.cli
                    break
            except Exception as e:
                continue
    return cli

def pct(a, b):
    return 100 * a / b if b else 0

def lprocfs_oh_sum(oh):
    ret = 0
    for i in range(OBD_HIST_MAX):
        ret += oh.oh_buckets[i]
    return ret

def osc_rpc_stats_seq_show(client_obd):
    if not client_obd:
        print("invalid input for field 'client_obd'")
        return 1
    cli = readSU('struct client_obd', client_obd)
    print("read RPCs in flight:  %d" % cli.cl_r_in_flight)
    print("write RPCs in flight: %d" % cli.cl_w_in_flight)
    print("pending write pages:  %d" % cli.cl_pending_w_pages.counter)
    print("pending read pages:   %d" % cli.cl_pending_r_pages.counter)

    print("\n\t\t\tread\t\t\twrite")
    print("pages per rpc         rpcs   % cum % |       rpcs   % cum %\n")

    read_tot = lprocfs_oh_sum(cli.cl_read_page_hist)
    write_tot = lprocfs_oh_sum(cli.cl_write_page_hist)

    read_cum = 0
    write_cum = 0
    for i in range(OBD_HIST_MAX):
        r = cli.cl_read_page_hist.oh_buckets[i]
        w = cli.cl_write_page_hist.oh_buckets[i]

        read_cum += r
        write_cum += w
        print("%d:\t\t%10d %3d %3d   | %10d %3d %3d" % \
              (1 << i, r, pct(r, read_tot),
              pct(read_cum, read_tot), w,
              pct(w, write_tot),
              pct(write_cum, write_tot)))
        if read_cum == read_tot and write_cum == write_tot:
            break

    print("\n\t\t\tread\t\t\twrite")
    print("rpcs in flight        rpcs   % cum % |       rpcs   % cum %\n")

    read_tot = lprocfs_oh_sum(cli.cl_read_rpc_hist)
    write_tot = lprocfs_oh_sum(cli.cl_write_rpc_hist)

    read_cum = 0
    write_cum = 0
    for i in range(OBD_HIST_MAX):
        r = cli.cl_read_rpc_hist.oh_buckets[i]
        w = cli.cl_write_rpc_hist.oh_buckets[i]

        read_cum += r
        write_cum += w
        print("%d:\t\t%10d %3d %3d   | %10d %3d %3d" % \
              (i, r, pct(r, read_tot),
              pct(read_cum, read_tot), w,
              pct(w, write_tot),
              pct(write_cum, write_tot)))
        if read_cum == read_tot and write_cum == write_tot:
            break

    print("\n\t\t\tread\t\t\twrite")
    print("offset                rpcs   % cum % |       rpcs   % cum %\n")

    read_tot = lprocfs_oh_sum(cli.cl_read_offset_hist)
    write_tot = lprocfs_oh_sum(cli.cl_write_offset_hist)

    read_cum = 0
    write_cum = 0
    for i in range(OBD_HIST_MAX):
        r = cli.cl_read_offset_hist.oh_buckets[i]
        w = cli.cl_write_offset_hist.oh_buckets[i]

        read_cum += r
        write_cum += w
        offset = 0 if i == 0 else 1 << (i - 1)
        print("%d:      \t%10d %3d %3d   | %10d %3d %3d" % \
              (offset, r, pct(r, read_tot),
              pct(read_cum, read_tot), w,
              pct(w, write_tot),
              pct(write_cum, write_tot)))
        if read_cum == read_tot and write_cum == write_tot:
            break
    print()
    return 0

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=description_short)
    parser.add_argument("client_obd", nargs="?", default=[], type=toint,
        help="address of client_obd structure whose stats will be dumped")
    args = parser.parse_args()
    cli = get_cli_obd(args.client_obd)
    osc_rpc_stats_seq_show(cli)
