blob: 6b2b3a9c4c7e93180ab80547684b710037b1e84c [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 Wang802af162023-07-17 14:04:30 +080091 def __init__(
92 self,
93 size_version: SimpleNamespace,
94 host_arch: str
95 ) -> None:
Yanray Wang6a862582023-05-24 12:24:38 +080096 """
Yanray Wang923f9432023-07-17 12:43:00 +080097 size_version: SimpleNamespace containing info for code size measurement.
98 size_version.arch: architecture to measure code size on.
99 size_version.config: configuration type to measure code size with.
Yanray Wang802af162023-07-17 14:04:30 +0800100 host_arch: host architecture.
Yanray Wang6a862582023-05-24 12:24:38 +0800101 """
Yanray Wang923f9432023-07-17 12:43:00 +0800102 self.size_version = size_version
Yanray Wang802af162023-07-17 14:04:30 +0800103 self.host_arch = host_arch
Yanray Wang6a862582023-05-24 12:24:38 +0800104
Yanray Wang923f9432023-07-17 12:43:00 +0800105 def infer_make_command(self) -> str:
Yanray Wang6a862582023-05-24 12:24:38 +0800106 """Infer build command based on architecture and configuration."""
107
Yanray Wang923f9432023-07-17 12:43:00 +0800108 if self.size_version.config == SupportedConfig.DEFAULT.value and \
Yanray Wang802af162023-07-17 14:04:30 +0800109 self.size_version.arch == self.host_arch:
Yanray Wang6a862582023-05-24 12:24:38 +0800110 return 'make -j lib CFLAGS=\'-Os \' '
Yanray Wang923f9432023-07-17 12:43:00 +0800111 elif self.size_version.arch == SupportedArch.ARMV8_M.value and \
112 self.size_version.config == SupportedConfig.TFM_MEDIUM.value:
Yanray Wang6a862582023-05-24 12:24:38 +0800113 return \
Yanray Wang60430bd2023-05-29 14:48:18 +0800114 'make -j lib CC=armclang \
Yanray Wang6a862582023-05-24 12:24:38 +0800115 CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
116 -DMBEDTLS_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_MBEDCRYPTO_H + '\\\" \
117 -DMBEDTLS_PSA_CRYPTO_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_PSA_CRYPTO_H + '\\\" \''
118 else:
Yanray Wang21f17442023-06-01 11:29:06 +0800119 print("Unsupported combination of architecture: {} and configuration: {}"
Yanray Wang923f9432023-07-17 12:43:00 +0800120 .format(self.size_version.arch, self.size_version.config))
Yanray Wangc18cd892023-05-31 11:08:04 +0800121 print("\nPlease use supported combination of architecture and configuration:")
Yanray Wang923f9432023-07-17 12:43:00 +0800122 for comb in CodeSizeBuildInfo.SupportedArchConfig:
Yanray Wangc18cd892023-05-31 11:08:04 +0800123 print(comb)
Yanray Wang21f17442023-06-01 11:29:06 +0800124 print("\nFor your system, please use:")
Yanray Wang923f9432023-07-17 12:43:00 +0800125 for comb in CodeSizeBuildInfo.SupportedArchConfig:
Yanray Wang802af162023-07-17 14:04:30 +0800126 if "default" in comb and self.host_arch not in comb:
Yanray Wang21f17442023-06-01 11:29:06 +0800127 continue
128 print(comb)
Yanray Wang6a862582023-05-24 12:24:38 +0800129 sys.exit(1)
130
131
Yanray Wange0e27602023-07-14 17:37:45 +0800132class CodeSizeCalculator:
133 """ A calculator to calculate code size of library objects based on
134 Git revision and code size measurement tool.
135 """
136
137 def __init__(
138 self,
139 revision: str,
140 make_cmd: str,
Yanray Wang802af162023-07-17 14:04:30 +0800141 measure_cmd: str
Yanray Wange0e27602023-07-14 17:37:45 +0800142 ) -> None:
143 """
144 revision: Git revision.(E.g: commit)
Yanray Wang802af162023-07-17 14:04:30 +0800145 make_cmd: command to build objects in library.
146 measure_cmd: command to measure code size for objects in library.
Yanray Wange0e27602023-07-14 17:37:45 +0800147 """
148 self.repo_path = "."
149 self.git_command = "git"
150 self.make_clean = 'make clean'
151
152 self.revision = revision
153 self.make_cmd = make_cmd
Yanray Wang802af162023-07-17 14:04:30 +0800154 self.measure_cmd = measure_cmd
Yanray Wange0e27602023-07-14 17:37:45 +0800155
156 @staticmethod
157 def validate_revision(revision: str) -> bytes:
158 result = subprocess.check_output(["git", "rev-parse", "--verify",
159 revision + "^{commit}"], shell=False)
160 return result
161
162 def _create_git_worktree(self, revision: str) -> str:
163 """Make a separate worktree for revision.
164 Do not modify the current worktree."""
165
166 if revision == "current":
167 print("Using current work directory")
168 git_worktree_path = self.repo_path
169 else:
170 print("Creating git worktree for", revision)
171 git_worktree_path = os.path.join(self.repo_path, "temp-" + revision)
172 subprocess.check_output(
173 [self.git_command, "worktree", "add", "--detach",
174 git_worktree_path, revision], cwd=self.repo_path,
175 stderr=subprocess.STDOUT
176 )
177
178 return git_worktree_path
179
180 def _build_libraries(self, git_worktree_path: str) -> None:
181 """Build libraries in the specified worktree."""
182
183 my_environment = os.environ.copy()
184 try:
185 subprocess.check_output(
186 self.make_clean, env=my_environment, shell=True,
187 cwd=git_worktree_path, stderr=subprocess.STDOUT,
188 )
189 subprocess.check_output(
190 self.make_cmd, env=my_environment, shell=True,
191 cwd=git_worktree_path, stderr=subprocess.STDOUT,
192 )
193 except subprocess.CalledProcessError as e:
194 self._handle_called_process_error(e, git_worktree_path)
195
196 def _gen_raw_code_size(self, revision, git_worktree_path):
197 """Calculate code size with measurement tool in UTF-8 encoding."""
198 if revision == "current":
199 print("Measuring code size in current work directory")
200 else:
201 print("Measuring code size for", revision)
202
203 res = {}
204 for mod, st_lib in MBEDTLS_STATIC_LIB.items():
205 try:
206 result = subprocess.check_output(
Yanray Wang802af162023-07-17 14:04:30 +0800207 [self.measure_cmd + ' ' + st_lib], cwd=git_worktree_path,
208 shell=True, universal_newlines=True
Yanray Wange0e27602023-07-14 17:37:45 +0800209 )
210 res[mod] = result
211 except subprocess.CalledProcessError as e:
212 self._handle_called_process_error(e, git_worktree_path)
213
214 return res
215
216 def _remove_worktree(self, git_worktree_path: str) -> None:
217 """Remove temporary worktree."""
218 if git_worktree_path != self.repo_path:
219 print("Removing temporary worktree", git_worktree_path)
220 subprocess.check_output(
221 [self.git_command, "worktree", "remove", "--force",
222 git_worktree_path], cwd=self.repo_path,
223 stderr=subprocess.STDOUT
224 )
225
226 def _handle_called_process_error(self, e: subprocess.CalledProcessError,
227 git_worktree_path: str) -> None:
228 """Handle a CalledProcessError and quit the program gracefully.
229 Remove any extra worktrees so that the script may be called again."""
230
231 # Tell the user what went wrong
232 print("The following command: {} failed and exited with code {}"
233 .format(e.cmd, e.returncode))
234 print("Process output:\n {}".format(str(e.output, "utf-8")))
235
236 # Quit gracefully by removing the existing worktree
237 self._remove_worktree(git_worktree_path)
238 sys.exit(-1)
239
240 def cal_libraries_code_size(self) -> typing.Dict:
241 """Calculate code size of libraries by measurement tool."""
242
243 revision = self.revision
244 git_worktree_path = self._create_git_worktree(revision)
245 self._build_libraries(git_worktree_path)
246 res = self._gen_raw_code_size(revision, git_worktree_path)
247 self._remove_worktree(git_worktree_path)
248
249 return res
250
251
Yanray Wang15c43f32023-07-17 11:17:12 +0800252class CodeSizeGenerator:
253 """ A generator based on size measurement tool for library objects.
254
255 This is an abstract class. To use it, derive a class that implements
256 size_generator_write_record and size_generator_write_comparison methods,
257 then call both of them with proper arguments.
258 """
259 def size_generator_write_record(
260 self,
261 revision: str,
262 code_size_text: typing.Dict,
263 output_file: str
264 ) -> None:
265 """Write size record into a file.
266
267 revision: Git revision.(E.g: commit)
268 code_size_text: text output (utf-8) from code size measurement tool.
269 output_file: file which the code size record is written to.
270 """
271 raise NotImplementedError
272
273 def size_generator_write_comparison(
274 self,
275 old_rev: str,
276 new_rev: str,
277 output_stream
278 ) -> None:
279 """Write a comparision result into a stream between two revisions.
280
281 old_rev: old git revision to compared with.
282 new_rev: new git revision to compared with.
283 output_stream: stream which the code size record is written to.
284 (E.g: file / sys.stdout)
285 """
286 raise NotImplementedError
287
288
289class CodeSizeGeneratorWithSize(CodeSizeGenerator):
Yanray Wang16ebc572023-05-30 18:10:20 +0800290 """Code Size Base Class for size record saving and writing."""
291
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800292 class SizeEntry: # pylint: disable=too-few-public-methods
293 """Data Structure to only store information of code size."""
294 def __init__(self, text, data, bss, dec):
295 self.text = text
296 self.data = data
297 self.bss = bss
298 self.total = dec # total <=> dec
299
Yanray Wang16ebc572023-05-30 18:10:20 +0800300 def __init__(self) -> None:
301 """ Variable code_size is used to store size info for any revisions.
302 code_size: (data format)
303 {revision: {module: {file_name: SizeEntry,
304 etc ...
305 },
306 etc ...
307 },
308 etc ...
309 }
310 """
311 self.code_size = {} #type: typing.Dict[str, typing.Dict]
312
313 def set_size_record(self, revision: str, mod: str, size_text: str) -> None:
314 """Store size information for target revision and high-level module.
315
316 size_text Format: text data bss dec hex filename
317 """
318 size_record = {}
319 for line in size_text.splitlines()[1:]:
320 data = line.split()
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800321 size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(\
322 data[0], data[1], data[2], data[3])
Yanray Wang16ebc572023-05-30 18:10:20 +0800323 if revision in self.code_size:
324 self.code_size[revision].update({mod: size_record})
325 else:
326 self.code_size[revision] = {mod: size_record}
327
328 def read_size_record(self, revision: str, fname: str) -> None:
329 """Read size information from csv file and write it into code_size.
330
331 fname Format: filename text data bss dec
332 """
333 mod = ""
334 size_record = {}
335 with open(fname, 'r') as csv_file:
336 for line in csv_file:
337 data = line.strip().split()
338 # check if we find the beginning of a module
339 if data and data[0] in MBEDTLS_STATIC_LIB:
340 mod = data[0]
341 continue
342
343 if mod:
344 size_record[data[0]] = \
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800345 CodeSizeGeneratorWithSize.SizeEntry(\
346 data[1], data[2], data[3], data[4])
Yanray Wang16ebc572023-05-30 18:10:20 +0800347
348 # check if we hit record for the end of a module
349 m = re.match(r'.?TOTALS', line)
350 if m:
351 if revision in self.code_size:
352 self.code_size[revision].update({mod: size_record})
353 else:
354 self.code_size[revision] = {mod: size_record}
355 mod = ""
356 size_record = {}
357
358 def _size_reader_helper(
359 self,
360 revision: str,
361 output: typing_util.Writable
362 ) -> typing.Iterator[tuple]:
363 """A helper function to peel code_size based on revision."""
364 for mod, file_size in self.code_size[revision].items():
365 output.write("\n" + mod + "\n")
366 for fname, size_entry in file_size.items():
367 yield mod, fname, size_entry
368
369 def write_size_record(
370 self,
371 revision: str,
372 output: typing_util.Writable
373 ) -> None:
374 """Write size information to a file.
375
376 Writing Format: file_name text data bss total(dec)
377 """
378 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
379 .format("filename", "text", "data", "bss", "total"))
380 for _, fname, size_entry in self._size_reader_helper(revision, output):
381 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
382 .format(fname, size_entry.text, size_entry.data,\
383 size_entry.bss, size_entry.total))
384
385 def write_comparison(
386 self,
387 old_rev: str,
388 new_rev: str,
389 output: typing_util.Writable
390 ) -> None:
391 """Write comparison result into a file.
392
393 Writing Format: file_name current(total) old(total) change(Byte) change_pct(%)
394 """
395 output.write("{:<30} {:>7} {:>7} {:>7} {:>7}\n"
396 .format("filename", "current", "old", "change", "change%"))
397 for mod, fname, size_entry in self._size_reader_helper(new_rev, output):
398 new_size = int(size_entry.total)
399 # check if we have the file in old revision
400 if fname in self.code_size[old_rev][mod]:
401 old_size = int(self.code_size[old_rev][mod][fname].total)
402 change = new_size - old_size
403 if old_size != 0:
404 change_pct = change / old_size
405 else:
406 change_pct = 0
407 output.write("{:<30} {:>7} {:>7} {:>7} {:>7.2%}\n"
408 .format(fname, new_size, old_size, change, change_pct))
409 else:
410 output.write("{} {}\n".format(fname, new_size))
411
Yanray Wang15c43f32023-07-17 11:17:12 +0800412 def size_generator_write_record(
413 self,
414 revision: str,
415 code_size_text: typing.Dict,
416 output_file: str
417 ) -> None:
418 """Write size record into a specified file based on Git revision and
419 output from `size` tool."""
420 for mod, size_text in code_size_text.items():
421 self.set_size_record(revision, mod, size_text)
422
423 print("Generating code size csv for", revision)
424 output = open(output_file, "w")
425 self.write_size_record(revision, output)
426
427 def size_generator_write_comparison(
428 self,
429 old_rev: str,
430 new_rev: str,
431 output_stream
432 ) -> None:
433 """Write a comparision result into a stream between two revisions."""
434 output = open(output_stream, "w")
435 self.write_comparison(old_rev, new_rev, output)
436
Yanray Wang16ebc572023-05-30 18:10:20 +0800437
Yanray Wangfc6ed4d2023-07-14 17:33:09 +0800438class CodeSizeComparison:
Xiaofei Bai2400b502021-10-21 12:22:58 +0000439 """Compare code size between two Git revisions."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000440
Yanray Wang72b105f2023-05-31 15:20:39 +0800441 def __init__(
442 self,
Yanray Wang923f9432023-07-17 12:43:00 +0800443 old_size_version: SimpleNamespace,
444 new_size_version: SimpleNamespace,
Yanray Wang802af162023-07-17 14:04:30 +0800445 code_size_common: SimpleNamespace,
Yanray Wang72b105f2023-05-31 15:20:39 +0800446 result_dir: str,
Yanray Wang72b105f2023-05-31 15:20:39 +0800447 ) -> None:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000448 """
Yanray Wang6a862582023-05-24 12:24:38 +0800449 old_revision: revision to compare against.
Xiaofei Baibca03e52021-09-09 09:42:37 +0000450 new_revision:
Yanray Wang6a862582023-05-24 12:24:38 +0800451 result_dir: directory for comparison result.
Xiaofei Baibca03e52021-09-09 09:42:37 +0000452 """
453 self.repo_path = "."
454 self.result_dir = os.path.abspath(result_dir)
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000455 os.makedirs(self.result_dir, exist_ok=True)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000456
457 self.csv_dir = os.path.abspath("code_size_records/")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000458 os.makedirs(self.csv_dir, exist_ok=True)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000459
Yanray Wang923f9432023-07-17 12:43:00 +0800460 self.old_size_version = old_size_version
461 self.new_size_version = new_size_version
Yanray Wang802af162023-07-17 14:04:30 +0800462 self.code_size_common = code_size_common
Yanray Wang923f9432023-07-17 12:43:00 +0800463 self.old_size_version.make_cmd = \
Yanray Wang802af162023-07-17 14:04:30 +0800464 CodeSizeBuildInfo(self.old_size_version,\
465 self.code_size_common.host_arch).infer_make_command()
Yanray Wang923f9432023-07-17 12:43:00 +0800466 self.new_size_version.make_cmd = \
Yanray Wang802af162023-07-17 14:04:30 +0800467 CodeSizeBuildInfo(self.new_size_version,\
468 self.code_size_common.host_arch).infer_make_command()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000469 self.git_command = "git"
Yanray Wang4c26db02023-07-04 16:49:04 +0800470 self.make_clean = 'make clean'
Yanray Wang802af162023-07-17 14:04:30 +0800471 self.code_size_generator = self.__init_code_size_generator__(\
472 self.code_size_common.measure_cmd)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000473
Yanray Wang923f9432023-07-17 12:43:00 +0800474 @staticmethod
Yanray Wang802af162023-07-17 14:04:30 +0800475 def __init_code_size_generator__(measure_cmd):
476 if re.match(r'size', measure_cmd.strip()):
477 return CodeSizeGeneratorWithSize()
478 else:
479 print("Error: unsupported tool:", measure_cmd.strip().split(' ')[0])
480 sys.exit(1)
481
482
483 def cal_code_size(self, size_version: SimpleNamespace):
Yanray Wang5e9130a2023-07-17 11:55:54 +0800484 """Calculate code size of library objects in a UTF-8 encoding"""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000485
Yanray Wang802af162023-07-17 14:04:30 +0800486 return CodeSizeCalculator(size_version.revision, size_version.make_cmd,\
487 self.code_size_common.measure_cmd).cal_libraries_code_size()
Yanray Wang8804db92023-05-30 18:18:18 +0800488
Yanray Wang802af162023-07-17 14:04:30 +0800489 def gen_file_name(self, old_size_version, new_size_version=None):
Yanray Wang923f9432023-07-17 12:43:00 +0800490 if new_size_version:
Yanray Wang802af162023-07-17 14:04:30 +0800491 return '{}-{}-{}-{}-{}-{}-{}.csv'\
Yanray Wang923f9432023-07-17 12:43:00 +0800492 .format(old_size_version.revision[:7],
493 old_size_version.arch, old_size_version.config,
494 new_size_version.revision[:7],
Yanray Wang802af162023-07-17 14:04:30 +0800495 new_size_version.arch, new_size_version.config,
496 self.code_size_common.measure_cmd.strip().split(' ')[0])
Yanray Wang923f9432023-07-17 12:43:00 +0800497 else:
Yanray Wang802af162023-07-17 14:04:30 +0800498 return '{}-{}-{}-{}.csv'\
Yanray Wang923f9432023-07-17 12:43:00 +0800499 .format(old_size_version.revision[:7],
Yanray Wang802af162023-07-17 14:04:30 +0800500 old_size_version.arch, old_size_version.config,
501 self.code_size_common.measure_cmd.strip().split(' ')[0])
Yanray Wang923f9432023-07-17 12:43:00 +0800502
503 def gen_code_size_report(self, size_version: SimpleNamespace):
Yanray Wang5e9130a2023-07-17 11:55:54 +0800504 """Generate code size record and write it into a file."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000505
Yanray Wang923f9432023-07-17 12:43:00 +0800506 output_file = os.path.join(self.csv_dir, self.gen_file_name(size_version))
Xiaofei Baibca03e52021-09-09 09:42:37 +0000507 # Check if the corresponding record exists
Yanray Wang923f9432023-07-17 12:43:00 +0800508 if (size_version.revision != "current") and os.path.exists(output_file):
509 print("Code size csv file for", size_version.revision, "already exists.")
510 self.code_size_generator.read_size_record(size_version.revision, output_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000511 else:
Yanray Wang923f9432023-07-17 12:43:00 +0800512 self.code_size_generator.size_generator_write_record(\
513 size_version.revision, self.cal_code_size(size_version),
514 output_file)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000515
Yanray Wang5e9130a2023-07-17 11:55:54 +0800516 def gen_code_size_comparison(self) -> int:
517 """Generate results of code size changes between two revisions,
Xiaofei Baibca03e52021-09-09 09:42:37 +0000518 old and new. Measured code size results of these two revisions
Xiaofei Bai2400b502021-10-21 12:22:58 +0000519 must be available."""
Xiaofei Baibca03e52021-09-09 09:42:37 +0000520
Yanray Wang923f9432023-07-17 12:43:00 +0800521 output_file = os.path.join(self.result_dir,\
522 self.gen_file_name(self.old_size_version, self.new_size_version))
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000523
Yanray Wangc7a2a6d2023-05-31 15:47:25 +0800524 print("\nGenerating comparison results between",\
Yanray Wang923f9432023-07-17 12:43:00 +0800525 self.old_size_version.revision, "and", self.new_size_version.revision)
Yanray Wange0e27602023-07-14 17:37:45 +0800526 self.code_size_generator.size_generator_write_comparison(\
Yanray Wang923f9432023-07-17 12:43:00 +0800527 self.old_size_version.revision, self.new_size_version.revision,\
528 output_file)
Xiaofei Bai2400b502021-10-21 12:22:58 +0000529 return 0
Xiaofei Baibca03e52021-09-09 09:42:37 +0000530
Yanray Wang72b105f2023-05-31 15:20:39 +0800531 def get_comparision_results(self) -> int:
Xiaofei Baibca03e52021-09-09 09:42:37 +0000532 """Compare size of library/*.o between self.old_rev and self.new_rev,
533 and generate the result file."""
Gilles Peskined9071e72022-09-18 21:17:09 +0200534 build_tree.check_repo_path()
Yanray Wang923f9432023-07-17 12:43:00 +0800535 self.gen_code_size_report(self.old_size_version)
536 self.gen_code_size_report(self.new_size_version)
Yanray Wang5e9130a2023-07-17 11:55:54 +0800537 return self.gen_code_size_comparison()
Xiaofei Baibca03e52021-09-09 09:42:37 +0000538
Yanray Wang923f9432023-07-17 12:43:00 +0800539
Xiaofei Bai2400b502021-10-21 12:22:58 +0000540def main():
Yanray Wang502c54f2023-05-31 11:41:36 +0800541 parser = argparse.ArgumentParser(description=(__doc__))
542 group_required = parser.add_argument_group(
543 'required arguments',
544 'required arguments to parse for running ' + os.path.basename(__file__))
545 group_required.add_argument(
546 "-o", "--old-rev", type=str, required=True,
547 help="old revision for comparison.")
548
549 group_optional = parser.add_argument_group(
550 'optional arguments',
551 'optional arguments to parse for running ' + os.path.basename(__file__))
552 group_optional.add_argument(
Xiaofei Baibca03e52021-09-09 09:42:37 +0000553 "-r", "--result-dir", type=str, default="comparison",
554 help="directory where comparison result is stored, \
Yanray Wang502c54f2023-05-31 11:41:36 +0800555 default is comparison")
556 group_optional.add_argument(
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000557 "-n", "--new-rev", type=str, default=None,
558 help="new revision for comparison, default is the current work \
Yanray Wang502c54f2023-05-31 11:41:36 +0800559 directory, including uncommitted changes.")
560 group_optional.add_argument(
Yanray Wang23bd5322023-05-24 11:03:59 +0800561 "-a", "--arch", type=str, default=detect_arch(),
562 choices=list(map(lambda s: s.value, SupportedArch)),
563 help="specify architecture for code size comparison, default is the\
Yanray Wang502c54f2023-05-31 11:41:36 +0800564 host architecture.")
565 group_optional.add_argument(
Yanray Wang6a862582023-05-24 12:24:38 +0800566 "-c", "--config", type=str, default=SupportedConfig.DEFAULT.value,
567 choices=list(map(lambda s: s.value, SupportedConfig)),
568 help="specify configuration type for code size comparison,\
Yanray Wang502c54f2023-05-31 11:41:36 +0800569 default is the current MbedTLS configuration.")
Xiaofei Baibca03e52021-09-09 09:42:37 +0000570 comp_args = parser.parse_args()
571
572 if os.path.isfile(comp_args.result_dir):
573 print("Error: {} is not a directory".format(comp_args.result_dir))
574 parser.exit()
575
Yanray Wange0e27602023-07-14 17:37:45 +0800576 validate_res = CodeSizeCalculator.validate_revision(comp_args.old_rev)
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000577 old_revision = validate_res.decode().replace("\n", "")
Xiaofei Bai2400b502021-10-21 12:22:58 +0000578
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000579 if comp_args.new_rev is not None:
Yanray Wange0e27602023-07-14 17:37:45 +0800580 validate_res = CodeSizeCalculator.validate_revision(comp_args.new_rev)
Xiaofei Baiccd738b2021-11-03 07:12:31 +0000581 new_revision = validate_res.decode().replace("\n", "")
Xiaofei Bai184e8b62021-10-26 09:23:42 +0000582 else:
583 new_revision = "current"
Xiaofei Bai2400b502021-10-21 12:22:58 +0000584
Yanray Wang923f9432023-07-17 12:43:00 +0800585 old_size_version = SimpleNamespace(
586 version="old",
587 revision=old_revision,
588 config=comp_args.config,
589 arch=comp_args.arch,
Yanray Wang923f9432023-07-17 12:43:00 +0800590 make_cmd='',
591 )
592 new_size_version = SimpleNamespace(
593 version="new",
594 revision=new_revision,
595 config=comp_args.config,
596 arch=comp_args.arch,
Yanray Wang923f9432023-07-17 12:43:00 +0800597 make_cmd='',
598 )
Yanray Wang802af162023-07-17 14:04:30 +0800599 code_size_common = SimpleNamespace(
600 host_arch=detect_arch(),
601 measure_cmd='size -t',
602 )
Yanray Wang923f9432023-07-17 12:43:00 +0800603
604 size_compare = CodeSizeComparison(old_size_version, new_size_version,\
Yanray Wang802af162023-07-17 14:04:30 +0800605 code_size_common, comp_args.result_dir)
Xiaofei Baibca03e52021-09-09 09:42:37 +0000606 return_code = size_compare.get_comparision_results()
607 sys.exit(return_code)
608
609
610if __name__ == "__main__":
Xiaofei Bai2400b502021-10-21 12:22:58 +0000611 main()