#!/usr/bin/python3 # -*- coding: UTF-8 -*- """ This script defines the function to do the vm_exit analysis """ import csv import struct from config import TSC_FREQ TSC_BEGIN = 0 TSC_END = 0 TOTAL_NR_EXITS = 0 VM_EXIT = 0x10 VM_ENTER = 0x11 VMEXIT_ENTRY = 0x10000 LIST_EVENTS = { 'VMEXIT_EXCEPTION_OR_NMI': VMEXIT_ENTRY + 0x00000000, 'VMEXIT_EXTERNAL_INTERRUPT': VMEXIT_ENTRY + 0x00000001, 'VMEXIT_INTERRUPT_WINDOW': VMEXIT_ENTRY + 0x00000002, 'VMEXIT_CPUID': VMEXIT_ENTRY + 0x00000004, 'VMEXIT_RDTSC': VMEXIT_ENTRY + 0x00000010, 'VMEXIT_VMCALL': VMEXIT_ENTRY + 0x00000012, 'VMEXIT_CR_ACCESS': VMEXIT_ENTRY + 0x0000001C, 'VMEXIT_IO_INSTRUCTION': VMEXIT_ENTRY + 0x0000001E, 'VMEXIT_RDMSR': VMEXIT_ENTRY + 0x0000001F, 'VMEXIT_WRMSR': VMEXIT_ENTRY + 0x00000020, 'VMEXIT_EPT_VIOLATION': VMEXIT_ENTRY + 0x00000030, 'VMEXIT_EPT_MISCONFIGURATION': VMEXIT_ENTRY + 0x00000031, 'VMEXIT_RDTSCP': VMEXIT_ENTRY + 0x00000033, 'VMEXIT_APICV_WRITE': VMEXIT_ENTRY + 0x00000038, 'VMEXIT_APICV_ACCESS': VMEXIT_ENTRY + 0x00000039, 'VMEXIT_APICV_VIRT_EOI': VMEXIT_ENTRY + 0x0000003A, 'VMEXIT_UNHANDLED': 0x20000 } NR_EXITS = { 'VMEXIT_EXCEPTION_OR_NMI': 0, 'VMEXIT_EXTERNAL_INTERRUPT': 0, 'VMEXIT_INTERRUPT_WINDOW': 0, 'VMEXIT_CPUID': 0, 'VMEXIT_RDTSC': 0, 'VMEXIT_VMCALL': 0, 'VMEXIT_CR_ACCESS': 0, 'VMEXIT_IO_INSTRUCTION': 0, 'VMEXIT_RDMSR': 0, 'VMEXIT_WRMSR': 0, 'VMEXIT_APICV_ACCESS': 0, 'VMEXIT_APICV_VIRT_EOI': 0, 'VMEXIT_EPT_VIOLATION': 0, 'VMEXIT_EPT_MISCONFIGURATION': 0, 'VMEXIT_RDTSCP': 0, 'VMEXIT_APICV_WRITE': 0, 'VMEXIT_UNHANDLED': 0 } TIME_IN_EXIT = { 'VMEXIT_EXCEPTION_OR_NMI': 0, 'VMEXIT_EXTERNAL_INTERRUPT': 0, 'VMEXIT_INTERRUPT_WINDOW': 0, 'VMEXIT_CPUID': 0, 'VMEXIT_RDTSC': 0, 'VMEXIT_VMCALL': 0, 'VMEXIT_CR_ACCESS': 0, 'VMEXIT_IO_INSTRUCTION': 0, 'VMEXIT_RDMSR': 0, 'VMEXIT_WRMSR': 0, 'VMEXIT_APICV_ACCESS': 0, 'VMEXIT_APICV_VIRT_EOI': 0, 'VMEXIT_EPT_VIOLATION': 0, 'VMEXIT_EPT_MISCONFIGURATION': 0, 'VMEXIT_RDTSCP': 0, 'VMEXIT_APICV_WRITE': 0, 'VMEXIT_UNHANDLED': 0 } # 4 * 64bit per trace entry TRCREC = "QQQQ" def parse_trace_data(ifile): """parse the trace data file Args: ifile: input trace data file Return: None """ global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS last_ev_id = '' tsc_enter = 0 tsc_exit = 0 tsc_last_exit_period = 0 fd = open(ifile, 'rb') while True: try: line = fd.read(struct.calcsize(TRCREC)) if not line: break (tsc, event, d1, d2) = struct.unpack(TRCREC, line) event = event & 0xffffffffffff if event == VM_ENTER: if TSC_BEGIN == 0: TSC_BEGIN = tsc tsc_exit = tsc TOTAL_NR_EXITS = 0 tsc_enter = tsc TSC_END = tsc_enter tsc_last_exit_period = tsc_enter - tsc_exit if tsc_last_exit_period != 0: TIME_IN_EXIT[last_ev_id] += tsc_last_exit_period elif event == VM_EXIT: tsc_exit = tsc TSC_END = tsc_exit TOTAL_NR_EXITS += 1 else: for key in LIST_EVENTS.keys(): if event == LIST_EVENTS.get(key): NR_EXITS[key] += 1 last_ev_id = key else: # Skip the non-VMEXIT trace event pass except (IOError, struct.error) as e: sys.exit() def generate_report(ofile, freq): """ generate analysis report Args: ofile: output report freq: TSC frequency of the device trace data from Return: None """ global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS csv_name = ofile + '.csv' try: with open(csv_name, 'a') as filep: f_csv = csv.writer(filep) total_exit_time = 0 rt_cycle = TSC_END - TSC_BEGIN assert rt_cycle != 0, "total_run_time in cycle is 0,\ tsc_end %d, tsc_begin %d"\ % (TSC_END, TSC_BEGIN) rt_sec = float(rt_cycle) / (float(freq) * 1000 * 1000) for event in LIST_EVENTS: total_exit_time += TIME_IN_EXIT[event] print ("Total run time: %d cycles" % (rt_cycle)) print ("TSC Freq: %f MHz" % (freq)) print ("Total run time: %d sec" % (rt_sec)) f_csv.writerow(['Run time(cycles)', 'Run time(Sec)', 'Freq(MHz)']) f_csv.writerow(['%d' % (rt_cycle), '%.3f' % (rt_sec), '%d' % (freq)]) print ("Event \tNR_Exit \tNR_Exit/Sec \tTime Consumed \tTime Percentage") f_csv.writerow(['Exit_Reason', 'NR_Exit', 'NR_Exit/Sec', 'Time Consumed(cycles)', 'Time Percentage']) for event in LIST_EVENTS: ev_freq = float(NR_EXITS[event]) / rt_sec pct = float(TIME_IN_EXIT[event]) * 100 / float(rt_cycle) print ("%s \t%d \t%.2f \t%d \t%2.2f" % (event, NR_EXITS[event], ev_freq, TIME_IN_EXIT[event], pct)) row = [event, NR_EXITS[event], '%.2f' % ev_freq, TIME_IN_EXIT[event], '%2.2f' % (pct)] f_csv.writerow(row) ev_freq = float(TOTAL_NR_EXITS) / rt_sec pct = float(total_exit_time) * 100 / float(rt_cycle) print("Total \t%d \t%.2f \t%d \t%2.2f" % (TOTAL_NR_EXITS, ev_freq, total_exit_time, pct)) row = ["Total", TOTAL_NR_EXITS, '%.2f' % ev_freq, total_exit_time, '%2.2f' % (pct)] f_csv.writerow(row) except IOError as err: print ("Output File Error: " + str(err)) def analyze_vm_exit(ifile, ofile): """do the vm exits analysis Args: ifile: input trace data file ofile: output report file Return: None """ print("VM exits analysis started... \n\tinput file: %s\n" "\toutput file: %s.csv" % (ifile, ofile)) parse_trace_data(ifile) # save report to the output file generate_report(ofile, TSC_FREQ)