blob: 9b58d5093cfec30e01948a529f49f1f0663361ed [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
Yanray Wang21127f72023-07-19 12:09:45 +080027import logging
Xiaofei Baibca03e52021-09-09 09:42:37 +000028import os
Yanray Wang16ebc572023-05-30 18:10:20 +080029import re
Xiaofei Baibca03e52021-09-09 09:42:37 +000030import subprocess
31import sys
Yanray Wang16ebc572023-05-30 18:10:20 +080032import typing
Yanray Wang23bd5322023-05-24 11:03:59 +080033from enum import Enum
Xiaofei Baibca03e52021-09-09 09:42:37 +000034
Gilles Peskined9071e72022-09-18 21:17:09 +020035from mbedtls_dev import build_tree
Yanray Wang21127f72023-07-19 12:09:45 +080036from mbedtls_dev import logging_util
37from mbedtls_dev import typing_util
Gilles Peskined9071e72022-09-18 21:17:09 +020038
Yanray Wang23bd5322023-05-24 11:03:59 +080039class SupportedArch(Enum):
40 """Supported architecture for code size measurement."""
41 AARCH64 = 'aarch64'
42 AARCH32 = 'aarch32'
Yanray Wangaba71582023-05-29 16:45:56 +080043 ARMV8_M = 'armv8-m'
Yanray Wang23bd5322023-05-24 11:03:59 +080044 X86_64 = 'x86_64'
45 X86 = 'x86'
46
Yanray Wang955671b2023-07-21 12:08:27 +080047
Yanray Wang386c2f92023-07-20 15:32:15 +080048CONFIG_TFM_MEDIUM_MBEDCRYPTO_H = '../configs/tfm_mbedcrypto_config_profile_medium.h'
49CONFIG_TFM_MEDIUM_PSA_CRYPTO_H = '../configs/crypto_config_profile_medium.h'
Yanray Wang6a862582023-05-24 12:24:38 +080050class SupportedConfig(Enum):
51 """Supported configuration for code size measurement."""
52 DEFAULT = 'default'
53 TFM_MEDIUM = 'tfm-medium'
54
Yanray Wang955671b2023-07-21 12:08:27 +080055
Yanray Wang16ebc572023-05-30 18:10:20 +080056# Static library
57MBEDTLS_STATIC_LIB = {
58 'CRYPTO': 'library/libmbedcrypto.a',
59 'X509': 'library/libmbedx509.a',
60 'TLS': 'library/libmbedtls.a',
61}
62
Yanray Wang955671b2023-07-21 12:08:27 +080063class CodeSizeDistinctInfo: # pylint: disable=too-few-public-methods
64 """Data structure to store possibly distinct information for code size
65 comparison."""
66 def __init__( #pylint: disable=too-many-arguments
67 self,
68 version: str,
69 git_rev: str,
70 arch: str,
71 config: str,
72 make_cmd: str,
73 ) -> None:
74 """
75 :param: version: which version to compare with for code size.
76 :param: git_rev: Git revision to calculate code size.
77 :param: arch: architecture to measure code size on.
78 :param: config: Configuration type to calculate code size.
79 (See SupportedConfig)
80 :param: make_cmd: make command to build library/*.o.
81 """
82 self.version = version
83 self.git_rev = git_rev
84 self.arch = arch
85 self.config = config
86 self.make_cmd = make_cmd
87
88
89class CodeSizeCommonInfo: # pylint: disable=too-few-public-methods
90 """Data structure to store common information for code size comparison."""
91 def __init__(
92 self,
93 host_arch: str,
94 measure_cmd: str,
95 ) -> None:
96 """
97 :param host_arch: host architecture.
98 :param measure_cmd: command to measure code size for library/*.o.
99 """
100 self.host_arch = host_arch
101 self.measure_cmd = measure_cmd
102
103
104class CodeSizeResultInfo: # pylint: disable=too-few-public-methods
105 """Data structure to store result options for code size comparison."""
106 def __init__(
107 self,
108 record_dir: str,
109 comp_dir: str,
110 with_markdown=False,
111 stdout=False,
112 ) -> None:
113 """
114 :param record_dir: directory to store code size record.
115 :param comp_dir: directory to store results of code size comparision.
116 :param with_markdown: write comparision result into a markdown table.
117 (Default: False)
118 :param stdout: direct comparison result into sys.stdout.
119 (Default False)
120 """
121 self.record_dir = record_dir
122 self.comp_dir = comp_dir
123 self.with_markdown = with_markdown
124 self.stdout = stdout
125
126
Yanray Wang23bd5322023-05-24 11:03:59 +0800127DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
128def detect_arch() -> str:
129 """Auto-detect host architecture."""
130 cc_output = subprocess.check_output(DETECT_ARCH_CMD, shell=True).decode()
Yanray Wang386c2f92023-07-20 15:32:15 +0800131 if '__aarch64__' in cc_output:
Yanray Wang23bd5322023-05-24 11:03:59 +0800132 return SupportedArch.AARCH64.value
Yanray Wang386c2f92023-07-20 15:32:15 +0800133 if '__arm__' in cc_output:
Yanray Wang23bd5322023-05-24 11:03:59 +0800134 return SupportedArch.AARCH32.value
Yanray Wang386c2f92023-07-20 15:32:15 +0800135 if '__x86_64__' in cc_output:
Yanray Wang23bd5322023-05-24 11:03:59 +0800136 return SupportedArch.X86_64.value
Yanray Wang386c2f92023-07-20 15:32:15 +0800137 if '__x86__' in cc_output:
Yanray Wang23bd5322023-05-24 11:03:59 +0800138 return SupportedArch.X86.value
139 else:
140 print("Unknown host architecture, cannot auto-detect arch.")
141 sys.exit(1)
Gilles Peskined9071e72022-09-18 21:17:09 +0200142
Yanray Wang923f9432023-07-17 12:43:00 +0800143class CodeSizeBuildInfo: # pylint: disable=too-few-public-methods
Yanray Wang6a862582023-05-24 12:24:38 +0800144 """Gather information used to measure code size.
145
146 It collects information about architecture, configuration in order to
147 infer build command for code size measurement.
148 """
149
Yanray Wangc18cd892023-05-31 11:08:04 +0800150 SupportedArchConfig = [
Yanray Wang386c2f92023-07-20 15:32:15 +0800151 '-a ' + SupportedArch.AARCH64.value + ' -c ' + SupportedConfig.DEFAULT.value,
152 '-a ' + SupportedArch.AARCH32.value + ' -c ' + SupportedConfig.DEFAULT.value,
153 '-a ' + SupportedArch.X86_64.value + ' -c ' + SupportedConfig.DEFAULT.value,
154 '-a ' + SupportedArch.X86.value + ' -c ' + SupportedConfig.DEFAULT.value,
155 '-a ' + SupportedArch.ARMV8_M.value + ' -c ' + SupportedConfig.TFM_MEDIUM.value,
Yanray Wangc18cd892023-05-31 11:08:04 +0800156 ]
157
Yanray Wang802af162023-07-17 14:04:30 +0800158 def __init__(
159 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800160 size_dist_info: CodeSizeDistinctInfo,
Yanray Wang21127f72023-07-19 12:09:45 +0800161 host_arch: str,
162 logger: logging.Logger,
Yanray Wang802af162023-07-17 14:04:30 +0800163 ) -> None:
Yanray Wang6a862582023-05-24 12:24:38 +0800164 """
Yanray Wang955671b2023-07-21 12:08:27 +0800165 :param size_dist_info:
166 CodeSizeDistinctInfo containing info for code size measurement.
167 - size_dist_info.arch: architecture to measure code size on.
168 - size_dist_info.config: configuration type to measure
169 code size with.
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800170 :param host_arch: host architecture.
171 :param logger: logging module
Yanray Wang6a862582023-05-24 12:24:38 +0800172 """
Yanray Wang955671b2023-07-21 12:08:27 +0800173 self.size_dist_info = size_dist_info
Yanray Wang802af162023-07-17 14:04:30 +0800174 self.host_arch = host_arch
Yanray Wang21127f72023-07-19 12:09:45 +0800175 self.logger = logger
Yanray Wang6a862582023-05-24 12:24:38 +0800176
Yanray Wang923f9432023-07-17 12:43:00 +0800177 def infer_make_command(self) -> str:
Yanray Wang386c2f92023-07-20 15:32:15 +0800178 """Infer make command based on architecture and configuration."""
Yanray Wang6a862582023-05-24 12:24:38 +0800179
Yanray Wang386c2f92023-07-20 15:32:15 +0800180 # make command by default
Yanray Wang955671b2023-07-21 12:08:27 +0800181 if self.size_dist_info.config == SupportedConfig.DEFAULT.value and \
182 self.size_dist_info.arch == self.host_arch:
Yanray Wang6a862582023-05-24 12:24:38 +0800183 return 'make -j lib CFLAGS=\'-Os \' '
Yanray Wang386c2f92023-07-20 15:32:15 +0800184 # make command for TF-M
Yanray Wang955671b2023-07-21 12:08:27 +0800185 elif self.size_dist_info.arch == SupportedArch.ARMV8_M.value and \
186 self.size_dist_info.config == SupportedConfig.TFM_MEDIUM.value:
Yanray Wang6a862582023-05-24 12:24:38 +0800187 return \
Yanray Wang60430bd2023-05-29 14:48:18 +0800188 'make -j lib CC=armclang \
Yanray Wang6a862582023-05-24 12:24:38 +0800189 CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
190 -DMBEDTLS_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_MBEDCRYPTO_H + '\\\" \
191 -DMBEDTLS_PSA_CRYPTO_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_PSA_CRYPTO_H + '\\\" \''
Yanray Wang386c2f92023-07-20 15:32:15 +0800192 # unsupported combinations
Yanray Wang6a862582023-05-24 12:24:38 +0800193 else:
Yanray Wang21127f72023-07-19 12:09:45 +0800194 self.logger.error("Unsupported combination of architecture: {} " \
195 "and configuration: {}.\n"
Yanray Wang955671b2023-07-21 12:08:27 +0800196 .format(self.size_dist_info.arch,
197 self.size_dist_info.config))
Yanray Wang21127f72023-07-19 12:09:45 +0800198 self.logger.info("Please use supported combination of " \
199 "architecture and configuration:")
Yanray Wang923f9432023-07-17 12:43:00 +0800200 for comb in CodeSizeBuildInfo.SupportedArchConfig:
Yanray Wang21127f72023-07-19 12:09:45 +0800201 self.logger.info(comb)
202 self.logger.info("")
203 self.logger.info("For your system, please use:")
Yanray Wang923f9432023-07-17 12:43:00 +0800204 for comb in CodeSizeBuildInfo.SupportedArchConfig:
Yanray Wang802af162023-07-17 14:04:30 +0800205 if "default" in comb and self.host_arch not in comb:
Yanray Wang21f17442023-06-01 11:29:06 +0800206 continue
Yanray Wang21127f72023-07-19 12:09:45 +0800207 self.logger.info(comb)
Yanray Wang6a862582023-05-24 12:24:38 +0800208 sys.exit(1)
209
210
Yanray Wange0e27602023-07-14 17:37:45 +0800211class CodeSizeCalculator:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800212 """ A calculator to calculate code size of library/*.o based on
Yanray Wange0e27602023-07-14 17:37:45 +0800213 Git revision and code size measurement tool.
214 """
215
216 def __init__(
217 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800218 git_rev: str,
Yanray Wange0e27602023-07-14 17:37:45 +0800219 make_cmd: str,
Yanray Wang21127f72023-07-19 12:09:45 +0800220 measure_cmd: str,
221 logger: logging.Logger,
Yanray Wange0e27602023-07-14 17:37:45 +0800222 ) -> None:
223 """
Yanray Wang955671b2023-07-21 12:08:27 +0800224 :param git_rev: Git revision. (E.g: commit)
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800225 :param make_cmd: command to build library/*.o.
226 :param measure_cmd: command to measure code size for library/*.o.
227 :param logger: logging module
Yanray Wange0e27602023-07-14 17:37:45 +0800228 """
229 self.repo_path = "."
230 self.git_command = "git"
231 self.make_clean = 'make clean'
232
Yanray Wang955671b2023-07-21 12:08:27 +0800233 self.git_rev = git_rev
Yanray Wange0e27602023-07-14 17:37:45 +0800234 self.make_cmd = make_cmd
Yanray Wang802af162023-07-17 14:04:30 +0800235 self.measure_cmd = measure_cmd
Yanray Wang21127f72023-07-19 12:09:45 +0800236 self.logger = logger
Yanray Wange0e27602023-07-14 17:37:45 +0800237
238 @staticmethod
Yanray Wang955671b2023-07-21 12:08:27 +0800239 def validate_git_revision(git_rev: str) -> str:
Yanray Wange0e27602023-07-14 17:37:45 +0800240 result = subprocess.check_output(["git", "rev-parse", "--verify",
Yanray Wang955671b2023-07-21 12:08:27 +0800241 git_rev + "^{commit}"],
242 shell=False, universal_newlines=True)
Yanray Wang386c2f92023-07-20 15:32:15 +0800243 return result[:7]
Yanray Wange0e27602023-07-14 17:37:45 +0800244
Yanray Wang21127f72023-07-19 12:09:45 +0800245 def _create_git_worktree(self) -> str:
Yanray Wang955671b2023-07-21 12:08:27 +0800246 """Create a separate worktree for Git revision.
247 If Git revision is current, use current worktree instead."""
Yanray Wange0e27602023-07-14 17:37:45 +0800248
Yanray Wang955671b2023-07-21 12:08:27 +0800249 if self.git_rev == "current":
Yanray Wang21127f72023-07-19 12:09:45 +0800250 self.logger.debug("Using current work directory.")
Yanray Wange0e27602023-07-14 17:37:45 +0800251 git_worktree_path = self.repo_path
252 else:
Yanray Wang21127f72023-07-19 12:09:45 +0800253 self.logger.debug("Creating git worktree for {}."
Yanray Wang955671b2023-07-21 12:08:27 +0800254 .format(self.git_rev))
Yanray Wang21127f72023-07-19 12:09:45 +0800255 git_worktree_path = os.path.join(self.repo_path,
Yanray Wang955671b2023-07-21 12:08:27 +0800256 "temp-" + self.git_rev)
Yanray Wange0e27602023-07-14 17:37:45 +0800257 subprocess.check_output(
258 [self.git_command, "worktree", "add", "--detach",
Yanray Wang955671b2023-07-21 12:08:27 +0800259 git_worktree_path, self.git_rev], cwd=self.repo_path,
Yanray Wange0e27602023-07-14 17:37:45 +0800260 stderr=subprocess.STDOUT
261 )
262
263 return git_worktree_path
264
265 def _build_libraries(self, git_worktree_path: str) -> None:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800266 """Build library/*.o in the specified worktree."""
Yanray Wange0e27602023-07-14 17:37:45 +0800267
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800268 self.logger.debug("Building library/*.o for {}."
Yanray Wang955671b2023-07-21 12:08:27 +0800269 .format(self.git_rev))
Yanray Wange0e27602023-07-14 17:37:45 +0800270 my_environment = os.environ.copy()
271 try:
272 subprocess.check_output(
273 self.make_clean, env=my_environment, shell=True,
274 cwd=git_worktree_path, stderr=subprocess.STDOUT,
Yanray Wang386c2f92023-07-20 15:32:15 +0800275 universal_newlines=True
Yanray Wange0e27602023-07-14 17:37:45 +0800276 )
277 subprocess.check_output(
278 self.make_cmd, env=my_environment, shell=True,
279 cwd=git_worktree_path, stderr=subprocess.STDOUT,
Yanray Wang386c2f92023-07-20 15:32:15 +0800280 universal_newlines=True
Yanray Wange0e27602023-07-14 17:37:45 +0800281 )
282 except subprocess.CalledProcessError as e:
283 self._handle_called_process_error(e, git_worktree_path)
284
Yanray Wang386c2f92023-07-20 15:32:15 +0800285 def _gen_raw_code_size(self, git_worktree_path: str) -> typing.Dict[str, str]:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800286 """Measure code size by a tool and return in UTF-8 encoding."""
Yanray Wang21127f72023-07-19 12:09:45 +0800287
288 self.logger.debug("Measuring code size for {} by `{}`."
Yanray Wang955671b2023-07-21 12:08:27 +0800289 .format(self.git_rev,
Yanray Wang21127f72023-07-19 12:09:45 +0800290 self.measure_cmd.strip().split(' ')[0]))
Yanray Wange0e27602023-07-14 17:37:45 +0800291
292 res = {}
293 for mod, st_lib in MBEDTLS_STATIC_LIB.items():
294 try:
295 result = subprocess.check_output(
Yanray Wang802af162023-07-17 14:04:30 +0800296 [self.measure_cmd + ' ' + st_lib], cwd=git_worktree_path,
297 shell=True, universal_newlines=True
Yanray Wange0e27602023-07-14 17:37:45 +0800298 )
299 res[mod] = result
300 except subprocess.CalledProcessError as e:
301 self._handle_called_process_error(e, git_worktree_path)
302
303 return res
304
305 def _remove_worktree(self, git_worktree_path: str) -> None:
306 """Remove temporary worktree."""
307 if git_worktree_path != self.repo_path:
Yanray Wang21127f72023-07-19 12:09:45 +0800308 self.logger.debug("Removing temporary worktree {}."
309 .format(git_worktree_path))
Yanray Wange0e27602023-07-14 17:37:45 +0800310 subprocess.check_output(
311 [self.git_command, "worktree", "remove", "--force",
312 git_worktree_path], cwd=self.repo_path,
313 stderr=subprocess.STDOUT
314 )
315
316 def _handle_called_process_error(self, e: subprocess.CalledProcessError,
317 git_worktree_path: str) -> None:
318 """Handle a CalledProcessError and quit the program gracefully.
319 Remove any extra worktrees so that the script may be called again."""
320
321 # Tell the user what went wrong
Yanray Wang21127f72023-07-19 12:09:45 +0800322 self.logger.error(e, exc_info=True)
Yanray Wang386c2f92023-07-20 15:32:15 +0800323 self.logger.error("Process output:\n {}".format(e.output))
Yanray Wange0e27602023-07-14 17:37:45 +0800324
325 # Quit gracefully by removing the existing worktree
326 self._remove_worktree(git_worktree_path)
327 sys.exit(-1)
328
Yanray Wang386c2f92023-07-20 15:32:15 +0800329 def cal_libraries_code_size(self) -> typing.Dict[str, str]:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800330 """Do a complete round to calculate code size of library/*.o
331 by measurement tool.
332
333 :return A dictionary of measured code size
334 - typing.Dict[mod: str]
335 """
Yanray Wange0e27602023-07-14 17:37:45 +0800336
Yanray Wang21127f72023-07-19 12:09:45 +0800337 git_worktree_path = self._create_git_worktree()
Yanray Wange0e27602023-07-14 17:37:45 +0800338 self._build_libraries(git_worktree_path)
Yanray Wang21127f72023-07-19 12:09:45 +0800339 res = self._gen_raw_code_size(git_worktree_path)
Yanray Wange0e27602023-07-14 17:37:45 +0800340 self._remove_worktree(git_worktree_path)
341
342 return res
343
344
Yanray Wang15c43f32023-07-17 11:17:12 +0800345class CodeSizeGenerator:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800346 """ A generator based on size measurement tool for library/*.o.
Yanray Wang15c43f32023-07-17 11:17:12 +0800347
348 This is an abstract class. To use it, derive a class that implements
349 size_generator_write_record and size_generator_write_comparison methods,
350 then call both of them with proper arguments.
351 """
Yanray Wang21127f72023-07-19 12:09:45 +0800352 def __init__(self, logger: logging.Logger) -> None:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800353 """
354 :param logger: logging module
355 """
Yanray Wang21127f72023-07-19 12:09:45 +0800356 self.logger = logger
357
Yanray Wang15c43f32023-07-17 11:17:12 +0800358 def size_generator_write_record(
359 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800360 git_rev: str,
Yanray Wang15c43f32023-07-17 11:17:12 +0800361 code_size_text: typing.Dict,
362 output_file: str
363 ) -> None:
364 """Write size record into a file.
365
Yanray Wang955671b2023-07-21 12:08:27 +0800366 :param git_rev: Git revision. (E.g: commit)
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800367 :param code_size_text:
368 string output (utf-8) from measurement tool of code size.
369 - typing.Dict[mod: str]
370 :param output_file: file which the code size record is written to.
Yanray Wang15c43f32023-07-17 11:17:12 +0800371 """
372 raise NotImplementedError
373
374 def size_generator_write_comparison(
375 self,
376 old_rev: str,
377 new_rev: str,
Yanray Wang386c2f92023-07-20 15:32:15 +0800378 output_stream: str,
Yanray Wang955671b2023-07-21 12:08:27 +0800379 result_options: CodeSizeResultInfo
Yanray Wang15c43f32023-07-17 11:17:12 +0800380 ) -> None:
Yanray Wang955671b2023-07-21 12:08:27 +0800381 """Write a comparision result into a stream between two Git revisions.
Yanray Wang15c43f32023-07-17 11:17:12 +0800382
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800383 :param old_rev: old Git revision to compared with.
384 :param new_rev: new Git revision to compared with.
385 :param output_stream: stream which the code size record is written to.
386 :param result_options:
Yanray Wang955671b2023-07-21 12:08:27 +0800387 CodeSizeResultInfo containing options for comparison result.
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800388 - result_options.with_markdown: write comparision result in a
389 markdown table. (Default: False)
390 - result_options.stdout: direct comparison result into
391 sys.stdout. (Default: False)
Yanray Wang15c43f32023-07-17 11:17:12 +0800392 """
393 raise NotImplementedError
394
395
396class CodeSizeGeneratorWithSize(CodeSizeGenerator):
Yanray Wang16ebc572023-05-30 18:10:20 +0800397 """Code Size Base Class for size record saving and writing."""
398
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800399 class SizeEntry: # pylint: disable=too-few-public-methods
400 """Data Structure to only store information of code size."""
401 def __init__(self, text, data, bss, dec):
402 self.text = text
403 self.data = data
404 self.bss = bss
405 self.total = dec # total <=> dec
406
Yanray Wang21127f72023-07-19 12:09:45 +0800407 def __init__(self, logger: logging.Logger) -> None:
Yanray Wang955671b2023-07-21 12:08:27 +0800408 """ Variable code_size is used to store size info for any Git revisions.
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800409 :param code_size:
410 Data Format as following:
Yanray Wang955671b2023-07-21 12:08:27 +0800411 {git_rev: {module: {file_name: [text, data, bss, dec],
412 etc ...
413 },
414 etc ...
415 },
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800416 etc ...
417 }
Yanray Wang16ebc572023-05-30 18:10:20 +0800418 """
Yanray Wang21127f72023-07-19 12:09:45 +0800419 super().__init__(logger)
Yanray Wang16ebc572023-05-30 18:10:20 +0800420 self.code_size = {} #type: typing.Dict[str, typing.Dict]
421
Yanray Wang955671b2023-07-21 12:08:27 +0800422 def _set_size_record(self, git_rev: str, mod: str, size_text: str) -> None:
423 """Store size information for target Git revision and high-level module.
Yanray Wang16ebc572023-05-30 18:10:20 +0800424
425 size_text Format: text data bss dec hex filename
426 """
427 size_record = {}
428 for line in size_text.splitlines()[1:]:
429 data = line.split()
Yanray Wang9b174e92023-07-17 17:59:53 +0800430 # file_name: SizeEntry(text, data, bss, dec)
431 size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(
432 data[0], data[1], data[2], data[3])
Yanray Wang955671b2023-07-21 12:08:27 +0800433 if git_rev in self.code_size:
434 self.code_size[git_rev].update({mod: size_record})
Yanray Wang16ebc572023-05-30 18:10:20 +0800435 else:
Yanray Wang955671b2023-07-21 12:08:27 +0800436 self.code_size[git_rev] = {mod: size_record}
Yanray Wang16ebc572023-05-30 18:10:20 +0800437
Yanray Wang955671b2023-07-21 12:08:27 +0800438 def read_size_record(self, git_rev: str, fname: str) -> None:
Yanray Wang16ebc572023-05-30 18:10:20 +0800439 """Read size information from csv file and write it into code_size.
440
441 fname Format: filename text data bss dec
442 """
443 mod = ""
444 size_record = {}
445 with open(fname, 'r') as csv_file:
446 for line in csv_file:
447 data = line.strip().split()
448 # check if we find the beginning of a module
449 if data and data[0] in MBEDTLS_STATIC_LIB:
450 mod = data[0]
451 continue
452
453 if mod:
Yanray Wang9b174e92023-07-17 17:59:53 +0800454 # file_name: SizeEntry(text, data, bss, dec)
455 size_record[data[0]] = CodeSizeGeneratorWithSize.SizeEntry(
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800456 data[1], data[2], data[3], data[4])
Yanray Wang16ebc572023-05-30 18:10:20 +0800457
458 # check if we hit record for the end of a module
459 m = re.match(r'.?TOTALS', line)
460 if m:
Yanray Wang955671b2023-07-21 12:08:27 +0800461 if git_rev in self.code_size:
462 self.code_size[git_rev].update({mod: size_record})
Yanray Wang16ebc572023-05-30 18:10:20 +0800463 else:
Yanray Wang955671b2023-07-21 12:08:27 +0800464 self.code_size[git_rev] = {mod: size_record}
Yanray Wang16ebc572023-05-30 18:10:20 +0800465 mod = ""
466 size_record = {}
467
468 def _size_reader_helper(
469 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800470 git_rev: str,
Yanray Wangb664cb72023-07-18 12:28:35 +0800471 output: typing_util.Writable,
472 with_markdown=False
Yanray Wang16ebc572023-05-30 18:10:20 +0800473 ) -> typing.Iterator[tuple]:
Yanray Wang955671b2023-07-21 12:08:27 +0800474 """A helper function to peel code_size based on Git revision."""
475 for mod, file_size in self.code_size[git_rev].items():
Yanray Wangb664cb72023-07-18 12:28:35 +0800476 if not with_markdown:
477 output.write("\n" + mod + "\n")
Yanray Wang16ebc572023-05-30 18:10:20 +0800478 for fname, size_entry in file_size.items():
479 yield mod, fname, size_entry
480
Yanray Wang386c2f92023-07-20 15:32:15 +0800481 def _write_size_record(
Yanray Wang16ebc572023-05-30 18:10:20 +0800482 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800483 git_rev: str,
Yanray Wang16ebc572023-05-30 18:10:20 +0800484 output: typing_util.Writable
485 ) -> None:
486 """Write size information to a file.
487
488 Writing Format: file_name text data bss total(dec)
489 """
Yanray Wangb664cb72023-07-18 12:28:35 +0800490 format_string = "{:<30} {:>7} {:>7} {:>7} {:>7}\n"
491 output.write(format_string.format("filename",
492 "text", "data", "bss", "total"))
Yanray Wang955671b2023-07-21 12:08:27 +0800493 for _, fname, size_entry in self._size_reader_helper(git_rev, output):
Yanray Wangb664cb72023-07-18 12:28:35 +0800494 output.write(format_string.format(fname,
495 size_entry.text, size_entry.data,
496 size_entry.bss, size_entry.total))
Yanray Wang16ebc572023-05-30 18:10:20 +0800497
Yanray Wang386c2f92023-07-20 15:32:15 +0800498 def _write_comparison(
Yanray Wang16ebc572023-05-30 18:10:20 +0800499 self,
500 old_rev: str,
501 new_rev: str,
Yanray Wangb664cb72023-07-18 12:28:35 +0800502 output: typing_util.Writable,
503 with_markdown: bool
Yanray Wang16ebc572023-05-30 18:10:20 +0800504 ) -> None:
505 """Write comparison result into a file.
506
Yanray Wang9b174e92023-07-17 17:59:53 +0800507 Writing Format: file_name current(text,data) old(text,data)\
508 change(text,data) change_pct%(text,data)
Yanray Wang16ebc572023-05-30 18:10:20 +0800509 """
Yanray Wang9b174e92023-07-17 17:59:53 +0800510
511 def cal_size_section_variation(mod, fname, size_entry, attr):
512 new_size = int(size_entry.__dict__[attr])
Yanray Wang955671b2023-07-21 12:08:27 +0800513 # check if we have the file in old Git revision
Yanray Wang16ebc572023-05-30 18:10:20 +0800514 if fname in self.code_size[old_rev][mod]:
Yanray Wang9b174e92023-07-17 17:59:53 +0800515 old_size = int(self.code_size[old_rev][mod][fname].__dict__[attr])
Yanray Wang16ebc572023-05-30 18:10:20 +0800516 change = new_size - old_size
517 if old_size != 0:
518 change_pct = change / old_size
519 else:
520 change_pct = 0
Yanray Wang9b174e92023-07-17 17:59:53 +0800521 return [new_size, old_size, change, change_pct]
Yanray Wang16ebc572023-05-30 18:10:20 +0800522 else:
Yanray Wang9b174e92023-07-17 17:59:53 +0800523 return [new_size]
524
Yanray Wangb664cb72023-07-18 12:28:35 +0800525 if with_markdown:
526 format_string = "| {:<30} | {:<18} | {:<14} | {:<17} | {:<18} |\n"
527 else:
528 format_string = "{:<30} {:<18} {:<14} {:<17} {:<18}\n"
529
Yanray Wang386c2f92023-07-20 15:32:15 +0800530 output.write(format_string
531 .format("filename",
532 "current(text,data)", "old(text,data)",
533 "change(text,data)", "change%(text,data)"))
Yanray Wangb664cb72023-07-18 12:28:35 +0800534 if with_markdown:
535 output.write(format_string
536 .format("----:", "----:", "----:", "----:", "----:"))
537
Yanray Wang386c2f92023-07-20 15:32:15 +0800538 for mod, fname, size_entry in \
Yanray Wangb664cb72023-07-18 12:28:35 +0800539 self._size_reader_helper(new_rev, output, with_markdown):
540 text_vari = cal_size_section_variation(mod, fname,
541 size_entry, 'text')
542 data_vari = cal_size_section_variation(mod, fname,
543 size_entry, 'data')
Yanray Wang9b174e92023-07-17 17:59:53 +0800544
545 if len(text_vari) != 1:
Yanray Wangb664cb72023-07-18 12:28:35 +0800546 # skip the files that haven't changed in code size if we write
547 # comparison result in a markdown table.
548 if with_markdown and text_vari[2] == 0 and data_vari[2] == 0:
549 continue
Yanray Wang386c2f92023-07-20 15:32:15 +0800550 output.write(
551 format_string
552 .format(fname,
553 str(text_vari[0]) + "," + str(data_vari[0]),
554 str(text_vari[1]) + "," + str(data_vari[1]),
555 str(text_vari[2]) + "," + str(data_vari[2]),
556 "{:.2%}".format(text_vari[3]) + ","
557 + "{:.2%}".format(data_vari[3])))
Yanray Wang9b174e92023-07-17 17:59:53 +0800558 else:
Yanray Wang386c2f92023-07-20 15:32:15 +0800559 output.write("{:<30} {:<18}\n"
560 .format(fname,
561 str(text_vari[0]) + "," + str(data_vari[0])))
Yanray Wang16ebc572023-05-30 18:10:20 +0800562
Yanray Wang15c43f32023-07-17 11:17:12 +0800563 def size_generator_write_record(
564 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800565 git_rev: str,
Yanray Wang15c43f32023-07-17 11:17:12 +0800566 code_size_text: typing.Dict,
567 output_file: str
568 ) -> None:
569 """Write size record into a specified file based on Git revision and
570 output from `size` tool."""
Yanray Wang955671b2023-07-21 12:08:27 +0800571 self.logger.debug("Generating code size csv for {}.".format(git_rev))
Yanray Wang21127f72023-07-19 12:09:45 +0800572
Yanray Wang15c43f32023-07-17 11:17:12 +0800573 for mod, size_text in code_size_text.items():
Yanray Wang955671b2023-07-21 12:08:27 +0800574 self._set_size_record(git_rev, mod, size_text)
Yanray Wang15c43f32023-07-17 11:17:12 +0800575
Yanray Wang15c43f32023-07-17 11:17:12 +0800576 output = open(output_file, "w")
Yanray Wang955671b2023-07-21 12:08:27 +0800577 self._write_size_record(git_rev, output)
Yanray Wang15c43f32023-07-17 11:17:12 +0800578
579 def size_generator_write_comparison(
580 self,
581 old_rev: str,
582 new_rev: str,
Yanray Wang386c2f92023-07-20 15:32:15 +0800583 output_stream: str,
Yanray Wang955671b2023-07-21 12:08:27 +0800584 result_options: CodeSizeResultInfo
Yanray Wang15c43f32023-07-17 11:17:12 +0800585 ) -> None:
Yanray Wang955671b2023-07-21 12:08:27 +0800586 """Write a comparision result into a stream between two Git revisions.
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800587
588 By default, it's written into a file called output_stream.
589 Once result_options.stdout is set, it's written into sys.stdout instead.
590 """
Yanray Wang21127f72023-07-19 12:09:45 +0800591 self.logger.debug("Generating comparison results between {} and {}."
592 .format(old_rev, new_rev))
593
Yanray Wang227576a2023-07-18 14:35:05 +0800594 if result_options.stdout:
595 output = sys.stdout
596 else:
597 output = open(output_stream, "w")
Yanray Wang386c2f92023-07-20 15:32:15 +0800598 self._write_comparison(old_rev, new_rev, output,
599 result_options.with_markdown)
Yanray Wang15c43f32023-07-17 11:17:12 +0800600
Yanray Wang16ebc572023-05-30 18:10:20 +0800601
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800602class CodeSizeComparison:
Xiaofei Bai2400b502021-10-21 12:22:58 +0000603 """Compare code size between two Git revisions."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000604
Yanray Wang955671b2023-07-21 12:08:27 +0800605 def __init__( #pylint: disable=too-many-arguments
Yanray Wang72b105f2023-05-31 15:20:39 +0800606 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800607 old_size_dist_info: CodeSizeDistinctInfo,
608 new_size_dist_info: CodeSizeDistinctInfo,
609 size_common_info: CodeSizeCommonInfo,
610 result_options: CodeSizeResultInfo,
Yanray Wang21127f72023-07-19 12:09:45 +0800611 logger: logging.Logger,
Yanray Wang72b105f2023-05-31 15:20:39 +0800612 ) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000613 """
Yanray Wang955671b2023-07-21 12:08:27 +0800614 :param old_size_dist_info: CodeSizeDistinctInfo containing old distinct
615 info to compare code size with.
616 :param new_size_dist_info: CodeSizeDistinctInfo containing new distinct
617 info to take as comparision base.
618 :param size_common_info: CodeSizeCommonInfo containing common info for
619 both old and new size distinct info and
620 measurement tool.
621 :param result_options: CodeSizeResultInfo containing results options for
622 code size record and comparision.
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800623 :param logger: logging module
Xiaofei Baibca03e52021-09-09 09:42:37 +0000624 """
Xiaofei Baibca03e52021-09-09 09:42:37 +0000625
Yanray Wang21127f72023-07-19 12:09:45 +0800626 self.logger = logger
627
Yanray Wang955671b2023-07-21 12:08:27 +0800628 self.old_size_dist_info = old_size_dist_info
629 self.new_size_dist_info = new_size_dist_info
630 self.size_common_info = size_common_info
Yanray Wang386c2f92023-07-20 15:32:15 +0800631 # infer make command
Yanray Wang955671b2023-07-21 12:08:27 +0800632 self.old_size_dist_info.make_cmd = CodeSizeBuildInfo(
633 self.old_size_dist_info, self.size_common_info.host_arch,
Yanray Wang21127f72023-07-19 12:09:45 +0800634 self.logger).infer_make_command()
Yanray Wang955671b2023-07-21 12:08:27 +0800635 self.new_size_dist_info.make_cmd = CodeSizeBuildInfo(
636 self.new_size_dist_info, self.size_common_info.host_arch,
Yanray Wang21127f72023-07-19 12:09:45 +0800637 self.logger).infer_make_command()
Yanray Wang386c2f92023-07-20 15:32:15 +0800638 # initialize size parser with corresponding measurement tool
Yanray Wang21127f72023-07-19 12:09:45 +0800639 self.code_size_generator = self.__generate_size_parser()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000640
Yanray Wang955671b2023-07-21 12:08:27 +0800641 self.result_options = result_options
642 self.csv_dir = os.path.abspath(self.result_options.record_dir)
643 os.makedirs(self.csv_dir, exist_ok=True)
644 self.comp_dir = os.path.abspath(self.result_options.comp_dir)
645 os.makedirs(self.comp_dir, exist_ok=True)
646
Yanray Wang21127f72023-07-19 12:09:45 +0800647 def __generate_size_parser(self):
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800648 """Generate a parser for the corresponding measurement tool."""
Yanray Wang955671b2023-07-21 12:08:27 +0800649 if re.match(r'size', self.size_common_info.measure_cmd.strip()):
Yanray Wang21127f72023-07-19 12:09:45 +0800650 return CodeSizeGeneratorWithSize(self.logger)
Yanray Wang802af162023-07-17 14:04:30 +0800651 else:
Yanray Wang21127f72023-07-19 12:09:45 +0800652 self.logger.error("Unsupported measurement tool: `{}`."
Yanray Wang955671b2023-07-21 12:08:27 +0800653 .format(self.size_common_info.measure_cmd
Yanray Wang21127f72023-07-19 12:09:45 +0800654 .strip().split(' ')[0]))
Yanray Wang802af162023-07-17 14:04:30 +0800655 sys.exit(1)
656
657
Yanray Wang386c2f92023-07-20 15:32:15 +0800658 def cal_code_size(
659 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800660 size_dist_info: CodeSizeDistinctInfo
Yanray Wang386c2f92023-07-20 15:32:15 +0800661 ) -> typing.Dict[str, str]:
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800662 """Calculate code size of library/*.o in a UTF-8 encoding"""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000663
Yanray Wang955671b2023-07-21 12:08:27 +0800664 return CodeSizeCalculator(size_dist_info.git_rev,
665 size_dist_info.make_cmd,
666 self.size_common_info.measure_cmd,
Yanray Wang21127f72023-07-19 12:09:45 +0800667 self.logger).cal_libraries_code_size()
Yanray Wang8804db92023-05-30 18:18:18 +0800668
Yanray Wang386c2f92023-07-20 15:32:15 +0800669 def gen_file_name(
670 self,
Yanray Wang955671b2023-07-21 12:08:27 +0800671 old_size_dist_info: CodeSizeDistinctInfo,
672 new_size_dist_info=None
Yanray Wang386c2f92023-07-20 15:32:15 +0800673 ) -> str:
Yanray Wang21127f72023-07-19 12:09:45 +0800674 """Generate a literal string as csv file name."""
Yanray Wang955671b2023-07-21 12:08:27 +0800675 if new_size_dist_info:
Yanray Wang802af162023-07-17 14:04:30 +0800676 return '{}-{}-{}-{}-{}-{}-{}.csv'\
Yanray Wang955671b2023-07-21 12:08:27 +0800677 .format(old_size_dist_info.git_rev, old_size_dist_info.arch,
678 old_size_dist_info.config,
679 new_size_dist_info.git_rev, new_size_dist_info.arch,
680 new_size_dist_info.config,
681 self.size_common_info.measure_cmd.strip()\
Yanray Wang386c2f92023-07-20 15:32:15 +0800682 .split(' ')[0])
Yanray Wang923f9432023-07-17 12:43:00 +0800683 else:
Yanray Wang802af162023-07-17 14:04:30 +0800684 return '{}-{}-{}-{}.csv'\
Yanray Wang955671b2023-07-21 12:08:27 +0800685 .format(old_size_dist_info.git_rev,
686 old_size_dist_info.arch,
687 old_size_dist_info.config,
688 self.size_common_info.measure_cmd.strip()\
Yanray Wang386c2f92023-07-20 15:32:15 +0800689 .split(' ')[0])
Yanray Wang923f9432023-07-17 12:43:00 +0800690
Yanray Wang955671b2023-07-21 12:08:27 +0800691 def gen_code_size_report(self, size_dist_info: CodeSizeDistinctInfo) -> None:
Yanray Wang5e9130a2023-07-17 11:55:54 +0800692 """Generate code size record and write it into a file."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000693
Yanray Wang21127f72023-07-19 12:09:45 +0800694 self.logger.info("Start to generate code size record for {}."
Yanray Wang955671b2023-07-21 12:08:27 +0800695 .format(size_dist_info.git_rev))
Yanray Wang21127f72023-07-19 12:09:45 +0800696 output_file = os.path.join(self.csv_dir,
Yanray Wang955671b2023-07-21 12:08:27 +0800697 self.gen_file_name(size_dist_info))
Xiaofei Baibca03e52021-09-09 09:42:37 +0000698 # Check if the corresponding record exists
Yanray Wang955671b2023-07-21 12:08:27 +0800699 if size_dist_info.git_rev != "current" and \
Yanray Wang21127f72023-07-19 12:09:45 +0800700 os.path.exists(output_file):
701 self.logger.debug("Code size csv file for {} already exists."
Yanray Wang955671b2023-07-21 12:08:27 +0800702 .format(size_dist_info.git_rev))
Yanray Wang21127f72023-07-19 12:09:45 +0800703 self.code_size_generator.read_size_record(
Yanray Wang955671b2023-07-21 12:08:27 +0800704 size_dist_info.git_rev, output_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000705 else:
Yanray Wang386c2f92023-07-20 15:32:15 +0800706 self.code_size_generator.size_generator_write_record(
Yanray Wang955671b2023-07-21 12:08:27 +0800707 size_dist_info.git_rev, self.cal_code_size(size_dist_info),
Yanray Wang386c2f92023-07-20 15:32:15 +0800708 output_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000709
Yanray Wang386c2f92023-07-20 15:32:15 +0800710 def gen_code_size_comparison(self) -> None:
Yanray Wang955671b2023-07-21 12:08:27 +0800711 """Generate results of code size changes between two Git revisions,
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800712 old and new.
713
Yanray Wang955671b2023-07-21 12:08:27 +0800714 - Measured code size result of these two Git revisions must be available.
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800715 - The result is directed into either file / stdout depending on
Yanray Wang955671b2023-07-21 12:08:27 +0800716 the option, size_common_info.result_options.stdout. (Default: file)
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800717 """
Xiaofei Baibca03e52021-09-09 09:42:37 +0000718
Yanray Wang21127f72023-07-19 12:09:45 +0800719 self.logger.info("Start to generate comparision result between "\
720 "{} and {}."
Yanray Wang955671b2023-07-21 12:08:27 +0800721 .format(self.old_size_dist_info.git_rev,
722 self.new_size_dist_info.git_rev))
Yanray Wang21127f72023-07-19 12:09:45 +0800723 output_file = os.path.join(
Yanray Wang955671b2023-07-21 12:08:27 +0800724 self.comp_dir,
725 self.gen_file_name(self.old_size_dist_info, self.new_size_dist_info))
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000726
Yanray Wang21127f72023-07-19 12:09:45 +0800727 self.code_size_generator.size_generator_write_comparison(
Yanray Wang955671b2023-07-21 12:08:27 +0800728 self.old_size_dist_info.git_rev,
729 self.new_size_dist_info.git_rev,
730 output_file, self.result_options)
Yanray Wang21127f72023-07-19 12:09:45 +0800731
Yanray Wang386c2f92023-07-20 15:32:15 +0800732 def get_comparision_results(self) -> None:
Yanray Wang955671b2023-07-21 12:08:27 +0800733 """Compare size of library/*.o between self.old_size_dist_info and
734 self.old_size_dist_info and generate the result file."""
Gilles Peskined9071e72022-09-18 21:17:09 +0200735 build_tree.check_repo_path()
Yanray Wang955671b2023-07-21 12:08:27 +0800736 self.gen_code_size_report(self.old_size_dist_info)
737 self.gen_code_size_report(self.new_size_dist_info)
Yanray Wang386c2f92023-07-20 15:32:15 +0800738 self.gen_code_size_comparison()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000739
Yanray Wang923f9432023-07-17 12:43:00 +0800740
Xiaofei Bai2400b502021-10-21 12:22:58 +0000741def main():
Yanray Wang502c54f2023-05-31 11:41:36 +0800742 parser = argparse.ArgumentParser(description=(__doc__))
743 group_required = parser.add_argument_group(
744 'required arguments',
745 'required arguments to parse for running ' + os.path.basename(__file__))
746 group_required.add_argument(
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800747 '-o', '--old-rev', type=str, required=True,
Yanray Wang955671b2023-07-21 12:08:27 +0800748 help='old Git revision for comparison.')
Yanray Wang502c54f2023-05-31 11:41:36 +0800749
750 group_optional = parser.add_argument_group(
751 'optional arguments',
752 'optional arguments to parse for running ' + os.path.basename(__file__))
753 group_optional.add_argument(
Yanray Wang955671b2023-07-21 12:08:27 +0800754 '--record_dir', type=str, default='code_size_records',
755 help='directory where code size record is stored. '
756 '(Default: code_size_records)')
757 group_optional.add_argument(
758 '-r', '--comp-dir', type=str, default='comparison',
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800759 help='directory where comparison result is stored. '
760 '(Default: comparison)')
Yanray Wang502c54f2023-05-31 11:41:36 +0800761 group_optional.add_argument(
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800762 '-n', '--new-rev', type=str, default=None,
Yanray Wang955671b2023-07-21 12:08:27 +0800763 help='new Git revision as comparison base. '
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800764 '(Default is the current work directory, including uncommitted '
765 'changes.)')
Yanray Wang502c54f2023-05-31 11:41:36 +0800766 group_optional.add_argument(
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800767 '-a', '--arch', type=str, default=detect_arch(),
Yanray Wang23bd5322023-05-24 11:03:59 +0800768 choices=list(map(lambda s: s.value, SupportedArch)),
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800769 help='Specify architecture for code size comparison. '
770 '(Default is the host architecture.)')
Yanray Wang502c54f2023-05-31 11:41:36 +0800771 group_optional.add_argument(
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800772 '-c', '--config', type=str, default=SupportedConfig.DEFAULT.value,
Yanray Wang6a862582023-05-24 12:24:38 +0800773 choices=list(map(lambda s: s.value, SupportedConfig)),
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800774 help='Specify configuration type for code size comparison. '
775 '(Default is the current MbedTLS configuration.)')
Yanray Wangb664cb72023-07-18 12:28:35 +0800776 group_optional.add_argument(
777 '--markdown', action='store_true', dest='markdown',
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800778 help='Show comparision of code size in a markdown table. '
779 '(Only show the files that have changed).')
Yanray Wang227576a2023-07-18 14:35:05 +0800780 group_optional.add_argument(
781 '--stdout', action='store_true', dest='stdout',
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800782 help='Set this option to direct comparison result into sys.stdout. '
783 '(Default: file)')
Yanray Wang21127f72023-07-19 12:09:45 +0800784 group_optional.add_argument(
785 '--verbose', action='store_true', dest='verbose',
Yanray Wang5b64e4c2023-07-20 15:09:51 +0800786 help='Show logs in detail for code size measurement. '
787 '(Default: False)')
Xiaofei Baibca03e52021-09-09 09:42:37 +0000788 comp_args = parser.parse_args()
789
Yanray Wang21127f72023-07-19 12:09:45 +0800790 logger = logging.getLogger()
791 logging_util.configure_logger(logger)
792 logger.setLevel(logging.DEBUG if comp_args.verbose else logging.INFO)
793
Yanray Wang955671b2023-07-21 12:08:27 +0800794 if os.path.isfile(comp_args.comp_dir):
795 logger.error("{} is not a directory".format(comp_args.comp_dir))
Xiaofei Baibca03e52021-09-09 09:42:37 +0000796 parser.exit()
797
Yanray Wang955671b2023-07-21 12:08:27 +0800798 old_revision = CodeSizeCalculator.validate_git_revision(comp_args.old_rev)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000799 if comp_args.new_rev is not None:
Yanray Wang955671b2023-07-21 12:08:27 +0800800 new_revision = CodeSizeCalculator.validate_git_revision(
801 comp_args.new_rev)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000802 else:
803 new_revision = "current"
Xiaofei Bai2400b502021-10-21 12:22:58 +0000804
Yanray Wang955671b2023-07-21 12:08:27 +0800805 old_size_dist_info = CodeSizeDistinctInfo(
806 'old', old_revision, comp_args.arch, comp_args.config, '')
807 new_size_dist_info = CodeSizeDistinctInfo(
808 'new', new_revision, comp_args.arch, comp_args.config, '')
809 size_common_info = CodeSizeCommonInfo(
810 detect_arch(), 'size -t')
811 result_options = CodeSizeResultInfo(
812 comp_args.record_dir, comp_args.comp_dir,
813 comp_args.markdown, comp_args.stdout)
Yanray Wang923f9432023-07-17 12:43:00 +0800814
Yanray Wang21127f72023-07-19 12:09:45 +0800815 logger.info("Measure code size between {}:{}-{} and {}:{}-{} by `{}`."
Yanray Wang955671b2023-07-21 12:08:27 +0800816 .format(old_size_dist_info.git_rev, old_size_dist_info.config,
817 old_size_dist_info.arch,
818 new_size_dist_info.git_rev, old_size_dist_info.config,
819 new_size_dist_info.arch,
820 size_common_info.measure_cmd.strip().split(' ')[0]))
821 CodeSizeComparison(old_size_dist_info, new_size_dist_info,
822 size_common_info, result_options,
823 logger).get_comparision_results()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000824
Xiaofei Baibca03e52021-09-09 09:42:37 +0000825if __name__ == "__main__":
Xiaofei Bai2400b502021-10-21 12:22:58 +0000826 main()