123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- #!/usr/bin/env python
- # usage:callvalidator.py source=<src_input_dir> [file=<filename> | list=<listfilename>] [folder=<folder>]
- # tries to determine whether calls like gt 'location', 'event' are valid or not.
- from os import listdir
- from os.path import isfile, join
- import sys
- import re
- import io
- import time
- import xml.etree.ElementTree as ET
- idir = ''
- callList = []
- locationCallList = []
- neverCalledLocations = []
- callFileList = []
- calledFileList = []
- validate = 'all'
- def loadValidationList(validationListFile):
- result = []
- try:
- tree = ET.parse(validationListFile)
- root = tree.getroot()
- for file in root.iter('File'):
- result.append(join(file.attrib['folder'], file.attrib['name']))
- return result
- except:
- pass
- #endtry
- try:
- with io.open(validationListFile, 'r', encoding='utf-8') as ifile:
- lines = ifile.readlines()
- index = 1
- for line in lines:
- temp = line.strip(" \r\n\t").split(";")
- file = temp[0].strip(" \t")
- folder = idir
- if ".qsrc" not in file:
- raise SyntaxError("Incorrect file content: '<filename>[; <folder>] expected, found %s." % (index, line),(validationListFile, index, 1, 'if ".qsrc" not in temp[0].strip(" \t"):', index, len(line)))
- if len(temp) == 2:
- folder = temp[1].strip(" \t")
- result.append(join(folder,file))
- index += 1
- return result
- except SyntaxError as e:
- raise SystemExit("Invalid list filed: %s" % e.msg)
- #endtry
- #enddef
- def functionEmptyOrNamed(callArray):
- if len(callArray) < 3:
- return ''
- else:
- return callArray[2].strip(" \t\n")
- #endif
- #enddef
- def processLines(lines, fileName):
- findPattern = "(gt|gs|xgt|xgs)\s*('|\")\w+('|\")(,\s*('|\")\w+('|\"))?"
- blockEndPattern = "[^}]*}$"
- commentBlock = False
- lineNo = 0
- locationCalls = {"calllocation": lines[0].strip(' #\n'), "fileName": fileName, "calls": []}
- for line in lines:
- line = line.strip(" \t\n")
- if (not commentBlock) and line.strip("!").startswith("{"):
- commentBlock = True
- if commentBlock and (line == '}' or re.search(blockEndPattern, line) != None):
- commentBlock = False
- if not commentBlock and not line.startswith("!"):
- match = re.search(findPattern, line)
- if match != None:
- temp = match.group().replace('\t', '').replace(' ', '').replace("','","|").replace('","','|').strip(" '\n").replace("'","|").replace('"','|').split('|')
- temp = [t.strip(" '\"") for t in temp]
- if temp[1].lower() not in ['boystat', 'exp_gain']:
- calltype = temp[0]
- loc = temp[1]
- fun = functionEmptyOrNamed(temp)
- if ("%s.qsrc" % loc) not in calledFileList:
- calledFileList.append("%s.qsrc" % loc)
- calledLocation = {"location": loc, "function": fun, "valid": 0, "reason": "%s.qsrc doesn't exist." % loc}
- callLine = {"callId": 0, "calltype": calltype, "call": "%s '%s', '%s'" % (calltype, loc, fun), "lineNo": lineNo}
- if calledLocation not in callList:
- callList.append(calledLocation)
- callLine['callId'] = callList.index(calledLocation)
- locationCalls['calls'].append(callLine)
- #endif
- #endif
- #endif
- lineNo += 1
- #endfor
- locationCallList.append(locationCalls)
- #enddedf
- def validateCalls(lines):
- calls = [c for c in callList if c['location'].lower() == lines[0].strip(' #\n').lower()]
-
- if validate == 'all' and (calls == None or len(calls) == 0):
- neverCalledLocations.append(lines[0].strip(' #\n').lower())
- for call in calls:
- if call['function'] == 'start' or call['function'] == 'Start' or call['function'] == '':
- call['valid'] = 1
- call['reason'] = ""
- else:
- call['reason'] = "No $ARGS[0] or ARGS[i] expecting '%s'" % call['function']
- findPattern = "\$*(args|ARGS)\[(0|i)\]\s*=\s*('|\")%s('|\")" % call['function']
- for line in lines:
- match = re.search(findPattern, line)
- if match != None:
- call['valid'] = 1
- call['reason'] = ''
- break
- #endfor
- #endif
- #endfor
- #enddef
- runtime = [0,1,2,3,4,5,6,7,8,9,10,11,12]
- runtime[0] = time.perf_counter_ns()
- runtime_text = ["Setup","Build callfile","Build call list","Index call list","Validate calls","Validation finished","Build report [invalid calls]","Sort invalid calls","Build report [location list]","Sort location list","Generate report file","Build report file","Report finished, all done"]
- assert (len(sys.argv) == 2 or len(sys.argv) == 3 or len(sys.argv) == 4), "usage:\ncallvalidator.py source=<src_input_dir> [file=<filename> | list=<listfilename>] [folder=<folder>]"
- idir = str(sys.argv[1].replace("source=", ""))
- if len(sys.argv) == 4:
- vdir = str(sys.argv[3].replace("folder=", ""))
- else:
- vdir = idir
- validationTarget = ''
- runtime[1] = time.perf_counter_ns()
- if len(sys.argv) > 2 and "file=" in str(sys.argv[2]):
- validationTarget = sys.argv[2].replace("file=", "")
- callFileList = [join(vdir, sys.argv[2].replace("file=", ""))]
- validate = 'file'
-
- if len(sys.argv) > 2 and "list=" in str(sys.argv[2]):
- validationTarget = sys.argv[2].replace("list=", "")
- callFileList = loadValidationList(join(sys.argv[3].replace("folder=", ""), sys.argv[2].replace("list=", "")))
- validate = 'list'
- if validate == 'all':
- validationTarget = idir
- callFileList = [join(idir,f) for f in listdir(idir) if ".qsrc" in f]
- # build a list of all the calls happening
- runtime[2] = time.perf_counter_ns()
- for file in callFileList:
- with io.open(file, 'rt', encoding='utf-8') as ifile:
- lines = ifile.readlines()
- processLines(lines, ifile.name)
- #endwith
- #endfor
- runtime[3] = time.perf_counter_ns()
- for call in callList:
- call['callId'] = callList.index(call)
- runtime[4] = time.perf_counter_ns()
- # validating that all the calls are for valid locations
- for file in calledFileList:
- if file not in ['boyStat.qsrc', 'exp_gain.qsrc']:
- try:
- with io.open(join(idir, file), 'rt', encoding='utf-8') as ifile:
- lines = ifile.readlines()
- validateCalls(lines)
- except IOError:
- pass
- #endwith
- #endif
- #endfor
- runtime[5] = time.perf_counter_ns()
- # create the call validity file and a list of files that call invalid locations
- timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
- txtname = 'call_validity [%s] - %s.txt' % (validationTarget, time.strftime('%Y%m%d%H%M%S', time.localtime()))
- htmlname = 'call_validity [%s] - %s.html' % (validationTarget, time.strftime('%Y%m%d%H%M%S', time.localtime()))
- callheads = []
- callIds = []
- invalidCalls = []
- noInvLoc = 0
- noNonExistingLoc = 0
- noInvFun = 0
- noInvCall = 0
- noLocNeverCalled = len(neverCalledLocations)
- runtime[6] = time.perf_counter_ns()
- currLoc = ''
- for call in callList:
- if call['valid'] == 0:
- if currLoc != call['location']:
- noInvLoc += 1
- if ".qsrc doesn't exist" in call['reason']:
- noNonExistingLoc += 1
- callIds.append(call['callId'])
- invalidCalls.append(call)
- #enfif
- #endfor
- runtime[7] = time.perf_counter_ns()
- invalidCalls = sorted(invalidCalls, key=lambda k: (k['location'].lower(), k['function'].lower()))
- noInvFun = len(invalidCalls)
- runtime[8] = time.perf_counter_ns()
- for locationCall in locationCallList:
- calls = locationCall['calls']
- Ids = [call['callId'] for call in calls]
- if any(x in Ids for x in callIds):
- callheads.append(locationCall)
- #endfor
- #endfor
- runtime[9] = time.perf_counter_ns()
- callheads = sorted(callheads, key=lambda k: (k['calllocation'].lower()))
- noInvCall = len(callheads)
- runtime[10] = time.perf_counter_ns()
- location = ''
- txtInvalidLines = []
- for call in invalidCalls:
- if location != '' and location.lower() != call['location'].lower():
- txtInvalidLines.append('\n')
- txtInvalidLines.append(" '%s', '%s' : %s\n" % (call['location'], call['function'], call['reason']))
- location = call['location']
- #endfor
- txtLocationLines = []
- for head in callheads:
- txtLocationLines.append(" ---- %s [%s]:\n" % (head["calllocation"], head["fileName"]))
- for call in head['calls']:
- if call['callId'] in callIds:
- target = callList[call['callId']]
- txtLocationLines.append(" invalid call on line %04d: %s '%s', '%s'\t: %s\n" % (call['lineNo'], call['calltype'], target['location'], target['function'], target['reason']))
- #endif
- #endfor
- txtLocationLines.append('\n')
- #endfor
- runtime[11] = time.perf_counter_ns()
- try:
- with io.open(txtname, 'w', encoding='utf-8') as ofile:
- ofile.write("----- Summary -----\n")
- ofile.write(" Locations called incorrectly : %03d\n" % noInvLoc)
- ofile.write(" Non-existing locations called : %03d\n" % noNonExistingLoc)
- ofile.write(" Number of incorrect calls : %03d\n" % noInvFun)
- ofile.write(" Locations making incorrect calls: %03d\n" % noInvCall)
- if validate == 'all':
- ofile.write(" Locations never called : %03d\n" % noLocNeverCalled)
- ofile.write("\n")
- if validate == 'list':
- ofile.write("----- Validated files -----\n")
- if len(callFileList) > 10:
- ofile.write(" The list contains %d files. Please see the list in %s.\n" % (len(callFileList), validationTarget))
- else:
- for file in callFileList:
- ofile.write(" %s\n" % file)
- #endif
- ofile.write('\n')
- #endif
- ofile.write("----- List of Invalid calls -----\n")
- ofile.write("\n")
-
- for line in txtInvalidLines:
- ofile.write(line)
- ofile.write('\n')
- ofile.write("----- List of Locations and invalid calls they make -----\n")
- ofile.write('\n')
-
- for line in txtLocationLines:
- ofile.write(line)
- if validate == 'all':
- ofile.write("----- List of Locations that are never called -----\n")
- for line in neverCalledLocations:
- ofile.write("%s\n" % line)
- #endwith
- except IOError as e:
- raise SystemExit("ERROR: call validity file was not created! REASON: %s" % e.strerror)
- #endtry_except
- runtime[12] = time.perf_counter_ns()
- max = len(runtime)
- index = 0
- while index < max:
- print("%s: %s" % (runtime_text[index], runtime[index]))
- index += 1
|