blob: a5625c32a14b6f9b88b6f4cb6674f9d76b896ae5 [file] [log] [blame]
Xiaofei Baibca03e52021-09-09 09:42:37 +00001#!/usr/bin/env python3
2
3"""
Xiaofei Baibca03e52021-09-09 09:42:37 +00004This script is for comparing the size of the library files from two
5different Git revisions within an Mbed TLS repository.
6The results of the comparison is formatted as csv and stored at a
7configurable location.
8Note: must be run from Mbed TLS root.
9"""
10
11# Copyright The Mbed TLS Contributors
12# SPDX-License-Identifier: Apache-2.0
13#
14# Licensed under the Apache License, Version 2.0 (the "License"); you may
15# not use this file except in compliance with the License.
16# You may obtain a copy of the License at
17#
18# http://www.apache.org/licenses/LICENSE-2.0
19#
20# Unless required by applicable law or agreed to in writing, software
21# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
22# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23# See the License for the specific language governing permissions and
24# limitations under the License.
25
26import argparse
27import os
Yanray Wang16ebc572023-05-30 18:10:20 +080028import re
Xiaofei Baibca03e52021-09-09 09:42:37 +000029import subprocess
30import sys
Yanray Wang16ebc572023-05-30 18:10:20 +080031import typing
Yanray Wang23bd5322023-05-24 11:03:59 +080032from enum import Enum
Xiaofei Baibca03e52021-09-09 09:42:37 +000033
Yanray Wang16ebc572023-05-30 18:10:20 +080034from mbedtls_dev import typing_util
Gilles Peskined9071e72022-09-18 21:17:09 +020035from mbedtls_dev import build_tree
36
Yanray Wang23bd5322023-05-24 11:03:59 +080037class SupportedArch(Enum):
38 """Supported architecture for code size measurement."""
39 AARCH64 = 'aarch64'
40 AARCH32 = 'aarch32'
Yanray Wangaba71582023-05-29 16:45:56 +080041 ARMV8_M = 'armv8-m'
Yanray Wang23bd5322023-05-24 11:03:59 +080042 X86_64 = 'x86_64'
43 X86 = 'x86'
44
Yanray Wang6a862582023-05-24 12:24:38 +080045CONFIG_TFM_MEDIUM_MBEDCRYPTO_H = "../configs/tfm_mbedcrypto_config_profile_medium.h"
46CONFIG_TFM_MEDIUM_PSA_CRYPTO_H = "../configs/crypto_config_profile_medium.h"
47class SupportedConfig(Enum):
48 """Supported configuration for code size measurement."""
49 DEFAULT = 'default'
50 TFM_MEDIUM = 'tfm-medium'
51
Yanray Wang16ebc572023-05-30 18:10:20 +080052# Static library
53MBEDTLS_STATIC_LIB = {
54 'CRYPTO': 'library/libmbedcrypto.a',
55 'X509': 'library/libmbedx509.a',
56 'TLS': 'library/libmbedtls.a',
57}
58
Yanray Wang23bd5322023-05-24 11:03:59 +080059DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
60def detect_arch() -> str:
61 """Auto-detect host architecture."""
62 cc_output = subprocess.check_output(DETECT_ARCH_CMD, shell=True).decode()
63 if "__aarch64__" in cc_output:
64 return SupportedArch.AARCH64.value
65 if "__arm__" in cc_output:
66 return SupportedArch.AARCH32.value
67 if "__x86_64__" in cc_output:
68 return SupportedArch.X86_64.value
69 if "__x86__" in cc_output:
70 return SupportedArch.X86.value
71 else:
72 print("Unknown host architecture, cannot auto-detect arch.")
73 sys.exit(1)
Gilles Peskined9071e72022-09-18 21:17:09 +020074
Yanray Wang6a862582023-05-24 12:24:38 +080075class CodeSizeInfo: # pylint: disable=too-few-public-methods
76 """Gather information used to measure code size.
77
78 It collects information about architecture, configuration in order to
79 infer build command for code size measurement.
80 """
81
Yanray Wangc18cd892023-05-31 11:08:04 +080082 SupportedArchConfig = [
83 "-a " + SupportedArch.AARCH64.value + " -c " + SupportedConfig.DEFAULT.value,
84 "-a " + SupportedArch.AARCH32.value + " -c " + SupportedConfig.DEFAULT.value,
85 "-a " + SupportedArch.X86_64.value + " -c " + SupportedConfig.DEFAULT.value,
86 "-a " + SupportedArch.X86.value + " -c " + SupportedConfig.DEFAULT.value,
87 "-a " + SupportedArch.ARMV8_M.value + " -c " + SupportedConfig.TFM_MEDIUM.value,
88 ]
89
Yanray Wang21f17442023-06-01 11:29:06 +080090 def __init__(self, arch: str, config: str, sys_arch: str) -> None:
Yanray Wang6a862582023-05-24 12:24:38 +080091 """
92 arch: architecture to measure code size on.
93 config: configuration type to measure code size with.
Yanray Wang699a6c82023-07-04 17:21:28 +080094 sys_arch: host architecture.
Yanray Wang6a862582023-05-24 12:24:38 +080095 make_command: command to build library (Inferred from arch and config).
96 """
97 self.arch = arch
98 self.config = config
Yanray Wang21f17442023-06-01 11:29:06 +080099 self.sys_arch = sys_arch
Yanray Wang6a862582023-05-24 12:24:38 +0800100 self.make_command = self.set_make_command()
101
102 def set_make_command(self) -> str:
103 """Infer build command based on architecture and configuration."""
104
Yanray Wang21f17442023-06-01 11:29:06 +0800105 if self.config == SupportedConfig.DEFAULT.value and \
106 self.arch == self.sys_arch:
Yanray Wang6a862582023-05-24 12:24:38 +0800107 return 'make -j lib CFLAGS=\'-Os \' '
Yanray Wangaba71582023-05-29 16:45:56 +0800108 elif self.arch == SupportedArch.ARMV8_M.value and \
Yanray Wang6a862582023-05-24 12:24:38 +0800109 self.config == SupportedConfig.TFM_MEDIUM.value:
110 return \
Yanray Wang60430bd2023-05-29 14:48:18 +0800111 'make -j lib CC=armclang \
Yanray Wang6a862582023-05-24 12:24:38 +0800112 CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
113 -DMBEDTLS_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_MBEDCRYPTO_H + '\\\" \
114 -DMBEDTLS_PSA_CRYPTO_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_PSA_CRYPTO_H + '\\\" \''
115 else:
Yanray Wang21f17442023-06-01 11:29:06 +0800116 print("Unsupported combination of architecture: {} and configuration: {}"
Yanray Wang6a862582023-05-24 12:24:38 +0800117 .format(self.arch, self.config))
Yanray Wangc18cd892023-05-31 11:08:04 +0800118 print("\nPlease use supported combination of architecture and configuration:")
119 for comb in CodeSizeInfo.SupportedArchConfig:
120 print(comb)
Yanray Wang21f17442023-06-01 11:29:06 +0800121 print("\nFor your system, please use:")
122 for comb in CodeSizeInfo.SupportedArchConfig:
123 if "default" in comb and self.sys_arch not in comb:
124 continue
125 print(comb)
Yanray Wang6a862582023-05-24 12:24:38 +0800126 sys.exit(1)
127
128
Yanray Wang15c43f32023-07-17 11:17:12 +0800129class CodeSizeGenerator:
130 """ A generator based on size measurement tool for library objects.
131
132 This is an abstract class. To use it, derive a class that implements
133 size_generator_write_record and size_generator_write_comparison methods,
134 then call both of them with proper arguments.
135 """
136 def size_generator_write_record(
137 self,
138 revision: str,
139 code_size_text: typing.Dict,
140 output_file: str
141 ) -> None:
142 """Write size record into a file.
143
144 revision: Git revision.(E.g: commit)
145 code_size_text: text output (utf-8) from code size measurement tool.
146 output_file: file which the code size record is written to.
147 """
148 raise NotImplementedError
149
150 def size_generator_write_comparison(
151 self,
152 old_rev: str,
153 new_rev: str,
154 output_stream
155 ) -> None:
156 """Write a comparision result into a stream between two revisions.
157
158 old_rev: old git revision to compared with.
159 new_rev: new git revision to compared with.
160 output_stream: stream which the code size record is written to.
161 (E.g: file / sys.stdout)
162 """
163 raise NotImplementedError
164
165
166class CodeSizeGeneratorWithSize(CodeSizeGenerator):
Yanray Wang16ebc572023-05-30 18:10:20 +0800167 """Code Size Base Class for size record saving and writing."""
168
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800169 class SizeEntry: # pylint: disable=too-few-public-methods
170 """Data Structure to only store information of code size."""
171 def __init__(self, text, data, bss, dec):
172 self.text = text
173 self.data = data
174 self.bss = bss
175 self.total = dec # total <=> dec
176
Yanray Wang16ebc572023-05-30 18:10:20 +0800177 def __init__(self) -> None:
178 """ Variable code_size is used to store size info for any revisions.
179 code_size: (data format)
180 {revision: {module: {file_name: SizeEntry,
181 etc ...
182 },
183 etc ...
184 },
185 etc ...
186 }
187 """
188 self.code_size = {} #type: typing.Dict[str, typing.Dict]
189
190 def set_size_record(self, revision: str, mod: str, size_text: str) -> None:
191 """Store size information for target revision and high-level module.
192
193 size_text Format: text data bss dec hex filename
194 """
195 size_record = {}
196 for line in size_text.splitlines()[1:]:
197 data = line.split()
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800198 size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(\
199 data[0], data[1], data[2], data[3])
Yanray Wang16ebc572023-05-30 18:10:20 +0800200 if revision in self.code_size:
201 self.code_size[revision].update({mod: size_record})
202 else:
203 self.code_size[revision] = {mod: size_record}
204
205 def read_size_record(self, revision: str, fname: str) -> None:
206 """Read size information from csv file and write it into code_size.
207
208 fname Format: filename text data bss dec
209 """
210 mod = ""
211 size_record = {}
212 with open(fname, 'r') as csv_file:
213 for line in csv_file:
214 data = line.strip().split()
215 # check if we find the beginning of a module
216 if data and data[0] in MBEDTLS_STATIC_LIB:
217 mod = data[0]
218 continue
219
220 if mod:
221 size_record[data[0]] = \
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800222 CodeSizeGeneratorWithSize.SizeEntry(\
223 data[1], data[2], data[3], data[4])
Yanray Wang16ebc572023-05-30 18:10:20 +0800224
225 # check if we hit record for the end of a module
226 m = re.match(r'.?TOTALS', line)
227 if m:
228 if revision in self.code_size:
229 self.code_size[revision].update({mod: size_record})
230 else:
231 self.code_size[revision] = {mod: size_record}
232 mod = ""
233 size_record = {}
234
235 def _size_reader_helper(
236 self,
237 revision: str,
238 output: typing_util.Writable
239 ) -> typing.Iterator[tuple]:
240 """A helper function to peel code_size based on revision."""
241 for mod, file_size in self.code_size[revision].items():
242 output.write("\n" + mod + "\n")
243 for fname, size_entry in file_size.items():
244 yield mod, fname, size_entry
245
246 def write_size_record(
247 self,
248 revision: str,
249 output: typing_util.Writable
250 ) -> None:
251 """Write size information to a file.
252
253 Writing Format: file_name text data bss total(dec)
254 """
255 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
256 .format("filename", "text", "data", "bss", "total"))
257 for _, fname, size_entry in self._size_reader_helper(revision, output):
258 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
259 .format(fname, size_entry.text, size_entry.data,\
260 size_entry.bss, size_entry.total))
261
262 def write_comparison(
263 self,
264 old_rev: str,
265 new_rev: str,
266 output: typing_util.Writable
267 ) -> None:
268 """Write comparison result into a file.
269
270 Writing Format: file_name current(total) old(total) change(Byte) change_pct(%)
271 """
272 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
273 .format("filename", "current", "old", "change", "change%"))
274 for mod, fname, size_entry in self._size_reader_helper(new_rev, output):
275 new_size = int(size_entry.total)
276 # check if we have the file in old revision
277 if fname in self.code_size[old_rev][mod]:
278 old_size = int(self.code_size[old_rev][mod][fname].total)
279 change = new_size - old_size
280 if old_size != 0:
281 change_pct = change / old_size
282 else:
283 change_pct = 0
284 output.write("{:<30} {:>7} {:>7} {:>7} {:>7.2%}\n"
285 .format(fname, new_size, old_size, change, change_pct))
286 else:
287 output.write("{} {}\n".format(fname, new_size))
288
Yanray Wang15c43f32023-07-17 11:17:12 +0800289 def size_generator_write_record(
290 self,
291 revision: str,
292 code_size_text: typing.Dict,
293 output_file: str
294 ) -> None:
295 """Write size record into a specified file based on Git revision and
296 output from `size` tool."""
297 for mod, size_text in code_size_text.items():
298 self.set_size_record(revision, mod, size_text)
299
300 print("Generating code size csv for", revision)
301 output = open(output_file, "w")
302 self.write_size_record(revision, output)
303
304 def size_generator_write_comparison(
305 self,
306 old_rev: str,
307 new_rev: str,
308 output_stream
309 ) -> None:
310 """Write a comparision result into a stream between two revisions."""
311 output = open(output_stream, "w")
312 self.write_comparison(old_rev, new_rev, output)
313
Yanray Wang16ebc572023-05-30 18:10:20 +0800314
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800315class CodeSizeComparison:
Xiaofei Bai2400b502021-10-21 12:22:58 +0000316 """Compare code size between two Git revisions."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000317
Yanray Wang72b105f2023-05-31 15:20:39 +0800318 def __init__(
319 self,
320 old_revision: str,
321 new_revision: str,
322 result_dir: str,
323 code_size_info: CodeSizeInfo
324 ) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000325 """
Yanray Wang6a862582023-05-24 12:24:38 +0800326 old_revision: revision to compare against.
Xiaofei Baibca03e52021-09-09 09:42:37 +0000327 new_revision:
Yanray Wang6a862582023-05-24 12:24:38 +0800328 result_dir: directory for comparison result.
329 code_size_info: an object containing information to build library.
Xiaofei Baibca03e52021-09-09 09:42:37 +0000330 """
Yanray Wang8804db92023-05-30 18:18:18 +0800331 super().__init__()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000332 self.repo_path = "."
333 self.result_dir = os.path.abspath(result_dir)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000334 os.makedirs(self.result_dir, exist_ok=True)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000335
336 self.csv_dir = os.path.abspath("code_size_records/")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000337 os.makedirs(self.csv_dir, exist_ok=True)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000338
339 self.old_rev = old_revision
340 self.new_rev = new_revision
341 self.git_command = "git"
Yanray Wang4c26db02023-07-04 16:49:04 +0800342 self.make_clean = 'make clean'
Yanray Wang6a862582023-05-24 12:24:38 +0800343 self.make_command = code_size_info.make_command
Yanray Wang369cd962023-05-24 17:13:29 +0800344 self.fname_suffix = "-" + code_size_info.arch + "-" +\
345 code_size_info.config
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800346 self.code_size_generator = CodeSizeGeneratorWithSize()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000347
348 @staticmethod
Yanray Wang72b105f2023-05-31 15:20:39 +0800349 def validate_revision(revision: str) -> bytes:
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000350 result = subprocess.check_output(["git", "rev-parse", "--verify",
351 revision + "^{commit}"], shell=False)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000352 return result
Xiaofei Bai2400b502021-10-21 12:22:58 +0000353
Yanray Wang72b105f2023-05-31 15:20:39 +0800354 def _create_git_worktree(self, revision: str) -> str:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000355 """Make a separate worktree for revision.
356 Do not modify the current worktree."""
357
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000358 if revision == "current":
Yanray Wangc7a2a6d2023-05-31 15:47:25 +0800359 print("Using current work directory")
Xiaofei Baibca03e52021-09-09 09:42:37 +0000360 git_worktree_path = self.repo_path
361 else:
362 print("Creating git worktree for", revision)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000363 git_worktree_path = os.path.join(self.repo_path, "temp-" + revision)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000364 subprocess.check_output(
365 [self.git_command, "worktree", "add", "--detach",
366 git_worktree_path, revision], cwd=self.repo_path,
367 stderr=subprocess.STDOUT
368 )
Aditya Deshpande41a0aad2023-04-13 16:32:21 +0100369
Xiaofei Baibca03e52021-09-09 09:42:37 +0000370 return git_worktree_path
371
Yanray Wang72b105f2023-05-31 15:20:39 +0800372 def _build_libraries(self, git_worktree_path: str) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000373 """Build libraries in the specified worktree."""
374
375 my_environment = os.environ.copy()
Aditya Deshpande41a0aad2023-04-13 16:32:21 +0100376 try:
377 subprocess.check_output(
Yanray Wang4c26db02023-07-04 16:49:04 +0800378 self.make_clean, env=my_environment, shell=True,
379 cwd=git_worktree_path, stderr=subprocess.STDOUT,
380 )
381 subprocess.check_output(
Aditya Deshpande41a0aad2023-04-13 16:32:21 +0100382 self.make_command, env=my_environment, shell=True,
383 cwd=git_worktree_path, stderr=subprocess.STDOUT,
384 )
385 except subprocess.CalledProcessError as e:
386 self._handle_called_process_error(e, git_worktree_path)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000387
Yanray Wang72b105f2023-05-31 15:20:39 +0800388 def _gen_code_size_csv(self, revision: str, git_worktree_path: str) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000389 """Generate code size csv file."""
390
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000391 if revision == "current":
Yanray Wangc7a2a6d2023-05-31 15:47:25 +0800392 print("Measuring code size in current work directory")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000393 else:
394 print("Measuring code size for", revision)
Yanray Wang8804db92023-05-30 18:18:18 +0800395
396 for mod, st_lib in MBEDTLS_STATIC_LIB.items():
397 try:
398 result = subprocess.check_output(
399 ["size", st_lib, "-t"], cwd=git_worktree_path
400 )
401 except subprocess.CalledProcessError as e:
402 self._handle_called_process_error(e, git_worktree_path)
403 size_text = result.decode("utf-8")
404
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800405 self.code_size_generator.set_size_record(revision, mod, size_text)
Yanray Wang8804db92023-05-30 18:18:18 +0800406
407 print("Generating code size csv for", revision)
408 csv_file = open(os.path.join(self.csv_dir, revision +
409 self.fname_suffix + ".csv"), "w")
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800410 self.code_size_generator.write_size_record(revision, csv_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000411
Yanray Wang72b105f2023-05-31 15:20:39 +0800412 def _remove_worktree(self, git_worktree_path: str) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000413 """Remove temporary worktree."""
414 if git_worktree_path != self.repo_path:
415 print("Removing temporary worktree", git_worktree_path)
416 subprocess.check_output(
417 [self.git_command, "worktree", "remove", "--force",
418 git_worktree_path], cwd=self.repo_path,
419 stderr=subprocess.STDOUT
420 )
421
Yanray Wang72b105f2023-05-31 15:20:39 +0800422 def _get_code_size_for_rev(self, revision: str) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000423 """Generate code size csv file for the specified git revision."""
424
425 # Check if the corresponding record exists
Yanray Wang369cd962023-05-24 17:13:29 +0800426 csv_fname = revision + self.fname_suffix + ".csv"
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000427 if (revision != "current") and \
Xiaofei Baibca03e52021-09-09 09:42:37 +0000428 os.path.exists(os.path.join(self.csv_dir, csv_fname)):
429 print("Code size csv file for", revision, "already exists.")
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800430 self.code_size_generator.read_size_record(revision,\
431 os.path.join(self.csv_dir, csv_fname))
Xiaofei Baibca03e52021-09-09 09:42:37 +0000432 else:
433 git_worktree_path = self._create_git_worktree(revision)
434 self._build_libraries(git_worktree_path)
435 self._gen_code_size_csv(revision, git_worktree_path)
436 self._remove_worktree(git_worktree_path)
437
Yanray Wang72b105f2023-05-31 15:20:39 +0800438 def _gen_code_size_comparison(self) -> int:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000439 """Generate results of the size changes between two revisions,
440 old and new. Measured code size results of these two revisions
Xiaofei Bai2400b502021-10-21 12:22:58 +0000441 must be available."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000442
Yanray Wang369cd962023-05-24 17:13:29 +0800443 res_file = open(os.path.join(self.result_dir, "compare-" +
444 self.old_rev + "-" + self.new_rev +
445 self.fname_suffix +
446 ".csv"), "w")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000447
Yanray Wangc7a2a6d2023-05-31 15:47:25 +0800448 print("\nGenerating comparison results between",\
449 self.old_rev, "and", self.new_rev)
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800450 self.code_size_generator.write_comparison(self.old_rev, self.new_rev, res_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000451
Xiaofei Bai2400b502021-10-21 12:22:58 +0000452 return 0
Xiaofei Baibca03e52021-09-09 09:42:37 +0000453
Yanray Wang72b105f2023-05-31 15:20:39 +0800454 def get_comparision_results(self) -> int:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000455 """Compare size of library/*.o between self.old_rev and self.new_rev,
456 and generate the result file."""
Gilles Peskined9071e72022-09-18 21:17:09 +0200457 build_tree.check_repo_path()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000458 self._get_code_size_for_rev(self.old_rev)
459 self._get_code_size_for_rev(self.new_rev)
Yanray Wang8804db92023-05-30 18:18:18 +0800460 return self._gen_code_size_comparison()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000461
Aditya Deshpande41a0aad2023-04-13 16:32:21 +0100462 def _handle_called_process_error(self, e: subprocess.CalledProcessError,
Yanray Wang72b105f2023-05-31 15:20:39 +0800463 git_worktree_path: str) -> None:
Aditya Deshpande41a0aad2023-04-13 16:32:21 +0100464 """Handle a CalledProcessError and quit the program gracefully.
465 Remove any extra worktrees so that the script may be called again."""
466
467 # Tell the user what went wrong
468 print("The following command: {} failed and exited with code {}"
469 .format(e.cmd, e.returncode))
470 print("Process output:\n {}".format(str(e.output, "utf-8")))
471
472 # Quit gracefully by removing the existing worktree
473 self._remove_worktree(git_worktree_path)
474 sys.exit(-1)
475
Xiaofei Bai2400b502021-10-21 12:22:58 +0000476def main():
Yanray Wang502c54f2023-05-31 11:41:36 +0800477 parser = argparse.ArgumentParser(description=(__doc__))
478 group_required = parser.add_argument_group(
479 'required arguments',
480 'required arguments to parse for running ' + os.path.basename(__file__))
481 group_required.add_argument(
482 "-o", "--old-rev", type=str, required=True,
483 help="old revision for comparison.")
484
485 group_optional = parser.add_argument_group(
486 'optional arguments',
487 'optional arguments to parse for running ' + os.path.basename(__file__))
488 group_optional.add_argument(
Xiaofei Baibca03e52021-09-09 09:42:37 +0000489 "-r", "--result-dir", type=str, default="comparison",
490 help="directory where comparison result is stored, \
Yanray Wang502c54f2023-05-31 11:41:36 +0800491 default is comparison")
492 group_optional.add_argument(
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000493 "-n", "--new-rev", type=str, default=None,
494 help="new revision for comparison, default is the current work \
Yanray Wang502c54f2023-05-31 11:41:36 +0800495 directory, including uncommitted changes.")
496 group_optional.add_argument(
Yanray Wang23bd5322023-05-24 11:03:59 +0800497 "-a", "--arch", type=str, default=detect_arch(),
498 choices=list(map(lambda s: s.value, SupportedArch)),
499 help="specify architecture for code size comparison, default is the\
Yanray Wang502c54f2023-05-31 11:41:36 +0800500 host architecture.")
501 group_optional.add_argument(
Yanray Wang6a862582023-05-24 12:24:38 +0800502 "-c", "--config", type=str, default=SupportedConfig.DEFAULT.value,
503 choices=list(map(lambda s: s.value, SupportedConfig)),
504 help="specify configuration type for code size comparison,\
Yanray Wang502c54f2023-05-31 11:41:36 +0800505 default is the current MbedTLS configuration.")
Xiaofei Baibca03e52021-09-09 09:42:37 +0000506 comp_args = parser.parse_args()
507
508 if os.path.isfile(comp_args.result_dir):
509 print("Error: {} is not a directory".format(comp_args.result_dir))
510 parser.exit()
511
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000512 validate_res = CodeSizeComparison.validate_revision(comp_args.old_rev)
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000513 old_revision = validate_res.decode().replace("\n", "")
Xiaofei Bai2400b502021-10-21 12:22:58 +0000514
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000515 if comp_args.new_rev is not None:
516 validate_res = CodeSizeComparison.validate_revision(comp_args.new_rev)
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000517 new_revision = validate_res.decode().replace("\n", "")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000518 else:
519 new_revision = "current"
Xiaofei Bai2400b502021-10-21 12:22:58 +0000520
Yanray Wang21f17442023-06-01 11:29:06 +0800521 code_size_info = CodeSizeInfo(comp_args.arch, comp_args.config,
522 detect_arch())
Yanray Wangc7a2a6d2023-05-31 15:47:25 +0800523 print("Measure code size for architecture: {}, configuration: {}\n"
Yanray Wangaba71582023-05-29 16:45:56 +0800524 .format(code_size_info.arch, code_size_info.config))
Xiaofei Baibca03e52021-09-09 09:42:37 +0000525 result_dir = comp_args.result_dir
Yanray Wang6a862582023-05-24 12:24:38 +0800526 size_compare = CodeSizeComparison(old_revision, new_revision, result_dir,
527 code_size_info)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000528 return_code = size_compare.get_comparision_results()
529 sys.exit(return_code)
530
531
532if __name__ == "__main__":
Xiaofei Bai2400b502021-10-21 12:22:58 +0000533 main()