processResult.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. # Process the test results
  2. # Test status (like passed, or failed with error code)
  3. import argparse
  4. import re
  5. import TestScripts.NewParser as parse
  6. import TestScripts.CodeGen
  7. from collections import deque
  8. import os.path
  9. import csv
  10. def findItem(root,path):
  11. """ Find a node in a tree
  12. Args:
  13. path (list) : A list of node ID
  14. This list is describing a path in the tree.
  15. By starting from the root and following this path,
  16. we can find the node in the tree.
  17. Raises:
  18. Nothing
  19. Returns:
  20. TreeItem : A node
  21. """
  22. # The list is converted into a queue.
  23. q = deque(path)
  24. q.popleft()
  25. c = root
  26. while q:
  27. n = q.popleft()
  28. # We get the children based on its ID and continue
  29. c = c[n-1]
  30. return(c)
  31. def joinit(iterable, delimiter):
  32. # Intersperse a delimiter between element of a list
  33. it = iter(iterable)
  34. yield next(it)
  35. for x in it:
  36. yield delimiter
  37. yield x
  38. # Return test result as a text tree
  39. class TextFormatter:
  40. def start(self):
  41. None
  42. def printGroup(self,elem,theId):
  43. if elem is None:
  44. elem = root
  45. message=elem.data["message"]
  46. if not elem.data["deprecated"]:
  47. kind = "Suite"
  48. ident = " " * elem.ident
  49. if elem.kind == TestScripts.Parser.TreeElem.GROUP:
  50. kind = "Group"
  51. #print(elem.path)
  52. print("%s%s : %s (%d)" % (ident,kind,message,theId))
  53. def printTest(self,elem, theId, theError,theLine,passed,cycles,params):
  54. message=elem.data["message"]
  55. if not elem.data["deprecated"]:
  56. kind = "Test"
  57. ident = " " * elem.ident
  58. p="FAILED"
  59. if passed == 1:
  60. p="PASSED"
  61. print("%s%s (%d) : %s (cycles = %d)" % (ident,message,theId,p,cycles))
  62. if params:
  63. print("%s %s" % (ident,params))
  64. if passed != 1:
  65. print("%s Error = %d at line %d" % (ident, theError, theLine))
  66. def pop(self):
  67. None
  68. def end(self):
  69. None
  70. # Return test result as a CSV
  71. class CSVFormatter:
  72. def __init__(self):
  73. self.name=[]
  74. self._start=True
  75. def start(self):
  76. print("CATEGORY,NAME,ID,STATUS,CYCLES,PARAMS")
  77. def printGroup(self,elem,theId):
  78. if elem is None:
  79. elem = root
  80. # Remove Root from category name in CSV file.
  81. if not self._start:
  82. self.name.append(elem.data["class"])
  83. else:
  84. self._start=False
  85. message=elem.data["message"]
  86. if not elem.data["deprecated"]:
  87. kind = "Suite"
  88. ident = " " * elem.ident
  89. if elem.kind == TestScripts.Parser.TreeElem.GROUP:
  90. kind = "Group"
  91. def printTest(self,elem, theId, theError, theLine,passed,cycles,params):
  92. message=elem.data["message"]
  93. if not elem.data["deprecated"]:
  94. kind = "Test"
  95. name=elem.data["class"]
  96. category= "".join(list(joinit(self.name,":")))
  97. print("%s,%s,%d,%d,%d,\"%s\"" % (category,name,theId,passed,cycles,params))
  98. def pop(self):
  99. if self.name:
  100. self.name.pop()
  101. def end(self):
  102. None
  103. class MathematicaFormatter:
  104. def __init__(self):
  105. self._hasContent=[False]
  106. self._toPop=[]
  107. def start(self):
  108. None
  109. def printGroup(self,elem,theId):
  110. if self._hasContent[len(self._hasContent)-1]:
  111. print(",",end="")
  112. print("<|")
  113. self._hasContent[len(self._hasContent)-1] = True
  114. self._hasContent.append(False)
  115. if elem is None:
  116. elem = root
  117. message=elem.data["message"]
  118. if not elem.data["deprecated"]:
  119. kind = "Suite"
  120. ident = " " * elem.ident
  121. if elem.kind == TestScripts.Parser.TreeElem.GROUP:
  122. kind = "Group"
  123. print("\"%s\" ->" % (message))
  124. #if kind == "Suite":
  125. print("{",end="")
  126. self._toPop.append("}")
  127. #else:
  128. # self._toPop.append("")
  129. def printTest(self,elem, theId, theError,theLine,passed,cycles,params):
  130. message=elem.data["message"]
  131. if not elem.data["deprecated"]:
  132. kind = "Test"
  133. ident = " " * elem.ident
  134. p="FAILED"
  135. if passed == 1:
  136. p="PASSED"
  137. parameters=""
  138. if params:
  139. parameters = "%s" % params
  140. if self._hasContent[len(self._hasContent)-1]:
  141. print(",",end="")
  142. print("<|\"NAME\" -> \"%s\",\"ID\" -> %d,\"STATUS\" -> \"%s\",\"CYCLES\" -> %d,\"PARAMS\" -> \"%s\"|>" % (message,theId,p,cycles,parameters))
  143. self._hasContent[len(self._hasContent)-1] = True
  144. #if passed != 1:
  145. # print("%s Error = %d at line %d" % (ident, theError, theLine))
  146. def pop(self):
  147. print(self._toPop.pop(),end="")
  148. print("|>")
  149. self._hasContent.pop()
  150. def end(self):
  151. None
  152. NORMAL = 1
  153. INTEST = 2
  154. TESTPARAM = 3
  155. def createMissingDir(destPath):
  156. theDir=os.path.normpath(os.path.dirname(destPath))
  157. if not os.path.exists(theDir):
  158. os.makedirs(theDir)
  159. def correctPath(path):
  160. while (path[0]=="/") or (path[0] == "\\"):
  161. path = path[1:]
  162. return(path)
  163. def extractDataFiles(results,outputDir):
  164. infile = False
  165. f = None
  166. for l in results:
  167. if re.match(r'^.*D:[ ].*$',l):
  168. if infile:
  169. if re.match(r'^.*D:[ ]END$',l):
  170. infile = False
  171. if f:
  172. f.close()
  173. else:
  174. if f:
  175. m = re.match(r'^.*D:[ ](.*)$',l)
  176. data = m.group(1)
  177. f.write(data)
  178. f.write("\n")
  179. else:
  180. m = re.match(r'^.*D:[ ](.*)$',l)
  181. path = str(m.group(1))
  182. infile = True
  183. destPath = os.path.join(outputDir,correctPath(path))
  184. createMissingDir(destPath)
  185. f = open(destPath,"w")
  186. def writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config):
  187. if benchFile:
  188. name=elem.data["class"]
  189. category= elem.categoryDesc()
  190. old=""
  191. if "testData" in elem.data:
  192. if "oldID" in elem.data["testData"]:
  193. old=elem.data["testData"]["oldID"]
  194. benchFile.write("\"%s\",\"%s\",%d,\"%s\",%s,%d,%s\n" % (category,name,theId,old,params,cycles,config))
  195. def analyseResult(root,results,embedded,benchmark,formatter):
  196. formatter.start()
  197. path = []
  198. state = NORMAL
  199. prefix=""
  200. elem=None
  201. theId=None
  202. theError=None
  203. theLine=None
  204. passed=0
  205. cycles=None
  206. benchFile = None
  207. config=""
  208. if embedded:
  209. prefix = ".*S:[ ]"
  210. # Parse the result file.
  211. # NORMAL mode is when we are parsing suite or group.
  212. # Otherwise we are parsing a test and we need to analyse the
  213. # test result.
  214. # TESTPARAM is used to read parameters of the test.
  215. # Format of output is:
  216. #node ident : s id or g id or t or u
  217. #test status : id error linenb status Y or N (Y when passing)
  218. #param for this test b x,x,x,x or b alone if not param
  219. #node end : p
  220. # In FPGA mode:
  221. #Prefix S:[ ] before driver dump
  222. # D:[ ] before data dump (output patterns)
  223. for l in results:
  224. l = l.strip()
  225. if not re.match(r'^.*D:[ ].*$',l):
  226. if state == NORMAL:
  227. if len(l) > 0:
  228. # Line starting with g or s is a suite or group.
  229. # In FPGA mode, those line are prefixed with 'S: '
  230. # and data file with 'D: '
  231. if re.match(r'^%s[gs][ ]+[0-9]+.*$' % prefix,l):
  232. # Extract the test id
  233. theId=re.sub(r'^%s[gs][ ]+([0-9]+).*$' % prefix,r'\1',l)
  234. theId=int(theId)
  235. path.append(theId)
  236. # From a list of id, find the TreeElem in the Parsed tree
  237. # to know what is the node.
  238. elem = findItem(root,path)
  239. # Display formatted output for this node
  240. if elem.params:
  241. #print(elem.params.full)
  242. benchPath = os.path.join(benchmark,elem.fullPath(),"fullBenchmark.csv")
  243. createMissingDir(benchPath)
  244. if benchFile:
  245. printf("ERROR BENCH FILE %s ALREADY OPEN" % benchPath)
  246. benchFile.close()
  247. benchFile=None
  248. benchFile=open(benchPath,"w")
  249. header = "".join(list(joinit(elem.params.full,",")))
  250. # A test and a benchmark are different
  251. # so we don't dump a status and error
  252. # A status and error in a benchmark would
  253. # impact the cycles since the test
  254. # would be taken into account in the measurement
  255. # So benchmark are always passing and contain no test
  256. #benchFile.write("ID,%s,PASSED,ERROR,CYCLES\n" % header)
  257. csvheaders = ""
  258. with open('currentConfig.csv', 'r') as f:
  259. reader = csv.reader(f)
  260. csvheaders = next(reader, None)
  261. configList = list(reader)
  262. #print(configList)
  263. config = "".join(list(joinit(configList[0],",")))
  264. configHeaders = "".join(list(joinit(csvheaders,",")))
  265. benchFile.write("CATEGORY,NAME,ID,OLDID,%s,CYCLES,%s\n" % (header,configHeaders))
  266. formatter.printGroup(elem,theId)
  267. # If we have detected a test, we switch to test mode
  268. if re.match(r'^%s[t][ ]*$' % prefix,l):
  269. state = INTEST
  270. # Pop
  271. # End of suite or group
  272. if re.match(r'^%sp.*$' % prefix,l):
  273. if benchFile:
  274. benchFile.close()
  275. benchFile=None
  276. path.pop()
  277. formatter.pop()
  278. elif state == INTEST:
  279. if len(l) > 0:
  280. # In test mode, we are looking for test status.
  281. # A line starting with S
  282. # (There may be empty lines or line for data files)
  283. passRe = r'^%s([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([YN]).*$' % prefix
  284. if re.match(passRe,l):
  285. # If we have found a test status then we will start again
  286. # in normal mode after this.
  287. m = re.match(passRe,l)
  288. # Extract test ID, test error code, line number and status
  289. theId=m.group(1)
  290. theId=int(theId)
  291. theError=m.group(2)
  292. theError=int(theError)
  293. theLine=m.group(3)
  294. theLine=int(theLine)
  295. cycles = int(m.group(4))
  296. status=m.group(5)
  297. passed=0
  298. # Convert status to number as used by formatter.
  299. if status=="Y":
  300. passed = 1
  301. if status=="N":
  302. passed = 0
  303. # Compute path to this node
  304. newPath=path.copy()
  305. newPath.append(theId)
  306. # Find the node in the Tree
  307. elem = findItem(root,newPath)
  308. state = TESTPARAM
  309. else:
  310. if re.match(r'^%sp.*$' % prefix,l):
  311. if benchFile:
  312. benchFile.close()
  313. benchFile=None
  314. path.pop()
  315. formatter.pop()
  316. if re.match(r'^%s[t][ ]*$' % prefix,l):
  317. state = INTEST
  318. else:
  319. state = NORMAL
  320. else:
  321. if len(l) > 0:
  322. state = INTEST
  323. params=""
  324. if re.match(r'^.*b[ ]+([0-9,]+)$',l):
  325. m=re.match(r'^.*b[ ]+([0-9,]+)$',l)
  326. params=m.group(1).strip()
  327. # Format the node
  328. #print(elem.fullPath())
  329. #createMissingDir(destPath)
  330. writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config)
  331. else:
  332. params=""
  333. writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config)
  334. # Format the node
  335. formatter.printTest(elem,theId,theError,theLine,passed,cycles,params)
  336. formatter.end()
  337. parser = argparse.ArgumentParser(description='Parse test description')
  338. parser.add_argument('-f', nargs='?',type = str, default=None, help="Test description file path")
  339. # Where the result file can be found
  340. parser.add_argument('-r', nargs='?',type = str, default=None, help="Result file path")
  341. parser.add_argument('-c', action='store_true', help="CSV output")
  342. parser.add_argument('-e', action='store_true', help="Embedded test")
  343. # -o needed when -e is true to know where to extract the output files
  344. parser.add_argument('-o', nargs='?',type = str, default="Output", help="Output dir path")
  345. parser.add_argument('-b', nargs='?',type = str, default="FullBenchmark", help="Full Benchmark dir path")
  346. parser.add_argument('-m', action='store_true', help="Mathematica output")
  347. args = parser.parse_args()
  348. if args.f is not None:
  349. p = parse.Parser()
  350. # Parse the test description file
  351. root = p.parse(args.f)
  352. with open(args.r,"r") as results:
  353. if args.c:
  354. analyseResult(root,results,args.e,args.b,CSVFormatter())
  355. elif args.m:
  356. analyseResult(root,results,args.e,args.b,MathematicaFormatter())
  357. else:
  358. analyseResult(root,results,args.e,args.b,TextFormatter())
  359. if args.e:
  360. # In FPGA mode, extract output files from stdout (result file)
  361. with open(args.r,"r") as results:
  362. extractDataFiles(results,args.o)
  363. else:
  364. parser.print_help()