tools: acrntrace: Refactor vmexit_analyzer based on new trace format

- Refactor vmexit_analyzer based on new trace format (raw data). We can get
    the same output like original implementation.

  - Remove irq related analysis. Will add it back with an option "--irq".

  - Remove unused VMEXIT_EPT_VIOLATION_GVT

Signed-off-by: Kaige Fu <kaige.fu@intel.com>
Reviewed-by: Yan, Like <like.yan@intel.com>
Reviewed-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
This commit is contained in:
Kaige Fu 2018-06-25 23:09:28 +08:00 committed by lijinxia
parent 2bdd8112bd
commit 8a233eec63
2 changed files with 52 additions and 108 deletions

View File

@ -4,7 +4,6 @@
""" """
This is the main script of arnalyzer, which: This is the main script of arnalyzer, which:
- parse the options - parse the options
- pre-process the trace data file
- call a specific script to do analysis - call a specific script to do analysis
""" """
@ -46,22 +45,6 @@ def do_analysis(ifile, ofile, analyzer):
for alyer in analyzer: for alyer in analyzer:
alyer(ifile, ofile) alyer(ifile, ofile)
# pre process to make sure the trace data start and end with VMENTER
def pre_process(ifile, evstr):
"""invoke sh script to preprocess the data file
Args:
ifile: input trace data file
evstr: event string, after the processing, the data file should
start and end with the string
Returns:
status of sh script execution
Raises:
NA
"""
status = os.system('bash pre_process.sh %s %s' % (ifile, evstr))
return status
def main(argv): def main(argv):
"""Main enterance function """Main enterance function
@ -103,11 +86,7 @@ def main(argv):
assert outputfile != '', "output file is required" assert outputfile != '', "output file is required"
assert analyzer != '', 'MUST contain one of analyzer: ''vm_exit' assert analyzer != '', 'MUST contain one of analyzer: ''vm_exit'
status = pre_process(inputfile, 'VM_ENTER') do_analysis(inputfile, outputfile, analyzer)
if status == 0:
do_analysis(inputfile, outputfile, analyzer)
else:
print "Invalid trace data file %s" % (inputfile)
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv[1:]) main(sys.argv[1:])

View File

