1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2024 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from glob import glob 18import argparse 19import copy 20import multiprocessing 21import os 22import stat 23import subprocess 24import sys 25 26WORKLOAD_PATH = 'test_cases/ark-workload/weekly_workload' 27PARSE_COMMAND_PATH = '../parse_command' 28SIZE_PERCENTAGE_REPORT = 'size_percentage_report.html' 29HTML_CONTENT = \ 30""" 31<!DOCTYPE html> 32<html> 33<head> 34 <title>Size Percentage Report</title> 35 <style> 36 table { 37 width: 50%; 38 border-collapse: collapse; 39 margin: auto; 40 } 41 th, td { 42 padding: 8px; 43 text-align: center; 44 border: 1px solid black; 45 white-space: nowrap; 46 } 47 th:nth-child(2), td:nth-child(2) { 48 text-align: left; 49 } 50 th:last-child, td:last-child { 51 white-space: pre-wrap; 52 text-align: left; 53 } 54 h1 { 55 text-align: center; 56 } 57 .percentage_table { 58 width: 100%; 59 border-collapse: collapse; 60 margin-top: -35px; 61 } 62 .percentage_table th, .percentage_table td { 63 padding: 4px; 64 border: none; 65 } 66 .percentage_table td { 67 padding-right: 10px; 68 line-height: 1; 69 padding-top: 0; 70 } 71 .percentage_table .item_section { 72 text-align: left; 73 } 74 </style> 75</head> 76<body> 77 <h1>Size Percentage Report</h1> 78 <table> 79 <tr> 80 <th>No</th> 81 <th>Case Path</th> 82 <th>ABC Size</th> 83 <th>Percentage of Sections</th> 84 </tr> 85""" 86 87 88def is_directory(parser, arg): 89 if not os.path.isdir(arg): 90 parser.error("The directory '%s' does not exist" % arg) 91 return os.path.abspath(arg) 92 93 94def is_file(parser, arg): 95 if not os.path.isfile(arg): 96 parser.error("The file '%s' does not exist" % arg) 97 return os.path.abspath(arg) 98 99 100def check_timeout(value): 101 ivalue = int(value) 102 if ivalue <= 0: 103 raise argparse.ArgumentTypeError("%s is an invalid timeout value" % value) 104 return ivalue 105 106 107def get_args(): 108 description = "Generate statistics on the percentage of size corresponding to each case." 109 parser = argparse.ArgumentParser(description=description) 110 parser.add_argument('--es2abc-path', dest='es2abc_path', type=lambda arg : is_file(parser, arg), 111 help='Path to the executable program es2abc', required=True) 112 parser.add_argument('--no-progress', action='store_false', dest='progress', default=True, 113 help='Don\'t show progress bar') 114 parser.add_argument('--timeout', type=check_timeout, dest='timeout', default=180, 115 help='Time limits for use case execution (In seconds)') 116 return parser.parse_args() 117 118 119class Test: 120 def __init__(self, test_path, flags): 121 self.test_path = test_path 122 self.test_case_name = self.test_path.split('/')[-1] 123 self.flags = flags 124 self.output = None 125 126 def __lt__(self, other): 127 return self.test_path < other.test_path 128 129 def run(self, runner): 130 test_abc_name = self.test_case_name[:self.test_case_name.rfind('.')] + '.abc' 131 test_abc_path = os.path.join(os.path.dirname(self.test_path), test_abc_name) 132 cmd = runner.cmd_prefix + [runner.es2abc_path] 133 cmd.extend(self.flags) 134 cmd.extend(["--output=" + test_abc_path]) 135 cmd.append(self.test_path) 136 137 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 138 out, err = process.communicate(timeout=runner.args.timeout) 139 self.output = out.decode("utf-8", errors="ignore") + err.decode("utf-8", errors="ignore") 140 141 if os.path.exists(test_abc_path): 142 self.abc_size = os.path.getsize(test_abc_path) 143 self.percentage_of_sections = self.output 144 os.remove(test_abc_path) 145 else: 146 self.abc_size = 0 147 self.percentage_of_sections = 'NULL' 148 149 self.test_case_name = os.path.relpath(self.test_path, runner.test_root) 150 return self 151 152 153class Runner: 154 def __init__(self, args): 155 self.args = args 156 self.test_root = os.path.dirname(os.path.abspath(__file__)) 157 self.es2abc_path = args.es2abc_path 158 self.size_percentage_statistics_file = os.path.join(self.test_root, SIZE_PERCENTAGE_REPORT) 159 self.case_list = [] # store abs case_path 160 self.tests = [] # store Test obj 161 self.cmd_prefix = [] 162 self.results = [] 163 164 def wrap_cases(self, flags, func=Test): 165 self.tests += list(map(lambda f: func(f, flags), self.case_list)) 166 self.case_list.clear() 167 168 def add_case(self, case_path, extension, flags): 169 abs_case_path = os.path.abspath(case_path) 170 if abs_case_path not in self.case_list and abs_case_path.endswith(extension): 171 self.case_list.append(case_path) 172 self.wrap_cases(flags) 173 174 def add_directory(self, directory, extension, flags): 175 glob_expression = os.path.join(os.path.abspath(directory), "**/*%s" % (extension)) 176 cases = glob(glob_expression, recursive=True) 177 for case in cases: 178 self.add_case(case, extension, flags) 179 180 def run_test(self, test): 181 return test.run(self) 182 183 def run(self): 184 pool = multiprocessing.Pool() 185 result_iter = pool.imap_unordered(self.run_test, self.tests, chunksize=32) 186 pool.close() 187 188 if self.args.progress: 189 from tqdm import tqdm 190 result_iter = tqdm(result_iter, total=len(self.tests)) 191 192 self.results = [result for result in result_iter] 193 self.results.sort() 194 pool.join() 195 196 self.generate_size_percentage_report() 197 198 def format_sections_for_percentage_report(self, output, percentage_of_sections_list): 199 lines = output.split('\n') 200 start_flag = False 201 for line in lines: 202 if start_flag: 203 if 'percent' in line: 204 [item_section_and_item_size, percentage] = line.split(',') 205 [item_section, item_size] = item_section_and_item_size.split(':') 206 percentage = percentage.split(':')[-1].strip() 207 percentage_of_sections_list.append([item_section.strip(), item_size.strip(), \ 208 percentage.strip()]) 209 210 if 'Panda file size statistic:' in line: 211 start_flag = True 212 213 def generate_size_percentage_report(self): 214 global HTML_CONTENT 215 for number, result in enumerate(self.results, 1): 216 percentage_of_sections_list = [] 217 self.format_sections_for_percentage_report(result.percentage_of_sections, percentage_of_sections_list) 218 percentage_of_sections_list.sort(key=lambda percentage_of_section: int(percentage_of_section[1]), 219 reverse=True) 220 221 html_content_of_result = f""" 222 <tr> 223 <td>{number}</td> 224 <td>{result.test_case_name}</td> 225 <td>{result.abc_size}</td> 226 """ 227 HTML_CONTENT = "{}{}".format(HTML_CONTENT, html_content_of_result) 228 229 header = [''] * 3 230 if len(percentage_of_sections_list): 231 header = ['section', 'size', 'percentage'] 232 html_content_of_percentage = f""" 233 <td> 234 <table class='percentage_table'> 235 <tr> 236 <th>{header[0]}</th> 237 <th>{header[1]}</th> 238 <th>{header[2]}</th> 239 </tr> 240 """ 241 HTML_CONTENT = "{}{}".format(HTML_CONTENT, html_content_of_percentage) 242 243 total_size = 0 244 for [item_section, item_size, percentage] in percentage_of_sections_list: 245 html_content_of_sections = f""" 246 <tr> 247 <td class='item_section'>{item_section}</td> 248 <td>{item_size}</td> 249 <td>{percentage}</td> 250 </tr> 251 """ 252 HTML_CONTENT = "{}{}".format(HTML_CONTENT, html_content_of_sections) 253 total_size += int(item_size) 254 255 html_content_of_total_size = f""" 256 <tr> 257 <td class='item_section'>total size</td> 258 <td>{total_size}</td> 259 <td></td> 260 </tr> 261 """ 262 HTML_CONTENT = "{}{}".format(HTML_CONTENT, html_content_of_total_size) 263 264 HTML_CONTENT = "{}</table></td></tr>".format(HTML_CONTENT) 265 else: 266 html_content_of_not_available = f"<td>N/A</td></tr>" 267 HTML_CONTENT = "{}{}".format(HTML_CONTENT, html_content_of_not_available) 268 269 html_content_of_end_tag = "</table></body></html>" 270 HTML_CONTENT = "{}{}".format(HTML_CONTENT, html_content_of_end_tag) 271 272 flags = os.O_RDWR | os.O_CREAT 273 mode = stat.S_IWUSR | stat.S_IRUSR 274 with os.fdopen(os.open(self.size_percentage_statistics_file, flags, mode), 'w') as report: 275 report.truncate() 276 report.write(HTML_CONTENT) 277 278 279def main(): 280 args = get_args() 281 282 runner = Runner(args) 283 284 # Get CaseManager from parse_command 285 cur_dir = os.getcwd() 286 os.chdir(runner.test_root) 287 sys.path.insert(0, PARSE_COMMAND_PATH) 288 from parse_command import CaseManager 289 os.chdir(cur_dir) 290 291 # Add benchmark cases and workload cases (js) 292 args_for_case_manager = copy.copy(args) 293 setattr(args_for_case_manager, 'case', None) 294 setattr(args_for_case_manager, 'case_dir', None) 295 case_manager = CaseManager(args_for_case_manager, skip_test_flag=False) 296 for case_path in case_manager.case_list: 297 runner.add_case(case_path, '.js', ['--module', '--dump-file-item-size']) 298 299 # Add workload cases (ts) 300 ts_workload_path = os.path.join(case_manager.test_root, WORKLOAD_PATH, 'ts') 301 runner.add_directory(ts_workload_path, '.ts', ['--module', '--dump-file-item-size']) 302 303 runner.run() 304 305 306if __name__ == "__main__": 307 main()