mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-07-30 22:43:08 +03:00
Merge pull request #7650 from yanrayw/7360-code-size-tfm-medium
code size measurement support for tfm-medium
This commit is contained in:
@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Purpose
|
||||
|
||||
This script is for comparing the size of the library files from two
|
||||
different Git revisions within an Mbed TLS repository.
|
||||
The results of the comparison is formatted as csv and stored at a
|
||||
@ -29,18 +27,103 @@ import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from enum import Enum
|
||||
|
||||
from mbedtls_dev import build_tree
|
||||
|
||||
class SupportedArch(Enum):
|
||||
"""Supported architecture for code size measurement."""
|
||||
AARCH64 = 'aarch64'
|
||||
AARCH32 = 'aarch32'
|
||||
ARMV8_M = 'armv8-m'
|
||||
X86_64 = 'x86_64'
|
||||
X86 = 'x86'
|
||||
|
||||
CONFIG_TFM_MEDIUM_MBEDCRYPTO_H = "../configs/tfm_mbedcrypto_config_profile_medium.h"
|
||||
CONFIG_TFM_MEDIUM_PSA_CRYPTO_H = "../configs/crypto_config_profile_medium.h"
|
||||
class SupportedConfig(Enum):
|
||||
"""Supported configuration for code size measurement."""
|
||||
DEFAULT = 'default'
|
||||
TFM_MEDIUM = 'tfm-medium'
|
||||
|
||||
DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
|
||||
def detect_arch() -> str:
|
||||
"""Auto-detect host architecture."""
|
||||
cc_output = subprocess.check_output(DETECT_ARCH_CMD, shell=True).decode()
|
||||
if "__aarch64__" in cc_output:
|
||||
return SupportedArch.AARCH64.value
|
||||
if "__arm__" in cc_output:
|
||||
return SupportedArch.AARCH32.value
|
||||
if "__x86_64__" in cc_output:
|
||||
return SupportedArch.X86_64.value
|
||||
if "__x86__" in cc_output:
|
||||
return SupportedArch.X86.value
|
||||
else:
|
||||
print("Unknown host architecture, cannot auto-detect arch.")
|
||||
sys.exit(1)
|
||||
|
||||
class CodeSizeInfo: # pylint: disable=too-few-public-methods
|
||||
"""Gather information used to measure code size.
|
||||
|
||||
It collects information about architecture, configuration in order to
|
||||
infer build command for code size measurement.
|
||||
"""
|
||||
|
||||
SupportedArchConfig = [
|
||||
"-a " + SupportedArch.AARCH64.value + " -c " + SupportedConfig.DEFAULT.value,
|
||||
"-a " + SupportedArch.AARCH32.value + " -c " + SupportedConfig.DEFAULT.value,
|
||||
"-a " + SupportedArch.X86_64.value + " -c " + SupportedConfig.DEFAULT.value,
|
||||
"-a " + SupportedArch.X86.value + " -c " + SupportedConfig.DEFAULT.value,
|
||||
"-a " + SupportedArch.ARMV8_M.value + " -c " + SupportedConfig.TFM_MEDIUM.value,
|
||||
]
|
||||
|
||||
def __init__(self, arch: str, config: str, sys_arch: str) -> None:
|
||||
"""
|
||||
arch: architecture to measure code size on.
|
||||
config: configuration type to measure code size with.
|
||||
make_command: command to build library (Inferred from arch and config).
|
||||
"""
|
||||
self.arch = arch
|
||||
self.config = config
|
||||
self.sys_arch = sys_arch
|
||||
self.make_command = self.set_make_command()
|
||||
|
||||
def set_make_command(self) -> str:
|
||||
"""Infer build command based on architecture and configuration."""
|
||||
|
||||
if self.config == SupportedConfig.DEFAULT.value and \
|
||||
self.arch == self.sys_arch:
|
||||
return 'make -j lib CFLAGS=\'-Os \' '
|
||||
elif self.arch == SupportedArch.ARMV8_M.value and \
|
||||
self.config == SupportedConfig.TFM_MEDIUM.value:
|
||||
return \
|
||||
'make -j lib CC=armclang \
|
||||
CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
|
||||
-DMBEDTLS_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_MBEDCRYPTO_H + '\\\" \
|
||||
-DMBEDTLS_PSA_CRYPTO_CONFIG_FILE=\\\"' + CONFIG_TFM_MEDIUM_PSA_CRYPTO_H + '\\\" \''
|
||||
else:
|
||||
print("Unsupported combination of architecture: {} and configuration: {}"
|
||||
.format(self.arch, self.config))
|
||||
print("\nPlease use supported combination of architecture and configuration:")
|
||||
for comb in CodeSizeInfo.SupportedArchConfig:
|
||||
print(comb)
|
||||
print("\nFor your system, please use:")
|
||||
for comb in CodeSizeInfo.SupportedArchConfig:
|
||||
if "default" in comb and self.sys_arch not in comb:
|
||||
continue
|
||||
print(comb)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class CodeSizeComparison:
|
||||
"""Compare code size between two Git revisions."""
|
||||
|
||||
def __init__(self, old_revision, new_revision, result_dir):
|
||||
def __init__(self, old_revision, new_revision, result_dir, code_size_info):
|
||||
"""
|
||||
old_revision: revision to compare against
|
||||
old_revision: revision to compare against.
|
||||
new_revision:
|
||||
result_dir: directory for comparison result
|
||||
result_dir: directory for comparison result.
|
||||
code_size_info: an object containing information to build library.
|
||||
"""
|
||||
self.repo_path = "."
|
||||
self.result_dir = os.path.abspath(result_dir)
|
||||
@ -52,7 +135,9 @@ class CodeSizeComparison:
|
||||
self.old_rev = old_revision
|
||||
self.new_rev = new_revision
|
||||
self.git_command = "git"
|
||||
self.make_command = "make"
|
||||
self.make_command = code_size_info.make_command
|
||||
self.fname_suffix = "-" + code_size_info.arch + "-" +\
|
||||
code_size_info.config
|
||||
|
||||
@staticmethod
|
||||
def validate_revision(revision):
|
||||
@ -75,21 +160,25 @@ class CodeSizeComparison:
|
||||
git_worktree_path, revision], cwd=self.repo_path,
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
|
||||
return git_worktree_path
|
||||
|
||||
def _build_libraries(self, git_worktree_path):
|
||||
"""Build libraries in the specified worktree."""
|
||||
|
||||
my_environment = os.environ.copy()
|
||||
subprocess.check_output(
|
||||
[self.make_command, "-j", "lib"], env=my_environment,
|
||||
cwd=git_worktree_path, stderr=subprocess.STDOUT,
|
||||
)
|
||||
try:
|
||||
subprocess.check_output(
|
||||
self.make_command, env=my_environment, shell=True,
|
||||
cwd=git_worktree_path, stderr=subprocess.STDOUT,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
self._handle_called_process_error(e, git_worktree_path)
|
||||
|
||||
def _gen_code_size_csv(self, revision, git_worktree_path):
|
||||
"""Generate code size csv file."""
|
||||
|
||||
csv_fname = revision + ".csv"
|
||||
csv_fname = revision + self.fname_suffix + ".csv"
|
||||
if revision == "current":
|
||||
print("Measuring code size in current work directory.")
|
||||
else:
|
||||
@ -117,7 +206,7 @@ class CodeSizeComparison:
|
||||
"""Generate code size csv file for the specified git revision."""
|
||||
|
||||
# Check if the corresponding record exists
|
||||
csv_fname = revision + ".csv"
|
||||
csv_fname = revision + self.fname_suffix + ".csv"
|
||||
if (revision != "current") and \
|
||||
os.path.exists(os.path.join(self.csv_dir, csv_fname)):
|
||||
print("Code size csv file for", revision, "already exists.")
|
||||
@ -132,16 +221,20 @@ class CodeSizeComparison:
|
||||
old and new. Measured code size results of these two revisions
|
||||
must be available."""
|
||||
|
||||
old_file = open(os.path.join(self.csv_dir, self.old_rev + ".csv"), "r")
|
||||
new_file = open(os.path.join(self.csv_dir, self.new_rev + ".csv"), "r")
|
||||
res_file = open(os.path.join(self.result_dir, "compare-" + self.old_rev
|
||||
+ "-" + self.new_rev + ".csv"), "w")
|
||||
old_file = open(os.path.join(self.csv_dir, self.old_rev +
|
||||
self.fname_suffix + ".csv"), "r")
|
||||
new_file = open(os.path.join(self.csv_dir, self.new_rev +
|
||||
self.fname_suffix + ".csv"), "r")
|
||||
res_file = open(os.path.join(self.result_dir, "compare-" +
|
||||
self.old_rev + "-" + self.new_rev +
|
||||
self.fname_suffix +
|
||||
".csv"), "w")
|
||||
|
||||
res_file.write("file_name, this_size, old_size, change, change %\n")
|
||||
print("Generating comparison results.")
|
||||
|
||||
old_ds = {}
|
||||
for line in old_file.readlines()[1:]:
|
||||
for line in old_file.readlines():
|
||||
cols = line.split(", ")
|
||||
fname = cols[0]
|
||||
size = int(cols[1])
|
||||
@ -149,7 +242,7 @@ class CodeSizeComparison:
|
||||
old_ds[fname] = size
|
||||
|
||||
new_ds = {}
|
||||
for line in new_file.readlines()[1:]:
|
||||
for line in new_file.readlines():
|
||||
cols = line.split(", ")
|
||||
fname = cols[0]
|
||||
size = int(cols[1])
|
||||
@ -175,30 +268,50 @@ class CodeSizeComparison:
|
||||
self._get_code_size_for_rev(self.new_rev)
|
||||
return self.compare_code_size()
|
||||
|
||||
def _handle_called_process_error(self, e: subprocess.CalledProcessError,
|
||||
git_worktree_path):
|
||||
"""Handle a CalledProcessError and quit the program gracefully.
|
||||
Remove any extra worktrees so that the script may be called again."""
|
||||
|
||||
# Tell the user what went wrong
|
||||
print("The following command: {} failed and exited with code {}"
|
||||
.format(e.cmd, e.returncode))
|
||||
print("Process output:\n {}".format(str(e.output, "utf-8")))
|
||||
|
||||
# Quit gracefully by removing the existing worktree
|
||||
self._remove_worktree(git_worktree_path)
|
||||
sys.exit(-1)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"""This script is for comparing the size of the library files
|
||||
from two different Git revisions within an Mbed TLS repository.
|
||||
The results of the comparison is formatted as csv, and stored at
|
||||
a configurable location.
|
||||
Note: must be run from Mbed TLS root."""
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
parser = argparse.ArgumentParser(description=(__doc__))
|
||||
group_required = parser.add_argument_group(
|
||||
'required arguments',
|
||||
'required arguments to parse for running ' + os.path.basename(__file__))
|
||||
group_required.add_argument(
|
||||
"-o", "--old-rev", type=str, required=True,
|
||||
help="old revision for comparison.")
|
||||
|
||||
group_optional = parser.add_argument_group(
|
||||
'optional arguments',
|
||||
'optional arguments to parse for running ' + os.path.basename(__file__))
|
||||
group_optional.add_argument(
|
||||
"-r", "--result-dir", type=str, default="comparison",
|
||||
help="directory where comparison result is stored, \
|
||||
default is comparison",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o", "--old-rev", type=str, help="old revision for comparison.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
default is comparison")
|
||||
group_optional.add_argument(
|
||||
"-n", "--new-rev", type=str, default=None,
|
||||
help="new revision for comparison, default is the current work \
|
||||
directory, including uncommitted changes."
|
||||
)
|
||||
directory, including uncommitted changes.")
|
||||
group_optional.add_argument(
|
||||
"-a", "--arch", type=str, default=detect_arch(),
|
||||
choices=list(map(lambda s: s.value, SupportedArch)),
|
||||
help="specify architecture for code size comparison, default is the\
|
||||
host architecture.")
|
||||
group_optional.add_argument(
|
||||
"-c", "--config", type=str, default=SupportedConfig.DEFAULT.value,
|
||||
choices=list(map(lambda s: s.value, SupportedConfig)),
|
||||
help="specify configuration type for code size comparison,\
|
||||
default is the current MbedTLS configuration.")
|
||||
comp_args = parser.parse_args()
|
||||
|
||||
if os.path.isfile(comp_args.result_dir):
|
||||
@ -214,8 +327,13 @@ def main():
|
||||
else:
|
||||
new_revision = "current"
|
||||
|
||||
code_size_info = CodeSizeInfo(comp_args.arch, comp_args.config,
|
||||
detect_arch())
|
||||
print("Measure code size for architecture: {}, configuration: {}"
|
||||
.format(code_size_info.arch, code_size_info.config))
|
||||
result_dir = comp_args.result_dir
|
||||
size_compare = CodeSizeComparison(old_revision, new_revision, result_dir)
|
||||
size_compare = CodeSizeComparison(old_revision, new_revision, result_dir,
|
||||
code_size_info)
|
||||
return_code = size_compare.get_comparision_results()
|
||||
sys.exit(return_code)
|
||||
|
||||
|
Reference in New Issue
Block a user