1#!/usr/bin/python3 2# -*- coding: UTF-8 -*- 3 4""" 5This script defines the function to do the vm_exit analysis 6""" 7 8import csv 9import struct 10 11TSC_BEGIN = 0 12TSC_END = 0 13TOTAL_NR_EXITS = 0 14 15VM_EXIT = 0x10 16VM_ENTER = 0x11 17VMEXIT_ENTRY = 0x10000 18 19LIST_EVENTS = { 20 'VMEXIT_EXCEPTION_OR_NMI': VMEXIT_ENTRY + 0x00000000, 21 'VMEXIT_EXTERNAL_INTERRUPT': VMEXIT_ENTRY + 0x00000001, 22 'VMEXIT_INTERRUPT_WINDOW': VMEXIT_ENTRY + 0x00000002, 23 'VMEXIT_CPUID': VMEXIT_ENTRY + 0x00000004, 24 'VMEXIT_RDTSC': VMEXIT_ENTRY + 0x00000010, 25 'VMEXIT_VMCALL': VMEXIT_ENTRY + 0x00000012, 26 'VMEXIT_CR_ACCESS': VMEXIT_ENTRY + 0x0000001C, 27 'VMEXIT_IO_INSTRUCTION': VMEXIT_ENTRY + 0x0000001E, 28 'VMEXIT_RDMSR': VMEXIT_ENTRY + 0x0000001F, 29 'VMEXIT_WRMSR': VMEXIT_ENTRY + 0x00000020, 30 'VMEXIT_EPT_VIOLATION': VMEXIT_ENTRY + 0x00000030, 31 'VMEXIT_EPT_MISCONFIGURATION': VMEXIT_ENTRY + 0x00000031, 32 'VMEXIT_RDTSCP': VMEXIT_ENTRY + 0x00000033, 33 'VMEXIT_APICV_WRITE': VMEXIT_ENTRY + 0x00000038, 34 'VMEXIT_APICV_ACCESS': VMEXIT_ENTRY + 0x00000039, 35 'VMEXIT_APICV_VIRT_EOI': VMEXIT_ENTRY + 0x0000003A, 36 'VMEXIT_UNHANDLED': 0x20000 37} 38 39NR_EXITS = { 40 'VMEXIT_EXCEPTION_OR_NMI': 0, 41 'VMEXIT_EXTERNAL_INTERRUPT': 0, 42 'VMEXIT_INTERRUPT_WINDOW': 0, 43 'VMEXIT_CPUID': 0, 44 'VMEXIT_RDTSC': 0, 45 'VMEXIT_VMCALL': 0, 46 'VMEXIT_CR_ACCESS': 0, 47 'VMEXIT_IO_INSTRUCTION': 0, 48 'VMEXIT_RDMSR': 0, 49 'VMEXIT_WRMSR': 0, 50 'VMEXIT_APICV_ACCESS': 0, 51 'VMEXIT_APICV_VIRT_EOI': 0, 52 'VMEXIT_EPT_VIOLATION': 0, 53 'VMEXIT_EPT_MISCONFIGURATION': 0, 54 'VMEXIT_RDTSCP': 0, 55 'VMEXIT_APICV_WRITE': 0, 56 'VMEXIT_UNHANDLED': 0 57} 58 59TIME_IN_EXIT = { 60 'VMEXIT_EXCEPTION_OR_NMI': 0, 61 'VMEXIT_EXTERNAL_INTERRUPT': 0, 62 'VMEXIT_INTERRUPT_WINDOW': 0, 63 'VMEXIT_CPUID': 0, 64 'VMEXIT_RDTSC': 0, 65 'VMEXIT_VMCALL': 0, 66 'VMEXIT_CR_ACCESS': 0, 67 'VMEXIT_IO_INSTRUCTION': 0, 68 'VMEXIT_RDMSR': 0, 69 'VMEXIT_WRMSR': 0, 70 'VMEXIT_APICV_ACCESS': 0, 71 'VMEXIT_APICV_VIRT_EOI': 0, 72 'VMEXIT_EPT_VIOLATION': 0, 73 'VMEXIT_EPT_MISCONFIGURATION': 0, 74 'VMEXIT_RDTSCP': 0, 75 'VMEXIT_APICV_WRITE': 0, 76 'VMEXIT_UNHANDLED': 0 77} 78 79# 4 * 64bit per trace entry 80TRCREC = "QQQQ" 81 82def parse_trace_data(ifile): 83 """parse the trace data file 84 Args: 85 ifile: input trace data file 86 Return: 87 None 88 """ 89 90 global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS 91 last_ev_id = '' 92 tsc_exit = 0 93 94 fd = open(ifile, 'rb') 95 96 # The duration of one vmexit is tsc_enter - tsc_exit 97 # Here we should find the first vmexit and ignore other entries on top of the first vmexit 98 while True: 99 try: 100 line = fd.read(struct.calcsize(TRCREC)) 101 if not line: 102 break 103 (tsc, event, d1, d2) = struct.unpack(TRCREC, line) 104 event = event & 0xffffffffffff 105 106 if event != VM_EXIT: 107 continue 108 109 # We found the first vmexit and should seek back one line as we will read it in the following loop 110 TSC_BEGIN = tsc 111 fd.seek(fd.tell() - len(line)) 112 break 113 114 except (IOError, struct.error) as e: 115 sys.exit() 116 117 while True: 118 try: 119 line = fd.read(struct.calcsize(TRCREC)) 120 if not line: 121 break 122 (tsc, event, d1, d2) = struct.unpack(TRCREC, line) 123 124 event = event & 0xffffffffffff 125 126 if event == VM_ENTER: 127 TSC_END = tsc 128 # Found one vmenter in pair with the last vmexit 129 TIME_IN_EXIT[last_ev_id] += tsc - tsc_exit 130 131 elif event == VM_EXIT: 132 tsc_exit = tsc 133 TOTAL_NR_EXITS += 1 134 135 else: 136 for key in LIST_EVENTS.keys(): 137 if event == LIST_EVENTS.get(key): 138 NR_EXITS[key] += 1 139 last_ev_id = key 140 break 141 142 else: 143 # Skip the non-VMEXIT trace event 144 pass 145 146 except (IOError, struct.error) as e: 147 sys.exit() 148 149def generate_report(ofile, freq): 150 """ generate analysis report 151 Args: 152 ofile: output report 153 freq: TSC frequency of the device trace data from 154 Return: 155 None 156 """ 157 global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS 158 159 csv_name = ofile + '.csv' 160 try: 161 with open(csv_name, 'a') as filep: 162 f_csv = csv.writer(filep) 163 164 total_exit_time = 0 165 rt_cycle = TSC_END - TSC_BEGIN 166 assert rt_cycle != 0, "total_run_time in cycle is 0,\ 167 tsc_end %d, tsc_begin %d"\ 168 % (TSC_END, TSC_BEGIN) 169 170 rt_sec = float(rt_cycle) / (float(freq) * 1000 * 1000) 171 172 for event in LIST_EVENTS: 173 total_exit_time += TIME_IN_EXIT[event] 174 175 print ("Total run time: %d cycles" % (rt_cycle)) 176 print ("TSC Freq: %s MHz" % (freq)) 177 print ("Total run time: %.6f sec" % (rt_sec)) 178 179 f_csv.writerow(['Run time(cycles)', 'Run time(Sec)', 'Freq(MHz)']) 180 f_csv.writerow(['%d' % (rt_cycle), 181 '%.3f' % (rt_sec), 182 '%s' % (freq)]) 183 184 print ("%-28s\t%-12s\t%-12s\t%-24s\t%-16s" % ("Event", "NR_Exit", 185 "NR_Exit/Sec", "Time Consumed(cycles)", "Time percentage")) 186 f_csv.writerow(['Exit_Reason', 187 'NR_Exit', 188 'NR_Exit/Sec', 189 'Time Consumed(cycles)', 190 'Time Percentage']) 191 192 for event in LIST_EVENTS: 193 ev_freq = float(NR_EXITS[event]) / rt_sec 194 pct = float(TIME_IN_EXIT[event]) * 100 / float(rt_cycle) 195 196 print ("%-28s\t%-12d\t%-12.2f\t%-24d\t%-16.2f" % 197 (event, NR_EXITS[event], ev_freq, TIME_IN_EXIT[event], pct)) 198 row = [event, NR_EXITS[event], '%.2f' % ev_freq, TIME_IN_EXIT[event], 199 '%2.2f' % (pct)] 200 f_csv.writerow(row) 201 202 ev_freq = float(TOTAL_NR_EXITS) / rt_sec 203 pct = float(total_exit_time) * 100 / float(rt_cycle) 204 print("%-28s\t%-12d\t%-12.2f\t%-24d\t%-16.2f" 205 % ("Total", TOTAL_NR_EXITS, ev_freq, total_exit_time, pct)) 206 row = ["Total", TOTAL_NR_EXITS, '%.2f' % ev_freq, total_exit_time, 207 '%2.2f' % (pct)] 208 f_csv.writerow(row) 209 210 except IOError as err: 211 print ("Output File Error: " + str(err)) 212 213def analyze_vm_exit(ifile, ofile, freq): 214 """do the vm exits analysis 215 Args: 216 ifile: input trace data file 217 ofile: output report file 218 freq: TSC frequency of the host where we capture the trace data 219 Return: 220 None 221 """ 222 223 print("VM exits analysis started... \n\tinput file: %s\n" 224 "\toutput file: %s.csv" % (ifile, ofile)) 225 226 parse_trace_data(ifile) 227 # save report to the output file 228 generate_report(ofile, freq) 229