Commands.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. import subprocess
  2. import colorama
  3. from colorama import init,Fore, Back, Style
  4. import argparse
  5. import os
  6. import os.path
  7. from contextlib import contextmanager
  8. import shutil
  9. import glob
  10. from pathlib import Path
  11. import sys
  12. DEBUGMODE = False
  13. KEEPBUILDFOLDER = True
  14. DEBUGLIST=[
  15. "-DBASICMATH=ON",
  16. "-DCOMPLEXMATH=OFF",
  17. "-DCONTROLLER=OFF",
  18. "-DFASTMATH=OFF",
  19. "-DFILTERING=OFF",
  20. "-DMATRIX=OFF",
  21. "-DSTATISTICS=OFF",
  22. "-DSUPPORT=OFF",
  23. "-DTRANSFORM=OFF",
  24. "-DSVM=OFF",
  25. "-DBAYES=OFF",
  26. "-DDISTANCE=OFF"]
  27. NOTESTFAILED = 0
  28. MAKEFAILED = 1
  29. TESTFAILED = 2
  30. FLOWFAILURE = 3
  31. CALLFAILURE = 4
  32. def setDebugMode():
  33. global DEBUGMODE
  34. DEBUGMODE=True
  35. def isDebugMode():
  36. global DEBUGMODE
  37. return(DEBUGMODE)
  38. def setNokeepBuildFolder():
  39. global KEEPBUILDFOLDER
  40. KEEPBUILDFOLDER=False
  41. def isKeepMode():
  42. global KEEPBUILDFOLDER
  43. return(KEEPBUILDFOLDER)
  44. def joinit(iterable, delimiter):
  45. it = iter(iterable)
  46. yield next(it)
  47. for x in it:
  48. yield delimiter
  49. yield x
  50. def addToDb(db,desc,runid):
  51. msg("Add %s to summary database\n" % desc)
  52. completed = subprocess.run([sys.executable, "addToDB.py","-o",db,"-r",str(runid),desc], timeout=3600)
  53. check(completed)
  54. def addToRegDb(db,desc,runid):
  55. msg("Add %s to regression database\n" % desc)
  56. completed = subprocess.run([sys.executable, "addToRegDB.py","-o",db,"-r",str(runid),desc], timeout=3600)
  57. check(completed)
  58. class TestFlowFailure(Exception):
  59. def __init__(self,completed):
  60. self._errorcode = completed.returncode
  61. def errorCode(self):
  62. return(self._errorcode)
  63. class CallFailure(Exception):
  64. pass
  65. def check(n):
  66. #print(n)
  67. if n is not None:
  68. if n.returncode != 0:
  69. raise TestFlowFailure(n)
  70. else:
  71. raise CallFailure()
  72. def msg(t):
  73. print(Fore.CYAN + t + Style.RESET_ALL)
  74. def errorMsg(t):
  75. print(Fore.RED + t + Style.RESET_ALL)
  76. def fullTestFolder(rootFolder):
  77. return(os.path.join(rootFolder,"CMSIS","DSP","Testing","fulltests"))
  78. class BuildConfig:
  79. def __init__(self,toUnset,toSet,rootFolder,buildFolder,compiler,toolchain,core,cmake):
  80. self._toUnset = toUnset
  81. self._toSet = toSet
  82. self._buildFolder = buildFolder
  83. self._rootFolder = os.path.abspath(rootFolder)
  84. self._dspFolder = os.path.join(self._rootFolder,"CMSIS","DSP")
  85. self._testingFolder = os.path.join(self._dspFolder,"Testing")
  86. self._fullTests = os.path.join(self._testingFolder,"fulltests")
  87. self._compiler = compiler
  88. self._toolchain = toolchain
  89. self._core = core
  90. self._cmake = cmake
  91. self._savedEnv = {}
  92. def compiler(self):
  93. return(self._compiler)
  94. def toolChainFile(self):
  95. return(self._toolchain)
  96. def core(self):
  97. return(self._core)
  98. def path(self):
  99. return(os.path.join(self._fullTests,self._buildFolder))
  100. def archivePath(self):
  101. return(os.path.join(self._fullTests,"archive",self._buildFolder))
  102. def archiveResultPath(self):
  103. return(os.path.join(self._fullTests,"archive",self._buildFolder,"results"))
  104. def archiveLogPath(self):
  105. return(os.path.join(self._fullTests,"archive",self._buildFolder,"logs"))
  106. def archiveErrorPath(self):
  107. return(os.path.join(self._fullTests,"archive",self._buildFolder,"errors"))
  108. def toolChainPath(self):
  109. return(self._dspFolder)
  110. def cmakeFilePath(self):
  111. return(self._testingFolder)
  112. def buildFolderName(self):
  113. return(self._buildFolder)
  114. def saveEnv(self):
  115. if self._toUnset is not None:
  116. for v in self._toUnset:
  117. if v in os.environ:
  118. self._savedEnv[v] = os.environ[v]
  119. else:
  120. self._savedEnv[v] = None
  121. del os.environ[v]
  122. if self._toSet is not None:
  123. for v in self._toSet:
  124. for varName in v:
  125. if varName in os.environ:
  126. self._savedEnv[varName] = os.environ[varName]
  127. else:
  128. self._savedEnv[varName] = None
  129. os.environ[varName] = v[varName]
  130. def restoreEnv(self):
  131. for v in self._savedEnv:
  132. if self._savedEnv[v] is not None:
  133. os.environ[v] = self._savedEnv[v]
  134. else:
  135. if v in os.environ:
  136. del os.environ[v]
  137. self._savedEnv = {}
  138. # Build for a folder
  139. # We need to be able to detect failed build
  140. def build(self,test):
  141. completed=None
  142. # Save and unset some environment variables
  143. self.saveEnv()
  144. with self.buildFolder() as b:
  145. msg(" Build %s\n" % self.buildFolderName())
  146. with open(os.path.join(self.archiveLogPath(),"makelog_%s.txt" % test),"w") as makelog:
  147. with open(os.path.join(self.archiveErrorPath(),"makeerror_%s.txt" % test),"w") as makeerr:
  148. if DEBUGMODE:
  149. completed=subprocess.run(["make","-j4","VERBOSE=1"],timeout=3600)
  150. else:
  151. completed=subprocess.run(["make","-j4","VERBOSE=1"],stdout=makelog,stderr=makeerr,timeout=3600)
  152. # Restore environment variables
  153. self.restoreEnv()
  154. check(completed)
  155. def getTest(self,test):
  156. return(Test(self,test))
  157. # Launch cmake command.
  158. def createCMake(self,configid,flags,benchMode,platform):
  159. with self.buildFolder() as b:
  160. self.saveEnv()
  161. if benchMode:
  162. msg("Benchmark mode\n")
  163. msg("Create cmake for %s\n" % self.buildFolderName())
  164. toolchainCmake = os.path.join(self.toolChainPath(),self.toolChainFile())
  165. cmd = [self._cmake]
  166. cmd += ["-DCMAKE_PREFIX_PATH=%s" % self.compiler(),
  167. "-DCMAKE_TOOLCHAIN_FILE=%s" % toolchainCmake,
  168. "-DARM_CPU=%s" % self.core(),
  169. "-DPLATFORM=%s" % platform,
  170. "-DCONFIGID=%s" % configid
  171. ]
  172. cmd += flags
  173. if DEBUGMODE:
  174. cmd += DEBUGLIST
  175. if benchMode:
  176. cmd += ["-DBENCHMARK=ON"]
  177. cmd += ["-DWRAPPER=ON"]
  178. else:
  179. cmd += ["-DBENCHMARK=OFF"]
  180. cmd += ["-DWRAPPER=OFF"]
  181. cmd += ["-DROOT=%s" % self._rootFolder,
  182. "-DCMAKE_BUILD_TYPE=Release",
  183. "-G", "Unix Makefiles" ,"%s" % self.cmakeFilePath()]
  184. if DEBUGMODE:
  185. print(cmd)
  186. with open(os.path.join(self.archiveLogPath(),"cmakecmd.txt"),"w") as cmakecmd:
  187. cmakecmd.write("".join(joinit(cmd," ")))
  188. with open(os.path.join(self.archiveLogPath(),"cmakelog.txt"),"w") as cmakelog:
  189. with open(os.path.join(self.archiveErrorPath(),"cmakeerror.txt"),"w") as cmakeerr:
  190. completed=subprocess.run(cmd, stdout=cmakelog,stderr=cmakeerr, timeout=3600)
  191. self.restoreEnv()
  192. check(completed)
  193. # Create the build folder if missing
  194. def createFolder(self):
  195. os.makedirs(self.path(),exist_ok=True)
  196. def createArchive(self, flags):
  197. os.makedirs(self.archivePath(),exist_ok=True)
  198. os.makedirs(self.archiveResultPath(),exist_ok=True)
  199. os.makedirs(self.archiveErrorPath(),exist_ok=True)
  200. os.makedirs(self.archiveLogPath(),exist_ok=True)
  201. with open(os.path.join(self.archivePath(),"flags.txt"),"w") as f:
  202. for flag in flags:
  203. f.write(flag)
  204. f.write("\n")
  205. # Delete the build folder
  206. def cleanFolder(self):
  207. print("Delete %s\n" % self.path())
  208. #DEBUG
  209. if not isDebugMode() and not isKeepMode():
  210. shutil.rmtree(self.path())
  211. # Archive results and currentConfig.csv to another folder
  212. def archiveResults(self):
  213. results=glob.glob(os.path.join(self.path(),"results_*"))
  214. for result in results:
  215. dst=os.path.join(self.archiveResultPath(),os.path.basename(result))
  216. shutil.copy(result,dst)
  217. src = os.path.join(self.path(),"currentConfig.csv")
  218. dst = os.path.join(self.archiveResultPath(),os.path.basename(src))
  219. shutil.copy(src,dst)
  220. @contextmanager
  221. def buildFolder(self):
  222. current=os.getcwd()
  223. try:
  224. os.chdir(self.path() )
  225. yield self.path()
  226. finally:
  227. os.chdir(current)
  228. @contextmanager
  229. def archiveFolder(self):
  230. current=os.getcwd()
  231. try:
  232. os.chdir(self.archivePath() )
  233. yield self.archivePath()
  234. finally:
  235. os.chdir(current)
  236. @contextmanager
  237. def resultFolder(self):
  238. current=os.getcwd()
  239. try:
  240. os.chdir(self.archiveResultPath())
  241. yield self.archiveResultPath()
  242. finally:
  243. os.chdir(current)
  244. @contextmanager
  245. def logFolder(self):
  246. current=os.getcwd()
  247. try:
  248. os.chdir(self.archiveLogPath())
  249. yield self.archiveLogPath()
  250. finally:
  251. os.chdir(current)
  252. @contextmanager
  253. def errorFolder(self):
  254. current=os.getcwd()
  255. try:
  256. os.chdir(self.archiveErrorPath())
  257. yield self.archiveErrorPath()
  258. finally:
  259. os.chdir(current)
  260. class Test:
  261. def __init__(self,build,test):
  262. self._test = test
  263. self._buildConfig = build
  264. def buildConfig(self):
  265. return(self._buildConfig)
  266. def testName(self):
  267. return(self._test)
  268. # Process a test from the test description file
  269. def processTest(self,patternConfig):
  270. if isDebugMode():
  271. if patternConfig:
  272. completed=subprocess.run([sys.executable,"processTests.py","-p",patternConfig["patterns"],"-d",patternConfig["parameters"],"-e",self.testName(),"1"],timeout=3600)
  273. else:
  274. completed=subprocess.run([sys.executable,"processTests.py","-e",self.testName(),"1"],timeout=3600)
  275. check(completed)
  276. else:
  277. if patternConfig:
  278. completed=subprocess.run([sys.executable,"processTests.py","-p",patternConfig["patterns"],"-d",patternConfig["parameters"],"-e",self.testName()],timeout=3600)
  279. else:
  280. completed=subprocess.run([sys.executable,"processTests.py","-e",self.testName()],timeout=3600)
  281. check(completed)
  282. def getResultPath(self):
  283. return(os.path.join(self.buildConfig().path() ,self.resultName()))
  284. def resultName(self):
  285. return("results_%s.txt" % self.testName())
  286. # Run a specific test in the current folder
  287. # A specific results.txt file is created in
  288. # the build folder for this test
  289. #
  290. # We need a timeout and detect failed run
  291. def run(self,fvp,benchmode):
  292. timeoutVal=3600
  293. if benchmode:
  294. timeoutVal = 3600 * 4
  295. completed = None
  296. with self.buildConfig().buildFolder() as b:
  297. msg(" Run %s\n" % self.testName() )
  298. with open(self.resultName(),"w") as results:
  299. if isDebugMode():
  300. print(os.getcwd())
  301. print(fvp.split())
  302. completed=subprocess.run(fvp.split(),timeout=timeoutVal)
  303. else:
  304. completed=subprocess.run(fvp.split(),stdout=results,timeout=timeoutVal)
  305. check(completed)
  306. # Process results of the given tests
  307. # in given build folder
  308. # We need to detect failed tests
  309. def processResult(self):
  310. msg(" Parse result for %s\n" % self.testName())
  311. with open(os.path.join(self.buildConfig().archiveResultPath(),"processedResult_%s.txt" % self.testName()),"w") as presult:
  312. completed=subprocess.run([sys.executable,"processResult.py","-e","-r",self.getResultPath()],stdout=presult,timeout=3600)
  313. # When a test fail, the regression is continuing but we
  314. # track that a test has failed
  315. if completed.returncode==0:
  316. return(NOTESTFAILED)
  317. else:
  318. return(TESTFAILED)
  319. # Compute the regression data
  320. def computeSummaryStat(self):
  321. msg(" Compute regressions for %s\n" % self.testName())
  322. completed=subprocess.run([sys.executable,"summaryBench.py","-r",self.getResultPath(),self.testName()],timeout=3600)
  323. # When a test fail, the regression is continuing but we
  324. # track that a test has failed
  325. if completed.returncode==0:
  326. return(NOTESTFAILED)
  327. else:
  328. return(TESTFAILED)
  329. def runAndProcess(self,patternConfig,compiler,fvp,sim,benchmode,db,regdb,benchid,regid):
  330. # If we can't parse test description we fail all tests
  331. self.processTest(patternConfig)
  332. # Otherwise if only building or those tests are failing, we continue
  333. # with other tests
  334. try:
  335. self.buildConfig().build(self.testName())
  336. except:
  337. return(MAKEFAILED)
  338. # We run tests only for AC6
  339. # For other compilers only build is tests
  340. # Since full build is no more possible because of huge pattersn,
  341. # build is done per test suite.
  342. if sim:
  343. if fvp is not None:
  344. if isDebugMode():
  345. print(fvp)
  346. self.run(fvp,benchmode)
  347. error=self.processResult()
  348. if benchmode and (error == NOTESTFAILED):
  349. error = self.computeSummaryStat()
  350. if db is not None:
  351. addToDb(db,self.testName(),benchid)
  352. if regdb is not None:
  353. addToRegDb(regdb,self.testName(),regid)
  354. return(error)
  355. else:
  356. msg("No FVP available")
  357. return(NOTESTFAILED)
  358. else:
  359. return(NOTESTFAILED)
  360. # Preprocess the test description
  361. def preprocess(desc):
  362. msg("Process test description file %s\n" % desc)
  363. completed = subprocess.run([sys.executable, "preprocess.py","-f",desc],timeout=3600)
  364. check(completed)
  365. # Generate all missing C code by using all classes in the
  366. # test description file
  367. def generateAllCCode(patternConfig):
  368. msg("Generate all missing C files\n")
  369. if patternConfig:
  370. completed = subprocess.run([sys.executable,"processTests.py",
  371. "-p",patternConfig["patterns"],"-d",patternConfig["parameters"],"-e"],timeout=3600)
  372. else:
  373. completed = subprocess.run([sys.executable,"processTests.py", "-e"],timeout=3600)
  374. check(completed)
  375. # Create db
  376. def createDb(sqlite,desc):
  377. msg("Create database %s\n" % desc)
  378. with open("createDb.sql") as db:
  379. completed = subprocess.run([sqlite, desc],stdin=db, timeout=3600)
  380. check(completed)