1#!/usr/bin/python3 2# -*- coding: UTF-8 -*- 3 4""" 5This script defines the function to do cpu usage analysis 6""" 7import sys 8import string 9import struct 10import csv 11import os 12 13cpu_id = 0 14stat_tsc = 0 15 16# map event TRACE_SCHED_NEXT defined in file trace.h 17SCHED_NEXT = 0x20 18 19# max number of vm is 16, another 1 is for hv 20VM_NUM = 16 21time_vm_running = [0] * (VM_NUM + 1) 22count_all_trace = 0 23count_sched_trace = 0 24 25# Q: 64-bit tsc, Q: 64-bit event, 16c: char name[16] 26TRCREC = "QQ16c" 27 28def process_trace_data(ifile): 29 """parse the trace data file 30 Args: 31 ifile: input trace data file 32 Return: 33 None 34 """ 35 36 global stat_tsc, cpu_id, time_vm_running, count_all_trace, count_sched_trace 37 38 # The duration of cpu running is tsc_end - tsc_begin 39 tsc_begin = 0 40 tsc_end = 0 41 time_ambiguous = 0 42 43 fd = open(ifile, 'rb') 44 while True: 45 try: 46 count_all_trace += 1 47 line = fd.read(struct.calcsize(TRCREC)) 48 if not line: 49 break 50 x = struct.unpack(TRCREC, line) 51 if x[0] == 0: 52 break 53 tsc_end = x[0] 54 if count_all_trace == 1: 55 tsc_begin = tsc_end 56 tsc_last_sched = tsc_begin 57 event = x[1] 58 cpu_id = event >> 56 59 event = event & 0xffffffffffff 60 if event == SCHED_NEXT: 61 count_sched_trace += 1 62 s='' 63 for _ in list(x[2:]): 64 d = _.decode('ascii', errors='ignore') 65 s += d 66 67 if s[:2] == "vm": 68 vm_prev = int(s[2]) 69 if s[3] != ":": 70 vm_prev = vm_prev*10 + int(s[3]) 71 elif s[:4] == "idle": 72 vm_prev = VM_NUM 73 else: 74 print("Error: trace data is not correct!") 75 return 76 77 if s[4:6] == "vm": 78 vm_next = int(s[6]) 79 if s[7] != ":": 80 vm_next = vm_next*10 + int(s[7]) 81 elif s[4:8] == "idle": 82 vm_next = VM_NUM 83 else: 84 print("Error: trace data is not correct!") 85 return 86 87 if (count_sched_trace == 1) or (vm_prev == vm_prev_last): 88 time_vm_running[vm_prev] += tsc_end - tsc_last_sched 89 else: 90 print("line %d: last_next =vm%d, current_prev=vm%d" % (count_all_trace, vm_prev, vm_prev_last)) 91 print("Warning: last schedule next is not the current task. Trace log is lost.") 92 time_ambiguous += tsc_end - tsc_last_sched 93 94 tsc_last_sched = tsc_end 95 vm_prev_last = vm_next 96 97 except (IOError, struct.error) as e: 98 sys.exit() 99 100 print ("Start trace %d tsc cycle" % (tsc_begin)) 101 print ("End trace %d tsc cycle" % (tsc_end)) 102 stat_tsc = tsc_end - tsc_begin 103 assert stat_tsc != 0, "total_run_time in statistic is 0,\ 104 tsc_end %d, tsc_begin %d"\ 105 % (tsc_end, tsc_begin) 106 107 if count_sched_trace == 0: 108 print ("There is no context switch in HV scheduling during this period. " 109 "This CPU may be exclusively owned by one vm.\n" 110 "The CPU usage is 100%") 111 return 112 if time_ambiguous > 0: 113 print("Warning: ambiguous running time: %d tsc cycle, occupying %2.2f%% cpu." 114 % (time_ambiguous, float(time_ambiguous)*100/stat_tsc)) 115 116 # the last time 117 time_vm_running[vm_next] += tsc_end - tsc_last_sched 118 119 120def generate_report(ofile, freq): 121 """ generate analysis report 122 Args: 123 ofile: output report 124 freq: TSC frequency of the device trace data from 125 Return: 126 None 127 """ 128 global stat_tsc, cpu_id, time_vm_running, count_all_trace, count_sched_trace 129 130 if (count_sched_trace == 0): 131 return 132 133 csv_name = ofile + '.csv' 134 try: 135 with open(csv_name, 'a') as filep: 136 f_csv = csv.writer(filep) 137 138 stat_sec = float(stat_tsc) / (float(freq) * 1000 * 1000) 139 print ("Total run time: %d cpu cycles" % (stat_tsc)) 140 print ("TSC Freq: %s MHz" % (freq)) 141 print ("Total run time: %.2f sec" % (stat_sec)) 142 print ("Total trace items: %d" % (count_all_trace)) 143 print ("Total scheduling trace: %d" % (count_sched_trace)) 144 145 f_csv.writerow(['Total run time(tsc cycles)', 'Total run time(sec)', 'Freq(MHz)']) 146 f_csv.writerow(['%d' % (stat_tsc), 147 '%.2f' % (stat_sec), 148 '%s' % (freq)]) 149 150 print ("%-28s\t%-12s\t%-12s\t%-24s\t%-16s" % ("PCPU ID", "VM ID", 151 "VM Running/sec", "VM Running(tsc cycles)", "CPU Usage")) 152 f_csv.writerow(['PCPU ID', 153 'VM_ID', 154 'Time Consumed/sec', 155 'Time Consumed(tsc cycles)', 156 'CPU Usage%']) 157 158 for vmid, tsc in enumerate(time_vm_running): 159 run_tsc = tsc 160 run_per = float(run_tsc) * 100 / float(stat_tsc) 161 run_sec = float(run_tsc) / (float(freq) * 1000 * 1000) 162 if vmid != VM_NUM: 163 print ("%-28d\t%-12d\t%-10.2f\t%-24d\t%-2.2f%%" % 164 (cpu_id, vmid, run_sec, run_tsc, run_per)) 165 row = [cpu_id, vmid, '%.2f' % (run_sec), run_tsc, '%2.2f' % (run_per)] 166 else: 167 print ("%-28d\t%-12s\t%-10.2f\t%-24d\t%-2.2f%%" % 168 (cpu_id, 'Idle', run_sec, run_tsc, run_per)) 169 row = [cpu_id, 'Idle', '%.2f' % (run_sec), run_tsc, '%2.2f' % (run_per)] 170 f_csv.writerow(row) 171 172 except IOError as err: 173 print ("Output File Error: " + str(err)) 174 175def analyze_cpu_usage(ifile, ofile, freq): 176 """do cpu usage analysis of each vm 177 Args: 178 ifile: input trace data file 179 ofile: output report file 180 freq: TSC frequency of the host where we capture the trace data 181 Return: 182 None 183 """ 184 185 print("VM CPU usage analysis started... \n\tinput file: %s\n" 186 "\toutput file: %s.csv" % (ifile, ofile)) 187 188 if os.stat(ifile).st_size == 0: 189 print("The input trace file is empty. The corresponding CPU may be offline.") 190 return 191 if float(freq) == 0.0: 192 print("The input cpu frequency cannot be zero!") 193 return 194 process_trace_data(ifile) 195 # print cpu usage of each vm 196 generate_report(ofile, freq) 197