blob: 8f3730f240ad08b156d436486fba20c67b598e88 [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 Wang923f9432023-07-17 12:43:00 +080034from types import SimpleNamespace
Yanray Wang16ebc572023-05-30 18:10:20 +080035from mbedtls_dev import typing_util
Gilles Peskined9071e72022-09-18 21:17:09 +020036from mbedtls_dev import build_tree
37
Yanray Wang23bd5322023-05-24 11:03:59 +080038class SupportedArch(Enum):
39 """Supported architecture for code size measurement."""
40 AARCH64 = 'aarch64'
41 AARCH32 = 'aarch32'
Yanray Wangaba71582023-05-29 16:45:56 +080042 ARMV8_M = 'armv8-m'
Yanray Wang23bd5322023-05-24 11:03:59 +080043 X86_64 = 'x86_64'
44 X86 = 'x86'
45
Yanray Wang6a862582023-05-24 12:24:38 +080046CONFIG_TFM_MEDIUM_MBEDCRYPTO_H = "../configs/tfm_mbedcrypto_config_profile_medium.h"
47CONFIG_TFM_MEDIUM_PSA_CRYPTO_H = "../configs/crypto_config_profile_medium.h"
48class SupportedConfig(Enum):
49 """Supported configuration for code size measurement."""
50 DEFAULT = 'default'
51 TFM_MEDIUM = 'tfm-medium'
52
Yanray Wang16ebc572023-05-30 18:10:20 +080053# Static library
54MBEDTLS_STATIC_LIB = {
55 'CRYPTO': 'library/libmbedcrypto.a',
56 'X509': 'library/libmbedx509.a',
57 'TLS': 'library/libmbedtls.a',
58}
59
Yanray Wang23bd5322023-05-24 11:03:59 +080060DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
61def detect_arch() -> str:
62 """Auto-detect host architecture."""
63 cc_output = subprocess.check_output(DETECT_ARCH_CMD, shell=True).decode()
64 if "__aarch64__" in cc_output:
65 return SupportedArch.AARCH64.value
66 if "__arm__" in cc_output:
67 return SupportedArch.AARCH32.value
68 if "__x86_64__" in cc_output:
69 return SupportedArch.X86_64.value
70 if "__x86__" in cc_output:
71 return SupportedArch.X86.value
72 else:
73 print("Unknown host architecture, cannot auto-detect arch.")
74 sys.exit(1)
Gilles Peskined9071e72022-09-18 21:17:09 +020075
Yanray Wang923f9432023-07-17 12:43:00 +080076class CodeSizeBuildInfo: # pylint: disable=too-few-public-methods
Yanray Wang6a862582023-05-24 12:24:38 +080077 """Gather information used to measure code size.
78
79 It collects information about architecture, configuration in order to
80 infer build command for code size measurement.
81 """
82
Yanray Wangc18cd892023-05-31 11:08:04 +080083 SupportedArchConfig = [
84 "-a " + SupportedArch.AARCH64.value + " -c " + SupportedConfig.DEFAULT.value,
85 "-a " + SupportedArch.AARCH32.value + " -c " + SupportedConfig.DEFAULT.value,
86 "-a " + SupportedArch.X86_64.value + " -c " + SupportedConfig.DEFAULT.value,
87 "-a " + SupportedArch.X86.value + " -c " + SupportedConfig.DEFAULT.value,
88 "-a " + SupportedArch.ARMV8_M.value + " -c " + SupportedConfig.TFM_MEDIUM.value,
89 ]
90
Yanray Wang923f9432023-07-17 12:43:00 +080091 def __init__(self, size_version: SimpleNamespace) -> None:
Yanray Wang6a862582023-05-24 12:24:38 +080092 """
Yanray Wang923f9432023-07-17 12:43:00 +080093 size_version: SimpleNamespace containing info for code size measurement.
94 size_version.arch: architecture to measure code size on.
95 size_version.config: configuration type to measure code size with.
96 size_version.host_arch: host architecture.
Yanray Wang6a862582023-05-24 12:24:38 +080097 """
Yanray Wang923f9432023-07-17 12:43:00 +080098 self.size_version = size_version
Yanray Wang6a862582023-05-24 12:24:38 +080099
Yanray Wang923f9432023-07-17 12:43:00 +0800100 def infer_make_command(self) -> str:
Yanray Wang6a862582023-05-24 12:24:38 +0800101 """Infer build command based on architecture and configuration."""
102
Yanray Wang923f9432023-07-17 12:43:00 +0800103 if self.size_version.config == SupportedConfig.DEFAULT.value and \
104 self.size_version.arch == self.size_version.host_arch:
Yanray Wang6a862582023-05-24 12:24:38 +0800105 return 'make -j lib CFLAGS=\'-Os \' '
Yanray Wang923f9432023-07-17 12:43:00 +0800106 elif self.size_version.arch == SupportedArch.ARMV8_M.value and \
107 self.size_version.config == SupportedConfig.TFM_MEDIUM.value:
Yanray Wang6a862582023-05-24 12:24:38 +0800108 return \
Yanray Wang60430bd2023-05-29 14:48:18 +0800109 'make -j lib CC=armclang \
Yanray Wang6a862582023-05-24 12:24:38 +0800110 CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
111 -DMBEDTLS_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_MBEDCRYPTO_H + '\\\" \
112 -DMBEDTLS_PSA_CRYPTO_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_PSA_CRYPTO_H + '\\\" \''
113 else:
Yanray Wang21f17442023-06-01 11:29:06 +0800114 print("Unsupported combination of architecture: {} and configuration: {}"
Yanray Wang923f9432023-07-17 12:43:00 +0800115 .format(self.size_version.arch, self.size_version.config))
Yanray Wangc18cd892023-05-31 11:08:04 +0800116 print("\nPlease use supported combination of architecture and configuration:")
Yanray Wang923f9432023-07-17 12:43:00 +0800117 for comb in CodeSizeBuildInfo.SupportedArchConfig:
Yanray Wangc18cd892023-05-31 11:08:04 +0800118 print(comb)
Yanray Wang21f17442023-06-01 11:29:06 +0800119 print("\nFor your system, please use:")
Yanray Wang923f9432023-07-17 12:43:00 +0800120 for comb in CodeSizeBuildInfo.SupportedArchConfig:
121 if "default" in comb and self.size_version.host_arch not in comb:
Yanray Wang21f17442023-06-01 11:29:06 +0800122 continue
123 print(comb)
Yanray Wang6a862582023-05-24 12:24:38 +0800124 sys.exit(1)
125
126
Yanray Wange0e27602023-07-14 17:37:45 +0800127class CodeSizeCalculator:
128 """ A calculator to calculate code size of library objects based on
129 Git revision and code size measurement tool.
130 """
131
132 def __init__(
133 self,
134 revision: str,
135 make_cmd: str,
136 ) -> None:
137 """
138 revision: Git revision.(E.g: commit)
139 make_cmd: command to build library objects.
140 """
141 self.repo_path = "."
142 self.git_command = "git"
143 self.make_clean = 'make clean'
144
145 self.revision = revision
146 self.make_cmd = make_cmd
147
148 @staticmethod
149 def validate_revision(revision: str) -> bytes:
150 result = subprocess.check_output(["git", "rev-parse", "--verify",
151 revision + "^{commit}"], shell=False)
152 return result
153
154 def _create_git_worktree(self, revision: str) -> str:
155 """Make a separate worktree for revision.
156 Do not modify the current worktree."""
157
158 if revision == "current":
159 print("Using current work directory")
160 git_worktree_path = self.repo_path
161 else:
162 print("Creating git worktree for", revision)
163 git_worktree_path = os.path.join(self.repo_path, "temp-" + revision)
164 subprocess.check_output(
165 [self.git_command, "worktree", "add", "--detach",
166 git_worktree_path, revision], cwd=self.repo_path,
167 stderr=subprocess.STDOUT
168 )
169
170 return git_worktree_path
171
172 def _build_libraries(self, git_worktree_path: str) -> None:
173 """Build libraries in the specified worktree."""
174
175 my_environment = os.environ.copy()
176 try:
177 subprocess.check_output(
178 self.make_clean, env=my_environment, shell=True,
179 cwd=git_worktree_path, stderr=subprocess.STDOUT,
180 )
181 subprocess.check_output(
182 self.make_cmd, env=my_environment, shell=True,
183 cwd=git_worktree_path, stderr=subprocess.STDOUT,
184 )
185 except subprocess.CalledProcessError as e:
186 self._handle_called_process_error(e, git_worktree_path)
187
188 def _gen_raw_code_size(self, revision, git_worktree_path):
189 """Calculate code size with measurement tool in UTF-8 encoding."""
190 if revision == "current":
191 print("Measuring code size in current work directory")
192 else:
193 print("Measuring code size for", revision)
194
195 res = {}
196 for mod, st_lib in MBEDTLS_STATIC_LIB.items():
197 try:
198 result = subprocess.check_output(
199 ["size", st_lib, "-t"], cwd=git_worktree_path,
200 universal_newlines=True
201 )
202 res[mod] = result
203 except subprocess.CalledProcessError as e:
204 self._handle_called_process_error(e, git_worktree_path)
205
206 return res
207
208 def _remove_worktree(self, git_worktree_path: str) -> None:
209 """Remove temporary worktree."""
210 if git_worktree_path != self.repo_path:
211 print("Removing temporary worktree", git_worktree_path)
212 subprocess.check_output(
213 [self.git_command, "worktree", "remove", "--force",
214 git_worktree_path], cwd=self.repo_path,
215 stderr=subprocess.STDOUT
216 )
217
218 def _handle_called_process_error(self, e: subprocess.CalledProcessError,
219 git_worktree_path: str) -> None:
220 """Handle a CalledProcessError and quit the program gracefully.
221 Remove any extra worktrees so that the script may be called again."""
222
223 # Tell the user what went wrong
224 print("The following command: {} failed and exited with code {}"
225 .format(e.cmd, e.returncode))
226 print("Process output:\n {}".format(str(e.output, "utf-8")))
227
228 # Quit gracefully by removing the existing worktree
229 self._remove_worktree(git_worktree_path)
230 sys.exit(-1)
231
232 def cal_libraries_code_size(self) -> typing.Dict:
233 """Calculate code size of libraries by measurement tool."""
234
235 revision = self.revision
236 git_worktree_path = self._create_git_worktree(revision)
237 self._build_libraries(git_worktree_path)
238 res = self._gen_raw_code_size(revision, git_worktree_path)
239 self._remove_worktree(git_worktree_path)
240
241 return res
242
243
Yanray Wang15c43f32023-07-17 11:17:12 +0800244class CodeSizeGenerator:
245 """ A generator based on size measurement tool for library objects.
246
247 This is an abstract class. To use it, derive a class that implements
248 size_generator_write_record and size_generator_write_comparison methods,
249 then call both of them with proper arguments.
250 """
251 def size_generator_write_record(
252 self,
253 revision: str,
254 code_size_text: typing.Dict,
255 output_file: str
256 ) -> None:
257 """Write size record into a file.
258
259 revision: Git revision.(E.g: commit)
260 code_size_text: text output (utf-8) from code size measurement tool.
261 output_file: file which the code size record is written to.
262 """
263 raise NotImplementedError
264
265 def size_generator_write_comparison(
266 self,
267 old_rev: str,
268 new_rev: str,
269 output_stream
270 ) -> None:
271 """Write a comparision result into a stream between two revisions.
272
273 old_rev: old git revision to compared with.
274 new_rev: new git revision to compared with.
275 output_stream: stream which the code size record is written to.
276 (E.g: file / sys.stdout)
277 """
278 raise NotImplementedError
279
280
281class CodeSizeGeneratorWithSize(CodeSizeGenerator):
Yanray Wang16ebc572023-05-30 18:10:20 +0800282 """Code Size Base Class for size record saving and writing."""
283
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800284 class SizeEntry: # pylint: disable=too-few-public-methods
285 """Data Structure to only store information of code size."""
286 def __init__(self, text, data, bss, dec):
287 self.text = text
288 self.data = data
289 self.bss = bss
290 self.total = dec # total <=> dec
291
Yanray Wang16ebc572023-05-30 18:10:20 +0800292 def __init__(self) -> None:
293 """ Variable code_size is used to store size info for any revisions.
294 code_size: (data format)
295 {revision: {module: {file_name: SizeEntry,
296 etc ...
297 },
298 etc ...
299 },
300 etc ...
301 }
302 """
303 self.code_size = {} #type: typing.Dict[str, typing.Dict]
304
305 def set_size_record(self, revision: str, mod: str, size_text: str) -> None:
306 """Store size information for target revision and high-level module.
307
308 size_text Format: text data bss dec hex filename
309 """
310 size_record = {}
311 for line in size_text.splitlines()[1:]:
312 data = line.split()
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800313 size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(\
314 data[0], data[1], data[2], data[3])
Yanray Wang16ebc572023-05-30 18:10:20 +0800315 if revision in self.code_size:
316 self.code_size[revision].update({mod: size_record})
317 else:
318 self.code_size[revision] = {mod: size_record}
319
320 def read_size_record(self, revision: str, fname: str) -> None:
321 """Read size information from csv file and write it into code_size.
322
323 fname Format: filename text data bss dec
324 """
325 mod = ""
326 size_record = {}
327 with open(fname, 'r') as csv_file:
328 for line in csv_file:
329 data = line.strip().split()
330 # check if we find the beginning of a module
331 if data and data[0] in MBEDTLS_STATIC_LIB:
332 mod = data[0]
333 continue
334
335 if mod:
336 size_record[data[0]] = \
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800337 CodeSizeGeneratorWithSize.SizeEntry(\
338 data[1], data[2], data[3], data[4])
Yanray Wang16ebc572023-05-30 18:10:20 +0800339
340 # check if we hit record for the end of a module
341 m = re.match(r'.?TOTALS', line)
342 if m:
343 if revision in self.code_size:
344 self.code_size[revision].update({mod: size_record})
345 else:
346 self.code_size[revision] = {mod: size_record}
347 mod = ""
348 size_record = {}
349
350 def _size_reader_helper(
351 self,
352 revision: str,
353 output: typing_util.Writable
354 ) -> typing.Iterator[tuple]:
355 """A helper function to peel code_size based on revision."""
356 for mod, file_size in self.code_size[revision].items():
357 output.write("\n" + mod + "\n")
358 for fname, size_entry in file_size.items():
359 yield mod, fname, size_entry
360
361 def write_size_record(
362 self,
363 revision: str,
364 output: typing_util.Writable
365 ) -> None:
366 """Write size information to a file.
367
368 Writing Format: file_name text data bss total(dec)
369 """
370 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
371 .format("filename", "text", "data", "bss", "total"))
372 for _, fname, size_entry in self._size_reader_helper(revision, output):
373 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
374 .format(fname, size_entry.text, size_entry.data,\
375 size_entry.bss, size_entry.total))
376
377 def write_comparison(
378 self,
379 old_rev: str,
380 new_rev: str,
381 output: typing_util.Writable
382 ) -> None:
383 """Write comparison result into a file.
384
385 Writing Format: file_name current(total) old(total) change(Byte) change_pct(%)
386 """
387 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
388 .format("filename", "current", "old", "change", "change%"))
389 for mod, fname, size_entry in self._size_reader_helper(new_rev, output):
390 new_size = int(size_entry.total)
391 # check if we have the file in old revision
392 if fname in self.code_size[old_rev][mod]:
393 old_size = int(self.code_size[old_rev][mod][fname].total)
394 change = new_size - old_size
395 if old_size != 0:
396 change_pct = change / old_size
397 else:
398 change_pct = 0
399 output.write("{:<30} {:>7} {:>7} {:>7} {:>7.2%}\n"
400 .format(fname, new_size, old_size, change, change_pct))
401 else:
402 output.write("{} {}\n".format(fname, new_size))
403
Yanray Wang15c43f32023-07-17 11:17:12 +0800404 def size_generator_write_record(
405 self,
406 revision: str,
407 code_size_text: typing.Dict,
408 output_file: str
409 ) -> None:
410 """Write size record into a specified file based on Git revision and
411 output from `size` tool."""
412 for mod, size_text in code_size_text.items():
413 self.set_size_record(revision, mod, size_text)
414
415 print("Generating code size csv for", revision)
416 output = open(output_file, "w")
417 self.write_size_record(revision, output)
418
419 def size_generator_write_comparison(
420 self,
421 old_rev: str,
422 new_rev: str,
423 output_stream
424 ) -> None:
425 """Write a comparision result into a stream between two revisions."""
426 output = open(output_stream, "w")
427 self.write_comparison(old_rev, new_rev, output)
428
Yanray Wang16ebc572023-05-30 18:10:20 +0800429
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800430class CodeSizeComparison:
Xiaofei Bai2400b502021-10-21 12:22:58 +0000431 """Compare code size between two Git revisions."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000432
Yanray Wang72b105f2023-05-31 15:20:39 +0800433 def __init__(
434 self,
Yanray Wang923f9432023-07-17 12:43:00 +0800435 old_size_version: SimpleNamespace,
436 new_size_version: SimpleNamespace,
Yanray Wang72b105f2023-05-31 15:20:39 +0800437 result_dir: str,
Yanray Wang72b105f2023-05-31 15:20:39 +0800438 ) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000439 """
Yanray Wang6a862582023-05-24 12:24:38 +0800440 old_revision: revision to compare against.
Xiaofei Baibca03e52021-09-09 09:42:37 +0000441 new_revision:
Yanray Wang6a862582023-05-24 12:24:38 +0800442 result_dir: directory for comparison result.
Xiaofei Baibca03e52021-09-09 09:42:37 +0000443 """
444 self.repo_path = "."
445 self.result_dir = os.path.abspath(result_dir)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000446 os.makedirs(self.result_dir, exist_ok=True)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000447
448 self.csv_dir = os.path.abspath("code_size_records/")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000449 os.makedirs(self.csv_dir, exist_ok=True)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000450
Yanray Wang923f9432023-07-17 12:43:00 +0800451 self.old_size_version = old_size_version
452 self.new_size_version = new_size_version
453 self.old_size_version.make_cmd = \
454 CodeSizeBuildInfo(self.old_size_version).infer_make_command()
455 self.new_size_version.make_cmd = \
456 CodeSizeBuildInfo(self.new_size_version).infer_make_command()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000457 self.git_command = "git"
Yanray Wang4c26db02023-07-04 16:49:04 +0800458 self.make_clean = 'make clean'
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800459 self.code_size_generator = CodeSizeGeneratorWithSize()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000460
Yanray Wang923f9432023-07-17 12:43:00 +0800461 @staticmethod
462 def cal_code_size(size_version: SimpleNamespace):
Yanray Wang5e9130a2023-07-17 11:55:54 +0800463 """Calculate code size of library objects in a UTF-8 encoding"""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000464
Yanray Wang923f9432023-07-17 12:43:00 +0800465 return CodeSizeCalculator(size_version.revision, size_version.make_cmd).\
Yanray Wange0e27602023-07-14 17:37:45 +0800466 cal_libraries_code_size()
Yanray Wang8804db92023-05-30 18:18:18 +0800467
Yanray Wang923f9432023-07-17 12:43:00 +0800468 @staticmethod
469 def gen_file_name(old_size_version, new_size_version=None):
470 if new_size_version:
471 return '{}-{}-{}-{}-{}-{}.csv'\
472 .format(old_size_version.revision[:7],
473 old_size_version.arch, old_size_version.config,
474 new_size_version.revision[:7],
475 new_size_version.arch, new_size_version.config)
476 else:
477 return '{}-{}-{}.csv'\
478 .format(old_size_version.revision[:7],
479 old_size_version.arch, old_size_version.config)
480
481 def gen_code_size_report(self, size_version: SimpleNamespace):
Yanray Wang5e9130a2023-07-17 11:55:54 +0800482 """Generate code size record and write it into a file."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000483
Yanray Wang923f9432023-07-17 12:43:00 +0800484 output_file = os.path.join(self.csv_dir, self.gen_file_name(size_version))
Xiaofei Baibca03e52021-09-09 09:42:37 +0000485 # Check if the corresponding record exists
Yanray Wang923f9432023-07-17 12:43:00 +0800486 if (size_version.revision != "current") and os.path.exists(output_file):
487 print("Code size csv file for", size_version.revision, "already exists.")
488 self.code_size_generator.read_size_record(size_version.revision, output_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000489 else:
Yanray Wang923f9432023-07-17 12:43:00 +0800490 self.code_size_generator.size_generator_write_record(\
491 size_version.revision, self.cal_code_size(size_version),
492 output_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000493
Yanray Wang5e9130a2023-07-17 11:55:54 +0800494 def gen_code_size_comparison(self) -> int:
495 """Generate results of code size changes between two revisions,
Xiaofei Baibca03e52021-09-09 09:42:37 +0000496 old and new. Measured code size results of these two revisions
Xiaofei Bai2400b502021-10-21 12:22:58 +0000497 must be available."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000498
Yanray Wang923f9432023-07-17 12:43:00 +0800499 output_file = os.path.join(self.result_dir,\
500 self.gen_file_name(self.old_size_version, self.new_size_version))
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000501
Yanray Wangc7a2a6d2023-05-31 15:47:25 +0800502 print("\nGenerating comparison results between",\
Yanray Wang923f9432023-07-17 12:43:00 +0800503 self.old_size_version.revision, "and", self.new_size_version.revision)
Yanray Wange0e27602023-07-14 17:37:45 +0800504 self.code_size_generator.size_generator_write_comparison(\
Yanray Wang923f9432023-07-17 12:43:00 +0800505 self.old_size_version.revision, self.new_size_version.revision,\
506 output_file)
Xiaofei Bai2400b502021-10-21 12:22:58 +0000507 return 0
Xiaofei Baibca03e52021-09-09 09:42:37 +0000508
Yanray Wang72b105f2023-05-31 15:20:39 +0800509 def get_comparision_results(self) -> int:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000510 """Compare size of library/*.o between self.old_rev and self.new_rev,
511 and generate the result file."""
Gilles Peskined9071e72022-09-18 21:17:09 +0200512 build_tree.check_repo_path()
Yanray Wang923f9432023-07-17 12:43:00 +0800513 self.gen_code_size_report(self.old_size_version)
514 self.gen_code_size_report(self.new_size_version)
Yanray Wang5e9130a2023-07-17 11:55:54 +0800515 return self.gen_code_size_comparison()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000516
Yanray Wang923f9432023-07-17 12:43:00 +0800517
Xiaofei Bai2400b502021-10-21 12:22:58 +0000518def main():
Yanray Wang502c54f2023-05-31 11:41:36 +0800519 parser = argparse.ArgumentParser(description=(__doc__))
520 group_required = parser.add_argument_group(
521 'required arguments',
522 'required arguments to parse for running ' + os.path.basename(__file__))
523 group_required.add_argument(
524 "-o", "--old-rev", type=str, required=True,
525 help="old revision for comparison.")
526
527 group_optional = parser.add_argument_group(
528 'optional arguments',
529 'optional arguments to parse for running ' + os.path.basename(__file__))
530 group_optional.add_argument(
Xiaofei Baibca03e52021-09-09 09:42:37 +0000531 "-r", "--result-dir", type=str, default="comparison",
532 help="directory where comparison result is stored, \
Yanray Wang502c54f2023-05-31 11:41:36 +0800533 default is comparison")
534 group_optional.add_argument(
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000535 "-n", "--new-rev", type=str, default=None,
536 help="new revision for comparison, default is the current work \
Yanray Wang502c54f2023-05-31 11:41:36 +0800537 directory, including uncommitted changes.")
538 group_optional.add_argument(
Yanray Wang23bd5322023-05-24 11:03:59 +0800539 "-a", "--arch", type=str, default=detect_arch(),
540 choices=list(map(lambda s: s.value, SupportedArch)),
541 help="specify architecture for code size comparison, default is the\
Yanray Wang502c54f2023-05-31 11:41:36 +0800542 host architecture.")
543 group_optional.add_argument(
Yanray Wang6a862582023-05-24 12:24:38 +0800544 "-c", "--config", type=str, default=SupportedConfig.DEFAULT.value,
545 choices=list(map(lambda s: s.value, SupportedConfig)),
546 help="specify configuration type for code size comparison,\
Yanray Wang502c54f2023-05-31 11:41:36 +0800547 default is the current MbedTLS configuration.")
Xiaofei Baibca03e52021-09-09 09:42:37 +0000548 comp_args = parser.parse_args()
549
550 if os.path.isfile(comp_args.result_dir):
551 print("Error: {} is not a directory".format(comp_args.result_dir))
552 parser.exit()
553
Yanray Wange0e27602023-07-14 17:37:45 +0800554 validate_res = CodeSizeCalculator.validate_revision(comp_args.old_rev)
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000555 old_revision = validate_res.decode().replace("\n", "")
Xiaofei Bai2400b502021-10-21 12:22:58 +0000556
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000557 if comp_args.new_rev is not None:
Yanray Wange0e27602023-07-14 17:37:45 +0800558 validate_res = CodeSizeCalculator.validate_revision(comp_args.new_rev)
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000559 new_revision = validate_res.decode().replace("\n", "")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000560 else:
561 new_revision = "current"
Xiaofei Bai2400b502021-10-21 12:22:58 +0000562
Yanray Wang923f9432023-07-17 12:43:00 +0800563 old_size_version = SimpleNamespace(
564 version="old",
565 revision=old_revision,
566 config=comp_args.config,
567 arch=comp_args.arch,
568 host_arch=detect_arch(),
569 make_cmd='',
570 )
571 new_size_version = SimpleNamespace(
572 version="new",
573 revision=new_revision,
574 config=comp_args.config,
575 arch=comp_args.arch,
576 host_arch=detect_arch(),
577 make_cmd='',
578 )
579
580 size_compare = CodeSizeComparison(old_size_version, new_size_version,\
581 comp_args.result_dir)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000582 return_code = size_compare.get_comparision_results()
583 sys.exit(return_code)
584
585
586if __name__ == "__main__":
Xiaofei Bai2400b502021-10-21 12:22:58 +0000587 main()