@ -6,36 +6,38 @@ This script defines the function to do the vm_exit analysis
""" """
import csv import csv
import struct
from config import TSC_FREQ from config import TSC_FREQ
TSC_BEGIN = 0L TSC_BEGIN = 0L
TSC_END = 0L TSC_END = 0L
TOTAL_NR_EXITS = 0L TOTAL_NR_EXITS = 0L
LIST_EVENTS = [ VM_EXIT = 0x10
'VMEXIT_EXCEPTION_OR_NMI', VM_ENTER = 0x11
'VMEXIT_EXTERNAL_INTERRUPT', VMEXIT_ENTRY = 0x10000
'VMEXIT_INTERRUPT_WINDOW',
'VMEXIT_CPUID', LIST_EVENTS = {
'VMEXIT_RDTSC', 'VMEXIT_EXCEPTION_OR_NMI': VMEXIT_ENTRY + 0x00000000,
'VMEXIT_VMCALL', 'VMEXIT_EXTERNAL_INTERRUPT': VMEXIT_ENTRY + 0x00000001,
'VMEXIT_CR_ACCESS', 'VMEXIT_INTERRUPT_WINDOW': VMEXIT_ENTRY + 0x00000002,
'VMEXIT_IO_INSTRUCTION', 'VMEXIT_CPUID': VMEXIT_ENTRY + 0x00000004,
'VMEXIT_RDMSR', 'VMEXIT_RDTSC': VMEXIT_ENTRY + 0x00000010,
'VMEXIT_WRMSR', 'VMEXIT_VMCALL': VMEXIT_ENTRY + 0x00000012,
'VMEXIT_APICV_ACCESS', 'VMEXIT_CR_ACCESS': VMEXIT_ENTRY + 0x0000001C,
'VMEXIT_APICV_VIRT_EOI', 'VMEXIT_IO_INSTRUCTION': VMEXIT_ENTRY + 0x0000001E,
'VMEXIT_EPT_VIOLATION', 'VMEXIT_RDMSR': VMEXIT_ENTRY + 0x0000001F,
'VMEXIT_EPT_VIOLATION_GVT', 'VMEXIT_WRMSR': VMEXIT_ENTRY + 0x00000020,
'VMEXIT_EPT_MISCONFIGURATION', 'VMEXIT_APICV_ACCESS': VMEXIT_ENTRY + 0x00000030,
'VMEXIT_RDTSCP', 'VMEXIT_APICV_VIRT_EOI': VMEXIT_ENTRY + 0x00000031,
'VMEXIT_APICV_WRITE', 'VMEXIT_EPT_VIOLATION': VMEXIT_ENTRY + 0x00000033,
'VMEXIT_UNHANDLED' 'VMEXIT_EPT_MISCONFIGURATION': VMEXIT_ENTRY + 0x00000038,
] 'VMEXIT_RDTSCP': VMEXIT_ENTRY + 0x00000039,
'VMEXIT_APICV_WRITE': VMEXIT_ENTRY + 0x0000003A,
'VMEXIT_UNHANDLED': 0x20000
}
NR_EXITS = { NR_EXITS = {
'VM_EXIT': 0,
'VM_ENTER': 0,
'VMEXIT_EXCEPTION_OR_NMI': 0, 'VMEXIT_EXCEPTION_OR_NMI': 0,
'VMEXIT_EXTERNAL_INTERRUPT': 0, 'VMEXIT_EXTERNAL_INTERRUPT': 0,
'VMEXIT_INTERRUPT_WINDOW': 0, 'VMEXIT_INTERRUPT_WINDOW': 0,
@ -49,7 +51,6 @@ NR_EXITS = {
'VMEXIT_APICV_ACCESS': 0, 'VMEXIT_APICV_ACCESS': 0,
'VMEXIT_APICV_VIRT_EOI': 0, 'VMEXIT_APICV_VIRT_EOI': 0,
'VMEXIT_EPT_VIOLATION': 0, 'VMEXIT_EPT_VIOLATION': 0,
'VMEXIT_EPT_VIOLATION_GVT': 0,
'VMEXIT_EPT_MISCONFIGURATION': 0, 'VMEXIT_EPT_MISCONFIGURATION': 0,
'VMEXIT_RDTSCP': 0, 'VMEXIT_RDTSCP': 0,
'VMEXIT_APICV_WRITE': 0, 'VMEXIT_APICV_WRITE': 0,
@ -57,8 +58,6 @@ NR_EXITS = {
} }
TIME_IN_EXIT = { TIME_IN_EXIT = {
'VM_EXIT': 0,
'VM_ENTER': 0,
'VMEXIT_EXCEPTION_OR_NMI': 0, 'VMEXIT_EXCEPTION_OR_NMI': 0,
'VMEXIT_EXTERNAL_INTERRUPT': 0, 'VMEXIT_EXTERNAL_INTERRUPT': 0,
'VMEXIT_INTERRUPT_WINDOW': 0, 'VMEXIT_INTERRUPT_WINDOW': 0,
@ -72,21 +71,14 @@ TIME_IN_EXIT = {
'VMEXIT_APICV_ACCESS': 0, 'VMEXIT_APICV_ACCESS': 0,
'VMEXIT_APICV_VIRT_EOI': 0, 'VMEXIT_APICV_VIRT_EOI': 0,
'VMEXIT_EPT_VIOLATION': 0, 'VMEXIT_EPT_VIOLATION': 0,
'VMEXIT_EPT_VIOLATION_GVT': 0,
'VMEXIT_EPT_MISCONFIGURATION': 0, 'VMEXIT_EPT_MISCONFIGURATION': 0,
'VMEXIT_RDTSCP': 0, 'VMEXIT_RDTSCP': 0,
'VMEXIT_APICV_WRITE': 0, 'VMEXIT_APICV_WRITE': 0,
'VMEXIT_UNHANDLED': 0 'VMEXIT_UNHANDLED': 0
} }
IRQ_EXITS = {} # 4 * 64bit per trace entry
TRCREC = "QQQQ"
def count_irq(info):
vec = info[5:15]
if IRQ_EXITS.has_key(vec):
IRQ_EXITS[vec] += 1
else:
IRQ_EXITS[vec] = 1
def parse_trace_data(ifile): def parse_trace_data(ifile):
"""parse the trace data file """parse the trace data file
@ -95,37 +87,25 @@ def parse_trace_data(ifile):
Return: Return:
None None
""" """
global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS global TSC_BEGIN, TSC_END, TOTAL_NR_EXITS
last_ev_id = ''
tsc_enter = 0L tsc_enter = 0L
tsc_exit = 0L tsc_exit = 0L
tsc_last_exit_period = 0L tsc_last_exit_period = 0L
ev_id = '' fd = open(ifile)
last_ev_id = ''
try: while True:
ifp = open(ifile) try:
line = fd.read(struct.calcsize(TRCREC))
line = ifp.readline() if not line:
# should preprocess to make sure first event is VM_ENTER
while 1:
line = ifp.readline()
if line == '':
ifp.close()
break break
(tsc, event, d1, d2) = struct.unpack(TRCREC, line)
try: event = event & 0xffffffffffff
(cpuid, tsc, payload) = line.split(" | ")
(ev_id, info) = payload.split(":") if event == VM_ENTER:
except ValueError, execp:
print execp
print line
continue
if ev_id == 'VM_ENTER':
if TSC_BEGIN == 0: if TSC_BEGIN == 0:
TSC_BEGIN = long(tsc) TSC_BEGIN = long(tsc)
tsc_exit = long(tsc) tsc_exit = long(tsc)
@ -134,38 +114,33 @@ def parse_trace_data(ifile):
tsc_enter = long(tsc) tsc_enter = long(tsc)
TSC_END = tsc_enter TSC_END = tsc_enter
tsc_last_exit_period = tsc_enter - tsc_exit tsc_last_exit_period = tsc_enter - tsc_exit
if tsc_last_exit_period != 0: if tsc_last_exit_period != 0:
TIME_IN_EXIT[last_ev_id] += tsc_last_exit_period TIME_IN_EXIT[last_ev_id] += tsc_last_exit_period
elif ev_id == 'VM_EXIT':
elif event == VM_EXIT:
tsc_exit = long(tsc) tsc_exit = long(tsc)
TSC_END = tsc_exit TSC_END = tsc_exit
TOTAL_NR_EXITS += 1 TOTAL_NR_EXITS += 1
elif ev_id.startswith('VMEXIT_'):
if (ev_id == 'VMEXIT_EPT_VIOLATION'
and (eval(info[6:24]) & 0x38) == 0x28):
ev_id = 'VMEXIT_EPT_VIOLATION_GVT'
if ev_id.startswith('VMEXIT_EX'):
count_irq(info)
NR_EXITS[ev_id] += 1
last_ev_id = ev_id
else: else:
# skip the non-VMEXIT trace event for key in LIST_EVENTS.keys():
pass if event == LIST_EVENTS.get(key):
NR_EXITS[key] += 1
last_ev_id = key
except IOError as err: else:
print "Input File Error: " + str(err) # Skip the non-VMEXIT trace event
pass
finally: except IOError, struct.error:
if 'ifp' in locals(): sys.exit()
ifp.close()
def generate_report(ofile, freq): def generate_report(ofile, freq):
""" generate analysis report """ generate analysis report
Args: Args:
ofile: output report ofile: output report
freq: CPU frequency of the device trace data from freq: TSC frequency of the device trace data from
Return: Return:
None None
""" """
@ -173,7 +148,7 @@ def generate_report(ofile, freq):
csv_name = ofile + '.csv' csv_name = ofile + '.csv'
try: try:
with open(csv_name, 'w') as filep: with open(csv_name, 'a') as filep:
f_csv = csv.writer(filep) f_csv = csv.writer(filep)
total_exit_time = 0L total_exit_time = 0L
@ -188,7 +163,7 @@ def generate_report(ofile, freq):
total_exit_time += TIME_IN_EXIT[event] total_exit_time += TIME_IN_EXIT[event]
print "Total run time: %d (cycles)" % (rt_cycle) print "Total run time: %d (cycles)" % (rt_cycle)
print "CPU Freq: %f MHz)" % (freq) print "TSC Freq: %f MHz)" % (freq)
print "Total run time %d (Sec)" % (rt_sec) print "Total run time %d (Sec)" % (rt_sec)
f_csv.writerow(['Run time(cycles)', 'Run time(Sec)', 'Freq(MHz)']) f_csv.writerow(['Run time(cycles)', 'Run time(Sec)', 'Freq(MHz)'])
@ -221,16 +196,6 @@ def generate_report(ofile, freq):
'%2.2f' % (pct)] '%2.2f' % (pct)]
f_csv.writerow(row) f_csv.writerow(row)
# insert a empty row to separate two tables
f_csv.writerow([''])
print "\nVector \t\tCount \tNR_Exit/Sec"
f_csv.writerow(['Vector', 'NR_Exit', 'NR_Exit/Sec'])
for e in IRQ_EXITS.keys():
pct = float(IRQ_EXITS[e]) / rt_sec
print "%s \t %d \t%.2f" % (e,IRQ_EXITS[e], pct)
f_csv.writerow([e, IRQ_EXITS[e], '%.2f' % pct])
except IOError as err: except IOError as err:
print "Output File Error: " + str(err) print "Output File Error: " + str(err)