nsdk_bench.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. import time
  5. import copy
  6. import glob
  7. import serial
  8. import tempfile
  9. import json
  10. import argparse
  11. from prettytable import *
  12. from nsdk_builder import *
  13. from nsdk_utils import *
  14. from nsdk_report import *
  15. class nsdk_bench(nsdk_runner):
  16. def __init__(self):
  17. super().__init__()
  18. pass
  19. def build_apps(self, config:dict, show_output=True, logdir=None, stoponfail=False):
  20. if isinstance(config, dict) == False:
  21. return False, None
  22. global_build_config = config.get("build_config", dict())
  23. global_build_configs = config.get("build_configs", dict())
  24. global_target = config.get("build_target", "all")
  25. rootdirs = config.get("appdirs", [])
  26. ignored_rootdirs = config.get("appdirs_ignore", [])
  27. if (isinstance(rootdirs, list) and isinstance(ignored_rootdirs, list)) == False:
  28. print("appdirs type in config should be list, please check!")
  29. return False, None
  30. if len(rootdirs) == 0:
  31. print("No appdirs specified in config, please check!")
  32. return False, None
  33. root_appdirs = dict()
  34. appnum = 0
  35. # Find all the apps in rootdirs
  36. for rootdir in rootdirs:
  37. root_appdirs[rootdir] = self.find_apps(rootdir)
  38. appnum += len(root_appdirs[rootdir])
  39. # Find all the ignored apps in ignored_rootdirs
  40. ignored_apps = []
  41. for ig_rootdir in ignored_rootdirs:
  42. ignored_apps.extend(self.find_apps(ig_rootdir))
  43. if appnum == 0:
  44. print("No application found in appdirs specified in config")
  45. return False, None
  46. createlog = False
  47. if isinstance(logdir, str):
  48. createlog = True
  49. if os.path.isdir(logdir) == False:
  50. os.makedirs(logdir)
  51. appconfigs = config.get("appconfig", dict())
  52. # Construct all the applications' configuration
  53. apps_config = dict()
  54. for rootdir in root_appdirs:
  55. for appdir in root_appdirs[rootdir]:
  56. if appdir in ignored_apps:
  57. print("%s is ignored by appdirs_ignore section" % (appdir))
  58. continue
  59. appdir = appdir.replace("\\", "/") # Change windows \\ path to /
  60. app_buildcfg = copy.deepcopy(global_build_config)
  61. app_buildcfgs = copy.deepcopy(global_build_configs)
  62. app_buildtarget = copy.deepcopy(global_target)
  63. found_cfg = find_local_appconfig(appdir, appconfigs)
  64. if found_cfg:
  65. appcfg = appconfigs[found_cfg]
  66. appcfg = appconfigs[appdir]
  67. # merge_global is True if not present
  68. merge_global = appcfg.get("merge_global", True)
  69. # Merge global config when merge_global is True
  70. if merge_global:
  71. app_buildcfg.update(appcfg.get("build_config", dict()))
  72. app_buildcfgs.update(appcfg.get("build_configs", dict()))
  73. if "build_target" in appcfg:
  74. app_buildtarget = appcfg["build_target"]
  75. else: # if merge_global is false, then use app config only
  76. app_buildcfg = appcfg.get("build_config", dict())
  77. app_buildcfgs = appcfg.get("build_configs", dict())
  78. app_buildtarget = appcfg.get("build_target", "all")
  79. app_allconfigs = {"configs": {}}
  80. if len(app_buildcfgs) == 0:
  81. app_buildcfgs = { "default": {} }
  82. for cfgname in app_buildcfgs:
  83. if createlog:
  84. buildlog_name = os.path.join(cfgname, "build.log")
  85. applogfile = get_logfile(appdir, rootdir, logdir, buildlog_name)
  86. else:
  87. applogfile = None
  88. percfg_appbuildcfg = copy.deepcopy(app_buildcfg)
  89. percfg_appbuildcfg.update(app_buildcfgs[cfgname])
  90. app_allconfigs["configs"][cfgname] = {"build_config": percfg_appbuildcfg, "build_target": app_buildtarget, \
  91. "logs": {"build": applogfile}}
  92. apps_config[appdir] = copy.deepcopy(app_allconfigs)
  93. if len(apps_config) == 0:
  94. print("No applications need to be run according to the configuration")
  95. return False, None
  96. # Build all the applications
  97. print("Build %d applications defined by configuration" % (len(apps_config)))
  98. cmdsts, build_status = self.build_apps_with_configs(apps_config, show_output, stoponfail)
  99. return cmdsts, build_status
  100. def run_apps(self, config:dict, show_output=True, logdir=None, stoponfail=False):
  101. if isinstance(config, dict) == False:
  102. return False, None
  103. global_build_config = config.get("build_config", dict())
  104. global_build_configs = config.get("build_configs", dict())
  105. global_target = config.get("build_target", "clean all")
  106. global_run_config = config.get("run_config", dict())
  107. global_checks = config.get("checks", None)
  108. rootdirs = config.get("appdirs", [])
  109. ignored_rootdirs = config.get("appdirs_ignore", [])
  110. if (isinstance(rootdirs, list) and isinstance(ignored_rootdirs, list)) == False:
  111. print("appdirs type in config should be list, please check!")
  112. return False, None
  113. if len(rootdirs) == 0:
  114. print("No appdirs specified in config, please check!")
  115. return False, None
  116. root_appdirs = dict()
  117. appnum = 0
  118. # Find all the apps in rootdirs
  119. for rootdir in rootdirs:
  120. root_appdirs[rootdir] = self.find_apps(rootdir)
  121. appnum += len(root_appdirs[rootdir])
  122. # Find all the ignored apps in ignored_rootdirs
  123. ignored_apps = []
  124. for ig_rootdir in ignored_rootdirs:
  125. ignored_apps.extend(self.find_apps(ig_rootdir))
  126. if appnum == 0:
  127. print("No application found in appdirs specified in config")
  128. return False, None
  129. createlog = False
  130. if isinstance(logdir, str):
  131. createlog = True
  132. if os.path.isdir(logdir) == False:
  133. os.makedirs(logdir)
  134. appconfigs = config.get("appconfig", dict())
  135. # Construct all the applications' configuration
  136. apps_config = dict()
  137. for rootdir in root_appdirs:
  138. for appdir in root_appdirs[rootdir]:
  139. if appdir in ignored_apps:
  140. print("%s is ignored according to appdirs_ignore settings in configuration" % (appdir))
  141. continue
  142. appdir = appdir.replace("\\", "/") # Change windows \\ path to /
  143. app_buildcfg = copy.deepcopy(global_build_config)
  144. app_buildcfgs = copy.deepcopy(global_build_configs)
  145. app_buildtarget = copy.deepcopy(global_target)
  146. app_runcfg = copy.deepcopy(global_run_config)
  147. app_checks = copy.deepcopy(global_checks)
  148. found_cfg = find_local_appconfig(appdir, appconfigs)
  149. if found_cfg:
  150. appcfg = appconfigs[found_cfg]
  151. # merge_global is True if not present
  152. merge_global = appcfg.get("merge_global", True)
  153. # Merge global config when merge_global is True
  154. if merge_global:
  155. app_buildcfg.update(appcfg.get("build_config", dict()))
  156. app_buildcfgs.update(appcfg.get("build_configs", dict()))
  157. app_runcfg.update(appcfg.get("run_config", dict()))
  158. app_checks.update(appcfg.get("checks", dict()))
  159. if "build_target" in appcfg:
  160. app_buildtarget = appcfg["build_target"]
  161. else: # if merge_global is false, then use app config only
  162. app_buildcfg = appcfg.get("build_config", dict())
  163. app_buildcfgs = appcfg.get("build_configs", dict())
  164. app_buildtarget = appcfg.get("build_target", "clean all")
  165. app_runcfg = appcfg.get("run_config", dict())
  166. app_checks = appcfg.get("checks", dict())
  167. app_allconfigs = {"configs": {}}
  168. if len(app_buildcfgs) == 0:
  169. app_buildcfgs = { "default": {} }
  170. for cfgname in app_buildcfgs:
  171. if createlog:
  172. buildlog_name = os.path.join(cfgname, "build.log")
  173. runlog_name = os.path.join(cfgname, "run.log")
  174. app_buildlogfile = get_logfile(appdir, rootdir, logdir, buildlog_name)
  175. app_runlogfile = get_logfile(appdir, rootdir, logdir, runlog_name)
  176. else:
  177. app_buildlogfile = None
  178. app_runlogfile = None
  179. percfg_appbuildcfg = copy.deepcopy(app_buildcfg)
  180. percfg_appbuildcfg.update(app_buildcfgs[cfgname])
  181. app_allconfigs["configs"][cfgname] = {"build_config": percfg_appbuildcfg, "build_target": app_buildtarget, \
  182. "run_config": app_runcfg, "checks": app_checks, \
  183. "logs": {"build": app_buildlogfile, "run": app_runlogfile}}
  184. apps_config[appdir] = copy.deepcopy(app_allconfigs)
  185. if len(apps_config) == 0:
  186. print("No applications need to be run according to the configuration")
  187. return False, None
  188. print("Run %d applications defined by configuration" % (len(apps_config)))
  189. # Run all the applications
  190. cmdsts, build_status = self.run_apps_with_configs(apps_config, show_output, stoponfail)
  191. return cmdsts, build_status
  192. def merge_config(appcfg, hwcfg):
  193. if isinstance(appcfg, dict) == True and isinstance(hwcfg, dict) == False:
  194. return appcfg
  195. if isinstance(appcfg, dict) == False and isinstance(hwcfg, dict) == True:
  196. return hwcfg
  197. merged_appcfg = copy.deepcopy(appcfg)
  198. merged_appcfg.update(hwcfg)
  199. return merged_appcfg
  200. def merge_cmd_config(config, serport, baudrate, make_options):
  201. if isinstance(config, dict) == False:
  202. return None
  203. new_config = copy.deepcopy(config)
  204. if serport:
  205. run_cfg = new_config.get("run_config", None)
  206. if run_cfg is None:
  207. new_config["run_config"] = {"hardware" : {"serport": str(serport), "baudrate": 115200}}
  208. else:
  209. if "hardware" in run_cfg:
  210. new_config["run_config"]["hardware"]["serport"] = str(serport)
  211. else:
  212. new_config["run_config"]["hardware"] = {"serport": str(serport), "baudrate": 115200}
  213. if baudrate:
  214. run_cfg = new_config.get("run_config", None)
  215. if run_cfg is None:
  216. new_config["run_config"] = {"hardware" : {"serport": "/dev/ttyUSB1", "baudrate": int(baudrate)}}
  217. else:
  218. if "hardware" in run_cfg:
  219. new_config["run_config"]["hardware"]["baudrate"] = int(baudrate)
  220. else:
  221. new_config["run_config"]["hardware"] = {"serport": "/dev/ttyUSB1", "baudrate": int(baudrate)}
  222. if make_options:
  223. opt_splits=make_options.strip().split()
  224. passed_buildcfg = dict()
  225. for opt in opt_splits:
  226. if "=" in opt:
  227. values = opt.split("=")
  228. # Make new build config
  229. if (len(values) == 2):
  230. passed_buildcfg[values[0]] = values[1]
  231. build_cfg = new_config.get("build_config", None)
  232. if build_cfg is None:
  233. new_config["build_config"] = passed_buildcfg
  234. else:
  235. # update build_config using parsed config via values specified in make_options
  236. new_config["build_config"].update(passed_buildcfg)
  237. return new_config
  238. if __name__ == '__main__':
  239. parser = argparse.ArgumentParser(description="Nuclei SDK Benchmark and Report Tool")
  240. parser.add_argument('--appcfg', required=True, help="Application JSON Configuration File")
  241. parser.add_argument('--hwcfg', help="Hardware Target JSON Configuration File, if specified, will overwrite configuration defined in appcfg")
  242. parser.add_argument('--logdir', default='logs', help="logs directory, default logs")
  243. parser.add_argument('--serport', help="Serial port for monitor, if not specified, it will use the specified by appcfg and hwcfg")
  244. parser.add_argument('--baudrate', help="Serial port baudrate for monitor, it will use the specified by appcfg and hwcfg")
  245. parser.add_argument('--make_options', help="Extra make options passed to overwrite default build configuration passed via appcfg and hwcfg")
  246. parser.add_argument('--run', action='store_true', help="If specified, will do run not build process")
  247. parser.add_argument('--verbose', action='store_true', help="If specified, will show detailed build/run messsage")
  248. args = parser.parse_args()
  249. # Load appcfg and hwcfg
  250. ret, appcfg = load_json(args.appcfg)
  251. if ret != JSON_OK:
  252. print("Please provide valid json config file")
  253. sys.exit(1)
  254. # record start time
  255. start_time = time.time()
  256. ret, hwcfg = load_json(args.hwcfg)
  257. # Merge appcfg and hwcfg, hwcfg has higher priority
  258. config = merge_config(appcfg, hwcfg)
  259. # Merge options passed by serport, baudrate, make_options
  260. config = merge_cmd_config(config, args.serport, args.baudrate, args.make_options)
  261. nsdk_ext = nsdk_bench()
  262. if args.run:
  263. cmdsts, result = nsdk_ext.run_apps(config, args.verbose, args.logdir, False)
  264. else:
  265. cmdsts, result = nsdk_ext.build_apps(config, args.verbose, args.logdir, False)
  266. runtime = round(time.time() - start_time, 2)
  267. print("Applications specified in %s build or run status: %s, time cost %s seconds" % (args.appcfg, cmdsts, runtime))
  268. ret = check_expected(result, config, args.run)
  269. print("Application build as expected: %s" % (ret))
  270. save_results(appcfg, hwcfg, config, result, args.logdir)
  271. if result:
  272. # Generate build or run report
  273. rptfile = os.path.join(args.logdir, "report.md")
  274. generate_report(config, result, rptfile, args.logdir, args.run)
  275. print("App, Case, build, run, total, text, data, bss")
  276. for app in result:
  277. size_of_all_cases = result[app]
  278. for case in size_of_all_cases:
  279. size = size_of_all_cases[case]["size"]
  280. app_status = size_of_all_cases[case]["status"]
  281. print("%s, %s, %s, %s, %d, %d, %d, %d" % (app, case, app_status["build"], app_status.get("run", False), \
  282. size["total"], size["text"], size["data"], size["bss"]))
  283. # Exit with ret value
  284. if ret:
  285. sys.exit(0)
  286. else:
  287. sys.exit(1)