#!/bin/env python3

import os
import sys
import argparse
from prettytable import *
import json

HPM_EVENTS= [
        ["Reserved", "Cycle count", "Retired instruction count", "Integer load instruction (includes LR)",
         "Integer store instruction (includes SC)", "Atomic memory operation (do not include LR and SC)",
         "System instruction", "Integer computational instruction(excluding multiplication/division/remainder)",
         "Conditional branch", "Taken conditional branch", "JAL instruction", "JALR instruction", "Return instruction",
         "Control transfer instruction (CBR+JAL+JALR)", "-", "Integer multiplication instruction",
         "Integer division/remainder instruction", "Floating-point load instruction", "Floating-point store instruction",
         "Floating-point addition/subtraction", "Floating-point multiplication",
         "Floating-point fused multiply-add (FMADD, FMSUB, FNMSUB, FNMADD)", "Floating-point division or square-root",
         "Other floating-point instruction", "Conditional branch prediction fail", "JAL prediction fail", "JALR prediction fail"],
        ["Reserved", "Icache miss", "Dcache miss", "ITLB miss", "DTLB miss", "Main TLB miss"]
]

HPM_MEVENT_ENABLE = 0x8
HPM_SEVENT_ENABLE = 0x4
HPM_UEVENT_ENABLE = 0x1


def get_hpm_evmode(ena):
    evmode = ""
    if ena & HPM_MEVENT_ENABLE:
        evmode += "M"
    if ena & HPM_SEVENT_ENABLE:
        evmode += "S"
    if ena & HPM_UEVENT_ENABLE:
        evmode += "U"
    return evmode

def get_hpm_event(sel, idx):
    if sel >= len(HPM_EVENTS):
        return "INVAILD"
    if idx >= len(HPM_EVENTS[sel]):
        return "INVAILD"
    return HPM_EVENTS[sel][idx]

def parse_hpm(logfile):
    if os.path.isfile(logfile) == False:
        return dict()
    hpmrecords = dict()
    with open(logfile, "r") as lf:
        for line in lf.readlines():
            if line.startswith("HPM"):
                hpmkey, proc, hpmcount = line.split(",")
                hpmkey = hpmkey.strip()
                proc = proc.strip()
                hpmcount = hpmcount.strip()
                if proc not in hpmrecords:
                    hpmrecords[proc] = dict()
                hpmrecords[proc][hpmkey] = hpmcount
    return hpmrecords

def analyze_hpm(records):
    x = PrettyTable()
    x.set_style(MARKDOWN)
    x.field_names = ["Process", "HPM", "Mode", "Event", "Counts"]
    parsed_hpm = dict()
    csv_hpm = [x.field_names]
    for proc in records:
        parsed_hpm[proc] = dict()
        for hpmkey in records[proc]:
            hpmcounter, hpmevent = hpmkey.split(":")
            hpmevent = int(hpmevent, 16)
            # event_sel: 3:0
            event_sel = hpmevent & 0xF
            # event_idx: 8:4
            event_idx = (hpmevent >> 4) & 0x1F
            # event_ena: m/rsv/s/u 31:28
            event_ena = (hpmevent >> 28) & 0xF
            event_name = get_hpm_event(event_sel, event_idx)
            event_mode = get_hpm_evmode(event_ena)
            event_count = int(records[proc][hpmkey])
            x.add_row([proc, hpmcounter, event_mode, event_name, event_count])
            csv_hpm.append([proc, hpmcounter, event_mode, event_name, event_count])
            if hpmcounter not in parsed_hpm[proc]:
                parsed_hpm[proc][hpmcounter] = list()
            parsed_hpm[proc][hpmcounter].append((event_mode, event_name, event_count))
    return parsed_hpm, csv_hpm, x

def save_csv(csvlist, csvfile):
    with open(csvfile, "w") as cf:
        for csvline in csvlist:
            line = ",".join(map(str, csvline))
            cf.write(line + "\n")
    pass

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Nuclei SDK HPM Log Parsing")
    parser.add_argument('--logfile', required=True, help="log file of hpm running")

    args = parser.parse_args()

    hpmrecords = parse_hpm(args.logfile)
    if len(hpmrecords) == 0:
        print("None records found in %s!" % (args.logfile))
        sys.exit(1)
    parsedhpm, csv_hpm, table = analyze_hpm(hpmrecords)
    print(table)

    hpmresult = {"records": hpmrecords, "parsed": parsedhpm}
    hpmrstfile = args.logfile + ".json"
    with open(hpmrstfile, "w") as cf:
        json.dump(hpmresult, cf, indent=4)
    print("\nParsed HPM event saved into %s" %(hpmrstfile))

    hpmcsvfile = args.logfile + ".csv"
    save_csv(csv_hpm, hpmcsvfile)
    print("Save HPM CSV to %s" % (hpmcsvfile))
    sys.exit(0)

