parse_report.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import argparse
  2. import json
  3. import os
  4. import openpyxl.styles
  5. import openpyxl.utils
  6. import openpyxl.workbook
  7. import pyexcel as pe
  8. import openpyxl as openpyxl
  9. # TODO:
  10. # 1. Make the generated excel more readable and beautiful
  11. # - Make column widths more suitable for the table header
  12. # - Make excel content with more beautiful colors and fonts
  13. def parse_runresult(runresult_file, CAREVAR="bench", BENCHTYPE="barebench"):
  14. runresult = dict()
  15. # Load json result from runresult file runresult.json
  16. with open(runresult_file) as f:
  17. runresult = json.load(f)
  18. parsed_bench = dict()
  19. olevels = ["O0", "O1", "O2", "O3", "Ofast", "Os", "Oz"]
  20. # parse the bench results from runresult json
  21. for archcfg in runresult:
  22. for benchtype in runresult[archcfg]:
  23. # TODO only check barebench results now
  24. if benchtype != "barebench":
  25. continue
  26. for subtype in runresult[archcfg][benchtype]:
  27. sheettype = subtype
  28. if BENCHTYPE == "toolbench":
  29. matcholv = None
  30. for olv in olevels:
  31. if olv in archcfg:
  32. matcholv = olv
  33. break
  34. if matcholv is not None:
  35. sheettype = "%s_%s" % (subtype, matcholv)
  36. # collect the results of barebench
  37. # TODO ignore a lot meta data such as code size and compiler info
  38. if sheettype not in parsed_bench:
  39. parsed_bench[sheettype] = dict()
  40. if CAREVAR == "bench":
  41. # benchval is a dict like this
  42. # {"DMIPS/MHz": 1.426 } or {"CoreMark/MHz": 1.212} or {"MWIPS/MHz": 0.068}
  43. benchval = runresult[archcfg][benchtype][subtype]["value"]
  44. try:
  45. parsed_bench[sheettype][archcfg] = list(benchval.values())[0]
  46. except:
  47. # No value get from value dict
  48. parsed_bench[sheettype][archcfg] = ""
  49. elif CAREVAR.startswith("size"):
  50. sizeval = runresult[archcfg][benchtype][subtype]["size"]
  51. whichsize = CAREVAR.lstrip("size").split("+")
  52. # by default return total size
  53. if len(whichsize) == 1 and whichsize[0] not in ("text", "data", "bss"):
  54. parsed_bench[sheettype][archcfg] = sizeval["total"]
  55. else:
  56. # sum the key found in whichsize
  57. # eg. size:text+data will return the sum of text+data section
  58. sizesum = 0
  59. for szkey in whichsize:
  60. szkey = szkey.strip()
  61. if szkey in ("text", "data", "bss"):
  62. sizesum += sizeval[szkey]
  63. parsed_bench[sheettype][archcfg] = sizesum
  64. # return the parsed bench result
  65. return parsed_bench
  66. def parse_genreport(rptdir, BENCHTYPE="barebench", CAREVAR="bench"):
  67. # get the list of directory list in rptdir
  68. rv32_bench = dict()
  69. rv64_bench = dict()
  70. for d in os.listdir(rptdir):
  71. if d.startswith("."):
  72. continue
  73. # not a valid barebench directory
  74. if os.path.isdir(os.path.join(rptdir, d, BENCHTYPE)) == False:
  75. continue
  76. runresult_json_file = os.path.join(rptdir, d, BENCHTYPE, "runresult.json")
  77. if d.startswith("ux") or d.startswith("nx"):
  78. rv64_bench[d] = parse_runresult(runresult_json_file, BENCHTYPE=BENCHTYPE, CAREVAR=CAREVAR)
  79. else:
  80. rv32_bench[d] = parse_runresult(runresult_json_file, BENCHTYPE=BENCHTYPE, CAREVAR=CAREVAR)
  81. return rv32_bench, rv64_bench
  82. def shorten_bitname(bitname):
  83. first_part = bitname.split("_config")[0]
  84. last_part = bitname.split("_")[-1][4:-4]
  85. shortname = ""
  86. # if "_best" in first_part:
  87. # first_part = first_part.replace("_best", "_b")
  88. # if "_dual" in first_part:
  89. # first_part = first_part.replace("_dual", "_DI")
  90. if "single" in bitname:
  91. shortname = "%s_SI-%s" % (first_part, last_part)
  92. else:
  93. shortname = "%s-%s" % (first_part, last_part)
  94. return shortname
  95. def generate_excel_from_bench(rvbench, xlsname):
  96. # generate excel using rvbench
  97. rvsheets = dict()
  98. for bitname in rvbench:
  99. for subtype in rvbench[bitname]:
  100. if subtype not in rvsheets:
  101. rvsheets[subtype] = set()
  102. rvsheets[subtype] = set(rvbench[bitname][subtype].keys()).union(rvsheets[subtype])
  103. rvwbdict = dict()
  104. for subtype in rvsheets:
  105. rvsheets[subtype] = sorted(rvsheets[subtype])
  106. parsed_configs = []
  107. for bitname in rvbench:
  108. shortbitname = shorten_bitname(bitname)
  109. if shortbitname in parsed_configs:
  110. continue
  111. parsed_configs.append(shortbitname)
  112. for subtype in rvbench[bitname]:
  113. if subtype not in rvwbdict:
  114. rvwbdict[subtype] = [["config"]]
  115. for cfgname in rvsheets[subtype]:
  116. rvwbdict[subtype].append([cfgname])
  117. rvwbdict[subtype][0].extend([shorten_bitname(bitname)])
  118. cfgindex = 1
  119. for cfgname in rvsheets[subtype]:
  120. if cfgname in rvbench[bitname][subtype]:
  121. rvwbdict[subtype][cfgindex].extend([rvbench[bitname][subtype][cfgname]])
  122. else:
  123. rvwbdict[subtype][cfgindex].extend([""])
  124. cfgindex += 1
  125. print("Save Excel as %s\n" % (xlsname))
  126. # book = pe.isave_book_as(bookdict=rvwbdict, dest_file_name=xlsname)
  127. book = pe.get_book(bookdict=rvwbdict)
  128. print(book)
  129. book.save_as(xlsname)
  130. book.save_as(xlsname + ".html")
  131. pass
  132. def beautify_excel(excelfile):
  133. wb = openpyxl.load_workbook(filename=excelfile)
  134. tools = ["nuclei_gnu", "terapines", "nuclei_llvm"]
  135. for ws in wb.worksheets:
  136. # 设置字体样式
  137. bold_font_style = openpyxl.styles.Font(name="阿里巴巴普惠体 3.0 55 Regular", bold=True, size=11)
  138. font_stype = openpyxl.styles.Font(name="阿里巴巴普惠体 3.0 55 Regular", bold=False, size=11)
  139. alignment_style = openpyxl.styles.Alignment(horizontal="center", vertical="center", wrap_text=True)
  140. ## 设置不同工具链配置下的背景色
  141. # 淡红色
  142. def_fill_style = openpyxl.styles.PatternFill(start_color="da9694", end_color="da9694", fill_type="solid")
  143. # 黄色
  144. gnu_fill_style = openpyxl.styles.PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
  145. # 浅蓝色
  146. llvm_fill_style = openpyxl.styles.PatternFill(start_color="00b0f0", end_color="00b0f0", fill_type="solid")
  147. # 浅绿色
  148. zcc_fill_style = openpyxl.styles.PatternFill(start_color="92d050", end_color="92d050", fill_type="solid")
  149. fill_styles = {
  150. "nuclei_gnu": gnu_fill_style,
  151. "nuclei_llvm": llvm_fill_style,
  152. "terapines": zcc_fill_style,
  153. "default": def_fill_style
  154. }
  155. # 设置第一行第一列的指定宽度和高度
  156. # 设置第一行自动换行,第二列以后的固定宽度为10
  157. ws.row_dimensions[1].height = 60
  158. ws.column_dimensions['A'].width = 50
  159. for colidx in range(1, ws.max_row):
  160. ws.column_dimensions[openpyxl.utils.get_column_letter(colidx+1)].width = 10
  161. cfgnames = None
  162. # 遍历每个sheet, 第一行,第一列加粗
  163. # 然后从第二列开始,将每一列中最大值加粗
  164. # 且每一列按照 nuclei_gnu, nuclei_llvm, terapines这样的分类找到最大值,并给最大值给背景色,背景色的设定参见 fill_styles
  165. for col in ws.iter_cols(min_row=1, max_row=ws.max_row, min_col=1, max_col=ws.max_column):
  166. maxvals = dict()
  167. if col[0].column == 1:
  168. cfgnames = [cell.value for cell in col]
  169. else:
  170. colvals = [cell.value for cell in col]
  171. for (cfg, value) in zip(cfgnames, colvals):
  172. if cfg == "config":
  173. continue
  174. newval = value if value else 0
  175. matchedtool = "default"
  176. for tool in tools:
  177. if tool in cfg:
  178. matchedtool = tool
  179. break
  180. if matchedtool not in maxvals:
  181. maxvals[matchedtool] = newval
  182. else:
  183. maxvals[matchedtool] = max(maxvals[matchedtool], newval)
  184. maxval = 0
  185. if len(maxvals) > 0:
  186. maxval = max(list(maxvals.values()))
  187. for (cfg, cell) in zip(cfgnames, col):
  188. cell.alignment = alignment_style
  189. if cell.row == 1 or cell.column == 1:
  190. cell.font = bold_font_style
  191. else:
  192. cell.font = font_stype
  193. if cell.column > 1 and cell.row > 1:
  194. matchedtool = "default"
  195. for tool in tools:
  196. if tool in cfg:
  197. matchedtool = tool
  198. break
  199. if cell.value and maxvals[matchedtool] == cell.value:
  200. cell.fill = fill_styles[matchedtool]
  201. if maxval == cell.value: # 让最大值加粗
  202. cell.font = bold_font_style
  203. wb.save(excelfile.replace(".xlsx", "_styled.xlsx"))
  204. pass
  205. def generate_bench_excel(rptdir, BENCHTYPE="barebench", CAREVAR="bench"):
  206. rv32_bench, rv64_bench = parse_genreport(rptdir, BENCHTYPE=BENCHTYPE, CAREVAR=CAREVAR)
  207. # generate excel using rv32_bench
  208. generate_excel_from_bench(rv32_bench, os.path.join(rptdir, "rv32_%s.xlsx" % (BENCHTYPE)))
  209. beautify_excel(os.path.join(rptdir, "rv32_%s.xlsx" % (BENCHTYPE)))
  210. generate_excel_from_bench(rv64_bench, os.path.join(rptdir, "rv64_%s.xlsx" % (BENCHTYPE)))
  211. beautify_excel(os.path.join(rptdir, "rv64_%s.xlsx" % (BENCHTYPE)))
  212. pass
  213. # 使用argpaser 解析参数,-d 表示待解析的目录
  214. parser = argparse.ArgumentParser(description='Parse the nsdk_cli run reports')
  215. parser.add_argument('-d', '--dir', help='directory to parse')
  216. args = parser.parse_args()
  217. reportdir = args.dir.strip()
  218. for benchtype in ["barebench", "toolbench"]:
  219. generate_bench_excel(reportdir, benchtype)