mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-04-16 08:42:50 +00:00
code_size_compare: replace SimpleNameSpace to a clearer data struct
Signed-off-by: Yanray Wang <yanray.wang@arm.com>
This commit is contained in:
parent
5b64e4c7e0
commit
955671b0ef
@ -32,7 +32,6 @@ import sys
|
|||||||
import typing
|
import typing
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from types import SimpleNamespace
|
|
||||||
from mbedtls_dev import build_tree
|
from mbedtls_dev import build_tree
|
||||||
from mbedtls_dev import logging_util
|
from mbedtls_dev import logging_util
|
||||||
from mbedtls_dev import typing_util
|
from mbedtls_dev import typing_util
|
||||||
@ -45,6 +44,7 @@ class SupportedArch(Enum):
|
|||||||
X86_64 = 'x86_64'
|
X86_64 = 'x86_64'
|
||||||
X86 = 'x86'
|
X86 = 'x86'
|
||||||
|
|
||||||
|
|
||||||
CONFIG_TFM_MEDIUM_MBEDCRYPTO_H = '../configs/tfm_mbedcrypto_config_profile_medium.h'
|
CONFIG_TFM_MEDIUM_MBEDCRYPTO_H = '../configs/tfm_mbedcrypto_config_profile_medium.h'
|
||||||
CONFIG_TFM_MEDIUM_PSA_CRYPTO_H = '../configs/crypto_config_profile_medium.h'
|
CONFIG_TFM_MEDIUM_PSA_CRYPTO_H = '../configs/crypto_config_profile_medium.h'
|
||||||
class SupportedConfig(Enum):
|
class SupportedConfig(Enum):
|
||||||
@ -52,6 +52,7 @@ class SupportedConfig(Enum):
|
|||||||
DEFAULT = 'default'
|
DEFAULT = 'default'
|
||||||
TFM_MEDIUM = 'tfm-medium'
|
TFM_MEDIUM = 'tfm-medium'
|
||||||
|
|
||||||
|
|
||||||
# Static library
|
# Static library
|
||||||
MBEDTLS_STATIC_LIB = {
|
MBEDTLS_STATIC_LIB = {
|
||||||
'CRYPTO': 'library/libmbedcrypto.a',
|
'CRYPTO': 'library/libmbedcrypto.a',
|
||||||
@ -59,6 +60,70 @@ MBEDTLS_STATIC_LIB = {
|
|||||||
'TLS': 'library/libmbedtls.a',
|
'TLS': 'library/libmbedtls.a',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CodeSizeDistinctInfo: # pylint: disable=too-few-public-methods
|
||||||
|
"""Data structure to store possibly distinct information for code size
|
||||||
|
comparison."""
|
||||||
|
def __init__( #pylint: disable=too-many-arguments
|
||||||
|
self,
|
||||||
|
version: str,
|
||||||
|
git_rev: str,
|
||||||
|
arch: str,
|
||||||
|
config: str,
|
||||||
|
make_cmd: str,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
:param: version: which version to compare with for code size.
|
||||||
|
:param: git_rev: Git revision to calculate code size.
|
||||||
|
:param: arch: architecture to measure code size on.
|
||||||
|
:param: config: Configuration type to calculate code size.
|
||||||
|
(See SupportedConfig)
|
||||||
|
:param: make_cmd: make command to build library/*.o.
|
||||||
|
"""
|
||||||
|
self.version = version
|
||||||
|
self.git_rev = git_rev
|
||||||
|
self.arch = arch
|
||||||
|
self.config = config
|
||||||
|
self.make_cmd = make_cmd
|
||||||
|
|
||||||
|
|
||||||
|
class CodeSizeCommonInfo: # pylint: disable=too-few-public-methods
|
||||||
|
"""Data structure to store common information for code size comparison."""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
host_arch: str,
|
||||||
|
measure_cmd: str,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
:param host_arch: host architecture.
|
||||||
|
:param measure_cmd: command to measure code size for library/*.o.
|
||||||
|
"""
|
||||||
|
self.host_arch = host_arch
|
||||||
|
self.measure_cmd = measure_cmd
|
||||||
|
|
||||||
|
|
||||||
|
class CodeSizeResultInfo: # pylint: disable=too-few-public-methods
|
||||||
|
"""Data structure to store result options for code size comparison."""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
record_dir: str,
|
||||||
|
comp_dir: str,
|
||||||
|
with_markdown=False,
|
||||||
|
stdout=False,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
:param record_dir: directory to store code size record.
|
||||||
|
:param comp_dir: directory to store results of code size comparision.
|
||||||
|
:param with_markdown: write comparision result into a markdown table.
|
||||||
|
(Default: False)
|
||||||
|
:param stdout: direct comparison result into sys.stdout.
|
||||||
|
(Default False)
|
||||||
|
"""
|
||||||
|
self.record_dir = record_dir
|
||||||
|
self.comp_dir = comp_dir
|
||||||
|
self.with_markdown = with_markdown
|
||||||
|
self.stdout = stdout
|
||||||
|
|
||||||
|
|
||||||
DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
|
DETECT_ARCH_CMD = "cc -dM -E - < /dev/null"
|
||||||
def detect_arch() -> str:
|
def detect_arch() -> str:
|
||||||
"""Auto-detect host architecture."""
|
"""Auto-detect host architecture."""
|
||||||
@ -92,20 +157,20 @@ class CodeSizeBuildInfo: # pylint: disable=too-few-public-methods
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
size_version: SimpleNamespace,
|
size_dist_info: CodeSizeDistinctInfo,
|
||||||
host_arch: str,
|
host_arch: str,
|
||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:param size_version:
|
:param size_dist_info:
|
||||||
SimpleNamespace containing info for code size measurement.
|
CodeSizeDistinctInfo containing info for code size measurement.
|
||||||
- size_version.arch: architecture to measure code size on.
|
- size_dist_info.arch: architecture to measure code size on.
|
||||||
- size_version.config: configuration type to measure code size
|
- size_dist_info.config: configuration type to measure
|
||||||
with.
|
code size with.
|
||||||
:param host_arch: host architecture.
|
:param host_arch: host architecture.
|
||||||
:param logger: logging module
|
:param logger: logging module
|
||||||
"""
|
"""
|
||||||
self.size_version = size_version
|
self.size_dist_info = size_dist_info
|
||||||
self.host_arch = host_arch
|
self.host_arch = host_arch
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
@ -113,12 +178,12 @@ class CodeSizeBuildInfo: # pylint: disable=too-few-public-methods
|
|||||||
"""Infer make command based on architecture and configuration."""
|
"""Infer make command based on architecture and configuration."""
|
||||||
|
|
||||||
# make command by default
|
# make command by default
|
||||||
if self.size_version.config == SupportedConfig.DEFAULT.value and \
|
if self.size_dist_info.config == SupportedConfig.DEFAULT.value and \
|
||||||
self.size_version.arch == self.host_arch:
|
self.size_dist_info.arch == self.host_arch:
|
||||||
return 'make -j lib CFLAGS=\'-Os \' '
|
return 'make -j lib CFLAGS=\'-Os \' '
|
||||||
# make command for TF-M
|
# make command for TF-M
|
||||||
elif self.size_version.arch == SupportedArch.ARMV8_M.value and \
|
elif self.size_dist_info.arch == SupportedArch.ARMV8_M.value and \
|
||||||
self.size_version.config == SupportedConfig.TFM_MEDIUM.value:
|
self.size_dist_info.config == SupportedConfig.TFM_MEDIUM.value:
|
||||||
return \
|
return \
|
||||||
'make -j lib CC=armclang \
|
'make -j lib CC=armclang \
|
||||||
CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
|
CFLAGS=\'--target=arm-arm-none-eabi -mcpu=cortex-m33 -Os \
|
||||||
@ -128,8 +193,8 @@ class CodeSizeBuildInfo: # pylint: disable=too-few-public-methods
|
|||||||
else:
|
else:
|
||||||
self.logger.error("Unsupported combination of architecture: {} " \
|
self.logger.error("Unsupported combination of architecture: {} " \
|
||||||
"and configuration: {}.\n"
|
"and configuration: {}.\n"
|
||||||
.format(self.size_version.arch,
|
.format(self.size_dist_info.arch,
|
||||||
self.size_version.config))
|
self.size_dist_info.config))
|
||||||
self.logger.info("Please use supported combination of " \
|
self.logger.info("Please use supported combination of " \
|
||||||
"architecture and configuration:")
|
"architecture and configuration:")
|
||||||
for comb in CodeSizeBuildInfo.SupportedArchConfig:
|
for comb in CodeSizeBuildInfo.SupportedArchConfig:
|
||||||
@ -150,13 +215,13 @@ class CodeSizeCalculator:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
revision: str,
|
git_rev: str,
|
||||||
make_cmd: str,
|
make_cmd: str,
|
||||||
measure_cmd: str,
|
measure_cmd: str,
|
||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:param revision: Git revision.(E.g: commit)
|
:param git_rev: Git revision. (E.g: commit)
|
||||||
:param make_cmd: command to build library/*.o.
|
:param make_cmd: command to build library/*.o.
|
||||||
:param measure_cmd: command to measure code size for library/*.o.
|
:param measure_cmd: command to measure code size for library/*.o.
|
||||||
:param logger: logging module
|
:param logger: logging module
|
||||||
@ -165,33 +230,33 @@ class CodeSizeCalculator:
|
|||||||
self.git_command = "git"
|
self.git_command = "git"
|
||||||
self.make_clean = 'make clean'
|
self.make_clean = 'make clean'
|
||||||
|
|
||||||
self.revision = revision
|
self.git_rev = git_rev
|
||||||
self.make_cmd = make_cmd
|
self.make_cmd = make_cmd
|
||||||
self.measure_cmd = measure_cmd
|
self.measure_cmd = measure_cmd
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_revision(revision: str) -> str:
|
def validate_git_revision(git_rev: str) -> str:
|
||||||
result = subprocess.check_output(["git", "rev-parse", "--verify",
|
result = subprocess.check_output(["git", "rev-parse", "--verify",
|
||||||
revision + "^{commit}"], shell=False,
|
git_rev + "^{commit}"],
|
||||||
universal_newlines=True)
|
shell=False, universal_newlines=True)
|
||||||
return result[:7]
|
return result[:7]
|
||||||
|
|
||||||
def _create_git_worktree(self) -> str:
|
def _create_git_worktree(self) -> str:
|
||||||
"""Create a separate worktree for revision.
|
"""Create a separate worktree for Git revision.
|
||||||
If revision is current, use current worktree instead."""
|
If Git revision is current, use current worktree instead."""
|
||||||
|
|
||||||
if self.revision == "current":
|
if self.git_rev == "current":
|
||||||
self.logger.debug("Using current work directory.")
|
self.logger.debug("Using current work directory.")
|
||||||
git_worktree_path = self.repo_path
|
git_worktree_path = self.repo_path
|
||||||
else:
|
else:
|
||||||
self.logger.debug("Creating git worktree for {}."
|
self.logger.debug("Creating git worktree for {}."
|
||||||
.format(self.revision))
|
.format(self.git_rev))
|
||||||
git_worktree_path = os.path.join(self.repo_path,
|
git_worktree_path = os.path.join(self.repo_path,
|
||||||
"temp-" + self.revision)
|
"temp-" + self.git_rev)
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
[self.git_command, "worktree", "add", "--detach",
|
[self.git_command, "worktree", "add", "--detach",
|
||||||
git_worktree_path, self.revision], cwd=self.repo_path,
|
git_worktree_path, self.git_rev], cwd=self.repo_path,
|
||||||
stderr=subprocess.STDOUT
|
stderr=subprocess.STDOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -201,7 +266,7 @@ class CodeSizeCalculator:
|
|||||||
"""Build library/*.o in the specified worktree."""
|
"""Build library/*.o in the specified worktree."""
|
||||||
|
|
||||||
self.logger.debug("Building library/*.o for {}."
|
self.logger.debug("Building library/*.o for {}."
|
||||||
.format(self.revision))
|
.format(self.git_rev))
|
||||||
my_environment = os.environ.copy()
|
my_environment = os.environ.copy()
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
@ -221,7 +286,7 @@ class CodeSizeCalculator:
|
|||||||
"""Measure code size by a tool and return in UTF-8 encoding."""
|
"""Measure code size by a tool and return in UTF-8 encoding."""
|
||||||
|
|
||||||
self.logger.debug("Measuring code size for {} by `{}`."
|
self.logger.debug("Measuring code size for {} by `{}`."
|
||||||
.format(self.revision,
|
.format(self.git_rev,
|
||||||
self.measure_cmd.strip().split(' ')[0]))
|
self.measure_cmd.strip().split(' ')[0]))
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
@ -292,13 +357,13 @@ class CodeSizeGenerator:
|
|||||||
|
|
||||||
def size_generator_write_record(
|
def size_generator_write_record(
|
||||||
self,
|
self,
|
||||||
revision: str,
|
git_rev: str,
|
||||||
code_size_text: typing.Dict,
|
code_size_text: typing.Dict,
|
||||||
output_file: str
|
output_file: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Write size record into a file.
|
"""Write size record into a file.
|
||||||
|
|
||||||
:param revision: Git revision.(E.g: commit)
|
:param git_rev: Git revision. (E.g: commit)
|
||||||
:param code_size_text:
|
:param code_size_text:
|
||||||
string output (utf-8) from measurement tool of code size.
|
string output (utf-8) from measurement tool of code size.
|
||||||
- typing.Dict[mod: str]
|
- typing.Dict[mod: str]
|
||||||
@ -311,15 +376,15 @@ class CodeSizeGenerator:
|
|||||||
old_rev: str,
|
old_rev: str,
|
||||||
new_rev: str,
|
new_rev: str,
|
||||||
output_stream: str,
|
output_stream: str,
|
||||||
result_options: SimpleNamespace
|
result_options: CodeSizeResultInfo
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Write a comparision result into a stream between two revisions.
|
"""Write a comparision result into a stream between two Git revisions.
|
||||||
|
|
||||||
:param old_rev: old Git revision to compared with.
|
:param old_rev: old Git revision to compared with.
|
||||||
:param new_rev: new Git revision to compared with.
|
:param new_rev: new Git revision to compared with.
|
||||||
:param output_stream: stream which the code size record is written to.
|
:param output_stream: stream which the code size record is written to.
|
||||||
:param result_options:
|
:param result_options:
|
||||||
SimpleNamespace containing options for comparison result.
|
CodeSizeResultInfo containing options for comparison result.
|
||||||
- result_options.with_markdown: write comparision result in a
|
- result_options.with_markdown: write comparision result in a
|
||||||
markdown table. (Default: False)
|
markdown table. (Default: False)
|
||||||
- result_options.stdout: direct comparison result into
|
- result_options.stdout: direct comparison result into
|
||||||
@ -340,22 +405,22 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
self.total = dec # total <=> dec
|
self.total = dec # total <=> dec
|
||||||
|
|
||||||
def __init__(self, logger: logging.Logger) -> None:
|
def __init__(self, logger: logging.Logger) -> None:
|
||||||
""" Variable code_size is used to store size info for any revisions.
|
""" Variable code_size is used to store size info for any Git revisions.
|
||||||
:param code_size:
|
:param code_size:
|
||||||
Data Format as following:
|
Data Format as following:
|
||||||
{revision: {module: {file_name: [text, data, bss, dec],
|
{git_rev: {module: {file_name: [text, data, bss, dec],
|
||||||
etc ...
|
etc ...
|
||||||
},
|
},
|
||||||
etc ...
|
etc ...
|
||||||
},
|
},
|
||||||
etc ...
|
etc ...
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
super().__init__(logger)
|
super().__init__(logger)
|
||||||
self.code_size = {} #type: typing.Dict[str, typing.Dict]
|
self.code_size = {} #type: typing.Dict[str, typing.Dict]
|
||||||
|
|
||||||
def _set_size_record(self, revision: str, mod: str, size_text: str) -> None:
|
def _set_size_record(self, git_rev: str, mod: str, size_text: str) -> None:
|
||||||
"""Store size information for target revision and high-level module.
|
"""Store size information for target Git revision and high-level module.
|
||||||
|
|
||||||
size_text Format: text data bss dec hex filename
|
size_text Format: text data bss dec hex filename
|
||||||
"""
|
"""
|
||||||
@ -365,12 +430,12 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
# file_name: SizeEntry(text, data, bss, dec)
|
# file_name: SizeEntry(text, data, bss, dec)
|
||||||
size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(
|
size_record[data[5]] = CodeSizeGeneratorWithSize.SizeEntry(
|
||||||
data[0], data[1], data[2], data[3])
|
data[0], data[1], data[2], data[3])
|
||||||
if revision in self.code_size:
|
if git_rev in self.code_size:
|
||||||
self.code_size[revision].update({mod: size_record})
|
self.code_size[git_rev].update({mod: size_record})
|
||||||
else:
|
else:
|
||||||
self.code_size[revision] = {mod: size_record}
|
self.code_size[git_rev] = {mod: size_record}
|
||||||
|
|
||||||
def read_size_record(self, revision: str, fname: str) -> None:
|
def read_size_record(self, git_rev: str, fname: str) -> None:
|
||||||
"""Read size information from csv file and write it into code_size.
|
"""Read size information from csv file and write it into code_size.
|
||||||
|
|
||||||
fname Format: filename text data bss dec
|
fname Format: filename text data bss dec
|
||||||
@ -393,21 +458,21 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
# check if we hit record for the end of a module
|
# check if we hit record for the end of a module
|
||||||
m = re.match(r'.?TOTALS', line)
|
m = re.match(r'.?TOTALS', line)
|
||||||
if m:
|
if m:
|
||||||
if revision in self.code_size:
|
if git_rev in self.code_size:
|
||||||
self.code_size[revision].update({mod: size_record})
|
self.code_size[git_rev].update({mod: size_record})
|
||||||
else:
|
else:
|
||||||
self.code_size[revision] = {mod: size_record}
|
self.code_size[git_rev] = {mod: size_record}
|
||||||
mod = ""
|
mod = ""
|
||||||
size_record = {}
|
size_record = {}
|
||||||
|
|
||||||
def _size_reader_helper(
|
def _size_reader_helper(
|
||||||
self,
|
self,
|
||||||
revision: str,
|
git_rev: str,
|
||||||
output: typing_util.Writable,
|
output: typing_util.Writable,
|
||||||
with_markdown=False
|
with_markdown=False
|
||||||
) -> typing.Iterator[tuple]:
|
) -> typing.Iterator[tuple]:
|
||||||
"""A helper function to peel code_size based on revision."""
|
"""A helper function to peel code_size based on Git revision."""
|
||||||
for mod, file_size in self.code_size[revision].items():
|
for mod, file_size in self.code_size[git_rev].items():
|
||||||
if not with_markdown:
|
if not with_markdown:
|
||||||
output.write("\n" + mod + "\n")
|
output.write("\n" + mod + "\n")
|
||||||
for fname, size_entry in file_size.items():
|
for fname, size_entry in file_size.items():
|
||||||
@ -415,7 +480,7 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
|
|
||||||
def _write_size_record(
|
def _write_size_record(
|
||||||
self,
|
self,
|
||||||
revision: str,
|
git_rev: str,
|
||||||
output: typing_util.Writable
|
output: typing_util.Writable
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Write size information to a file.
|
"""Write size information to a file.
|
||||||
@ -425,7 +490,7 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
format_string = "{:<30} {:>7} {:>7} {:>7} {:>7}\n"
|
format_string = "{:<30} {:>7} {:>7} {:>7} {:>7}\n"
|
||||||
output.write(format_string.format("filename",
|
output.write(format_string.format("filename",
|
||||||
"text", "data", "bss", "total"))
|
"text", "data", "bss", "total"))
|
||||||
for _, fname, size_entry in self._size_reader_helper(revision, output):
|
for _, fname, size_entry in self._size_reader_helper(git_rev, output):
|
||||||
output.write(format_string.format(fname,
|
output.write(format_string.format(fname,
|
||||||
size_entry.text, size_entry.data,
|
size_entry.text, size_entry.data,
|
||||||
size_entry.bss, size_entry.total))
|
size_entry.bss, size_entry.total))
|
||||||
@ -445,7 +510,7 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
|
|
||||||
def cal_size_section_variation(mod, fname, size_entry, attr):
|
def cal_size_section_variation(mod, fname, size_entry, attr):
|
||||||
new_size = int(size_entry.__dict__[attr])
|
new_size = int(size_entry.__dict__[attr])
|
||||||
# check if we have the file in old revision
|
# check if we have the file in old Git revision
|
||||||
if fname in self.code_size[old_rev][mod]:
|
if fname in self.code_size[old_rev][mod]:
|
||||||
old_size = int(self.code_size[old_rev][mod][fname].__dict__[attr])
|
old_size = int(self.code_size[old_rev][mod][fname].__dict__[attr])
|
||||||
change = new_size - old_size
|
change = new_size - old_size
|
||||||
@ -497,28 +562,28 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
|
|
||||||
def size_generator_write_record(
|
def size_generator_write_record(
|
||||||
self,
|
self,
|
||||||
revision: str,
|
git_rev: str,
|
||||||
code_size_text: typing.Dict,
|
code_size_text: typing.Dict,
|
||||||
output_file: str
|
output_file: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Write size record into a specified file based on Git revision and
|
"""Write size record into a specified file based on Git revision and
|
||||||
output from `size` tool."""
|
output from `size` tool."""
|
||||||
self.logger.debug("Generating code size csv for {}.".format(revision))
|
self.logger.debug("Generating code size csv for {}.".format(git_rev))
|
||||||
|
|
||||||
for mod, size_text in code_size_text.items():
|
for mod, size_text in code_size_text.items():
|
||||||
self._set_size_record(revision, mod, size_text)
|
self._set_size_record(git_rev, mod, size_text)
|
||||||
|
|
||||||
output = open(output_file, "w")
|
output = open(output_file, "w")
|
||||||
self._write_size_record(revision, output)
|
self._write_size_record(git_rev, output)
|
||||||
|
|
||||||
def size_generator_write_comparison(
|
def size_generator_write_comparison(
|
||||||
self,
|
self,
|
||||||
old_rev: str,
|
old_rev: str,
|
||||||
new_rev: str,
|
new_rev: str,
|
||||||
output_stream: str,
|
output_stream: str,
|
||||||
result_options: SimpleNamespace
|
result_options: CodeSizeResultInfo
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Write a comparision result into a stream between two revisions.
|
"""Write a comparision result into a stream between two Git revisions.
|
||||||
|
|
||||||
By default, it's written into a file called output_stream.
|
By default, it's written into a file called output_stream.
|
||||||
Once result_options.stdout is set, it's written into sys.stdout instead.
|
Once result_options.stdout is set, it's written into sys.stdout instead.
|
||||||
@ -537,133 +602,139 @@ class CodeSizeGeneratorWithSize(CodeSizeGenerator):
|
|||||||
class CodeSizeComparison:
|
class CodeSizeComparison:
|
||||||
"""Compare code size between two Git revisions."""
|
"""Compare code size between two Git revisions."""
|
||||||
|
|
||||||
def __init__(
|
def __init__( #pylint: disable=too-many-arguments
|
||||||
self,
|
self,
|
||||||
old_size_version: SimpleNamespace,
|
old_size_dist_info: CodeSizeDistinctInfo,
|
||||||
new_size_version: SimpleNamespace,
|
new_size_dist_info: CodeSizeDistinctInfo,
|
||||||
code_size_common: SimpleNamespace,
|
size_common_info: CodeSizeCommonInfo,
|
||||||
|
result_options: CodeSizeResultInfo,
|
||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
:param old_size_version: SimpleNamespace containing old version info
|
:param old_size_dist_info: CodeSizeDistinctInfo containing old distinct
|
||||||
to compare code size with.
|
info to compare code size with.
|
||||||
:param new_size_version: SimpleNamespace containing new version info
|
:param new_size_dist_info: CodeSizeDistinctInfo containing new distinct
|
||||||
to take as comparision base.
|
info to take as comparision base.
|
||||||
:param code_size_common: SimpleNamespace containing common info for
|
:param size_common_info: CodeSizeCommonInfo containing common info for
|
||||||
both old and new size version,
|
both old and new size distinct info and
|
||||||
measurement tool and result options.
|
measurement tool.
|
||||||
|
:param result_options: CodeSizeResultInfo containing results options for
|
||||||
|
code size record and comparision.
|
||||||
:param logger: logging module
|
:param logger: logging module
|
||||||
"""
|
"""
|
||||||
self.result_dir = os.path.abspath(
|
|
||||||
code_size_common.result_options.result_dir)
|
|
||||||
os.makedirs(self.result_dir, exist_ok=True)
|
|
||||||
|
|
||||||
self.csv_dir = os.path.abspath("code_size_records/")
|
|
||||||
os.makedirs(self.csv_dir, exist_ok=True)
|
|
||||||
|
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
self.old_size_version = old_size_version
|
self.old_size_dist_info = old_size_dist_info
|
||||||
self.new_size_version = new_size_version
|
self.new_size_dist_info = new_size_dist_info
|
||||||
self.code_size_common = code_size_common
|
self.size_common_info = size_common_info
|
||||||
# infer make command
|
# infer make command
|
||||||
self.old_size_version.make_cmd = CodeSizeBuildInfo(
|
self.old_size_dist_info.make_cmd = CodeSizeBuildInfo(
|
||||||
self.old_size_version, self.code_size_common.host_arch,
|
self.old_size_dist_info, self.size_common_info.host_arch,
|
||||||
self.logger).infer_make_command()
|
self.logger).infer_make_command()
|
||||||
self.new_size_version.make_cmd = CodeSizeBuildInfo(
|
self.new_size_dist_info.make_cmd = CodeSizeBuildInfo(
|
||||||
self.new_size_version, self.code_size_common.host_arch,
|
self.new_size_dist_info, self.size_common_info.host_arch,
|
||||||
self.logger).infer_make_command()
|
self.logger).infer_make_command()
|
||||||
# initialize size parser with corresponding measurement tool
|
# initialize size parser with corresponding measurement tool
|
||||||
self.code_size_generator = self.__generate_size_parser()
|
self.code_size_generator = self.__generate_size_parser()
|
||||||
|
|
||||||
|
self.result_options = result_options
|
||||||
|
self.csv_dir = os.path.abspath(self.result_options.record_dir)
|
||||||
|
os.makedirs(self.csv_dir, exist_ok=True)
|
||||||
|
self.comp_dir = os.path.abspath(self.result_options.comp_dir)
|
||||||
|
os.makedirs(self.comp_dir, exist_ok=True)
|
||||||
|
|
||||||
def __generate_size_parser(self):
|
def __generate_size_parser(self):
|
||||||
"""Generate a parser for the corresponding measurement tool."""
|
"""Generate a parser for the corresponding measurement tool."""
|
||||||
if re.match(r'size', self.code_size_common.measure_cmd.strip()):
|
if re.match(r'size', self.size_common_info.measure_cmd.strip()):
|
||||||
return CodeSizeGeneratorWithSize(self.logger)
|
return CodeSizeGeneratorWithSize(self.logger)
|
||||||
else:
|
else:
|
||||||
self.logger.error("Unsupported measurement tool: `{}`."
|
self.logger.error("Unsupported measurement tool: `{}`."
|
||||||
.format(self.code_size_common.measure_cmd
|
.format(self.size_common_info.measure_cmd
|
||||||
.strip().split(' ')[0]))
|
.strip().split(' ')[0]))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def cal_code_size(
|
def cal_code_size(
|
||||||
self,
|
self,
|
||||||
size_version: SimpleNamespace
|
size_dist_info: CodeSizeDistinctInfo
|
||||||
) -> typing.Dict[str, str]:
|
) -> typing.Dict[str, str]:
|
||||||
"""Calculate code size of library/*.o in a UTF-8 encoding"""
|
"""Calculate code size of library/*.o in a UTF-8 encoding"""
|
||||||
|
|
||||||
return CodeSizeCalculator(size_version.revision, size_version.make_cmd,
|
return CodeSizeCalculator(size_dist_info.git_rev,
|
||||||
self.code_size_common.measure_cmd,
|
size_dist_info.make_cmd,
|
||||||
|
self.size_common_info.measure_cmd,
|
||||||
self.logger).cal_libraries_code_size()
|
self.logger).cal_libraries_code_size()
|
||||||
|
|
||||||
def gen_file_name(
|
def gen_file_name(
|
||||||
self,
|
self,
|
||||||
old_size_version: SimpleNamespace,
|
old_size_dist_info: CodeSizeDistinctInfo,
|
||||||
new_size_version=None
|
new_size_dist_info=None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Generate a literal string as csv file name."""
|
"""Generate a literal string as csv file name."""
|
||||||
if new_size_version:
|
if new_size_dist_info:
|
||||||
return '{}-{}-{}-{}-{}-{}-{}.csv'\
|
return '{}-{}-{}-{}-{}-{}-{}.csv'\
|
||||||
.format(old_size_version.revision, old_size_version.arch,
|
.format(old_size_dist_info.git_rev, old_size_dist_info.arch,
|
||||||
old_size_version.config,
|
old_size_dist_info.config,
|
||||||
new_size_version.revision, new_size_version.arch,
|
new_size_dist_info.git_rev, new_size_dist_info.arch,
|
||||||
new_size_version.config,
|
new_size_dist_info.config,
|
||||||
self.code_size_common.measure_cmd.strip()\
|
self.size_common_info.measure_cmd.strip()\
|
||||||
.split(' ')[0])
|
.split(' ')[0])
|
||||||
else:
|
else:
|
||||||
return '{}-{}-{}-{}.csv'\
|
return '{}-{}-{}-{}.csv'\
|
||||||
.format(old_size_version.revision, old_size_version.arch,
|
.format(old_size_dist_info.git_rev,
|
||||||
old_size_version.config,
|
old_size_dist_info.arch,
|
||||||
self.code_size_common.measure_cmd.strip()\
|
old_size_dist_info.config,
|
||||||
|
self.size_common_info.measure_cmd.strip()\
|
||||||
.split(' ')[0])
|
.split(' ')[0])
|
||||||
|
|
||||||
def gen_code_size_report(self, size_version: SimpleNamespace) -> None:
|
def gen_code_size_report(self, size_dist_info: CodeSizeDistinctInfo) -> None:
|
||||||
"""Generate code size record and write it into a file."""
|
"""Generate code size record and write it into a file."""
|
||||||
|
|
||||||
self.logger.info("Start to generate code size record for {}."
|
self.logger.info("Start to generate code size record for {}."
|
||||||
.format(size_version.revision))
|
.format(size_dist_info.git_rev))
|
||||||
output_file = os.path.join(self.csv_dir,
|
output_file = os.path.join(self.csv_dir,
|
||||||
self.gen_file_name(size_version))
|
self.gen_file_name(size_dist_info))
|
||||||
# Check if the corresponding record exists
|
# Check if the corresponding record exists
|
||||||
if size_version.revision != "current" and \
|
if size_dist_info.git_rev != "current" and \
|
||||||
os.path.exists(output_file):
|
os.path.exists(output_file):
|
||||||
self.logger.debug("Code size csv file for {} already exists."
|
self.logger.debug("Code size csv file for {} already exists."
|
||||||
.format(size_version.revision))
|
.format(size_dist_info.git_rev))
|
||||||
self.code_size_generator.read_size_record(
|
self.code_size_generator.read_size_record(
|
||||||
size_version.revision, output_file)
|
size_dist_info.git_rev, output_file)
|
||||||
else:
|
else:
|
||||||
self.code_size_generator.size_generator_write_record(
|
self.code_size_generator.size_generator_write_record(
|
||||||
size_version.revision, self.cal_code_size(size_version),
|
size_dist_info.git_rev, self.cal_code_size(size_dist_info),
|
||||||
output_file)
|
output_file)
|
||||||
|
|
||||||
def gen_code_size_comparison(self) -> None:
|
def gen_code_size_comparison(self) -> None:
|
||||||
"""Generate results of code size changes between two revisions,
|
"""Generate results of code size changes between two Git revisions,
|
||||||
old and new.
|
old and new.
|
||||||
|
|
||||||
- Measured code size results of these two revisions must be available.
|
- Measured code size result of these two Git revisions must be available.
|
||||||
- The result is directed into either file / stdout depending on
|
- The result is directed into either file / stdout depending on
|
||||||
the option, code_size_common.result_options.stdout. (Default: file)
|
the option, size_common_info.result_options.stdout. (Default: file)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.logger.info("Start to generate comparision result between "\
|
self.logger.info("Start to generate comparision result between "\
|
||||||
"{} and {}."
|
"{} and {}."
|
||||||
.format(self.old_size_version.revision,
|
.format(self.old_size_dist_info.git_rev,
|
||||||
self.new_size_version.revision))
|
self.new_size_dist_info.git_rev))
|
||||||
output_file = os.path.join(
|
output_file = os.path.join(
|
||||||
self.result_dir,
|
self.comp_dir,
|
||||||
self.gen_file_name(self.old_size_version, self.new_size_version))
|
self.gen_file_name(self.old_size_dist_info, self.new_size_dist_info))
|
||||||
|
|
||||||
self.code_size_generator.size_generator_write_comparison(
|
self.code_size_generator.size_generator_write_comparison(
|
||||||
self.old_size_version.revision, self.new_size_version.revision,
|
self.old_size_dist_info.git_rev,
|
||||||
output_file, self.code_size_common.result_options)
|
self.new_size_dist_info.git_rev,
|
||||||
|
output_file, self.result_options)
|
||||||
|
|
||||||
def get_comparision_results(self) -> None:
|
def get_comparision_results(self) -> None:
|
||||||
"""Compare size of library/*.o between self.old_size_version and
|
"""Compare size of library/*.o between self.old_size_dist_info and
|
||||||
self.old_size_version and generate the result file."""
|
self.old_size_dist_info and generate the result file."""
|
||||||
build_tree.check_repo_path()
|
build_tree.check_repo_path()
|
||||||
self.gen_code_size_report(self.old_size_version)
|
self.gen_code_size_report(self.old_size_dist_info)
|
||||||
self.gen_code_size_report(self.new_size_version)
|
self.gen_code_size_report(self.new_size_dist_info)
|
||||||
self.gen_code_size_comparison()
|
self.gen_code_size_comparison()
|
||||||
|
|
||||||
|
|
||||||
@ -674,18 +745,22 @@ def main():
|
|||||||
'required arguments to parse for running ' + os.path.basename(__file__))
|
'required arguments to parse for running ' + os.path.basename(__file__))
|
||||||
group_required.add_argument(
|
group_required.add_argument(
|
||||||
'-o', '--old-rev', type=str, required=True,
|
'-o', '--old-rev', type=str, required=True,
|
||||||
help='old revision for comparison.')
|
help='old Git revision for comparison.')
|
||||||
|
|
||||||
group_optional = parser.add_argument_group(
|
group_optional = parser.add_argument_group(
|
||||||
'optional arguments',
|
'optional arguments',
|
||||||
'optional arguments to parse for running ' + os.path.basename(__file__))
|
'optional arguments to parse for running ' + os.path.basename(__file__))
|
||||||
group_optional.add_argument(
|
group_optional.add_argument(
|
||||||
'-r', '--result-dir', type=str, default='comparison',
|
'--record_dir', type=str, default='code_size_records',
|
||||||
|
help='directory where code size record is stored. '
|
||||||
|
'(Default: code_size_records)')
|
||||||
|
group_optional.add_argument(
|
||||||
|
'-r', '--comp-dir', type=str, default='comparison',
|
||||||
help='directory where comparison result is stored. '
|
help='directory where comparison result is stored. '
|
||||||
'(Default: comparison)')
|
'(Default: comparison)')
|
||||||
group_optional.add_argument(
|
group_optional.add_argument(
|
||||||
'-n', '--new-rev', type=str, default=None,
|
'-n', '--new-rev', type=str, default=None,
|
||||||
help='new revision as comparison base. '
|
help='new Git revision as comparison base. '
|
||||||
'(Default is the current work directory, including uncommitted '
|
'(Default is the current work directory, including uncommitted '
|
||||||
'changes.)')
|
'changes.)')
|
||||||
group_optional.add_argument(
|
group_optional.add_argument(
|
||||||
@ -716,48 +791,36 @@ def main():
|
|||||||
logging_util.configure_logger(logger)
|
logging_util.configure_logger(logger)
|
||||||
logger.setLevel(logging.DEBUG if comp_args.verbose else logging.INFO)
|
logger.setLevel(logging.DEBUG if comp_args.verbose else logging.INFO)
|
||||||
|
|
||||||
if os.path.isfile(comp_args.result_dir):
|
if os.path.isfile(comp_args.comp_dir):
|
||||||
logger.error("{} is not a directory".format(comp_args.result_dir))
|
logger.error("{} is not a directory".format(comp_args.comp_dir))
|
||||||
parser.exit()
|
parser.exit()
|
||||||
|
|
||||||
old_revision = CodeSizeCalculator.validate_revision(comp_args.old_rev)
|
old_revision = CodeSizeCalculator.validate_git_revision(comp_args.old_rev)
|
||||||
if comp_args.new_rev is not None:
|
if comp_args.new_rev is not None:
|
||||||
new_revision = CodeSizeCalculator.validate_revision(comp_args.new_rev)
|
new_revision = CodeSizeCalculator.validate_git_revision(
|
||||||
|
comp_args.new_rev)
|
||||||
else:
|
else:
|
||||||
new_revision = "current"
|
new_revision = "current"
|
||||||
|
|
||||||
old_size_version = SimpleNamespace(
|
old_size_dist_info = CodeSizeDistinctInfo(
|
||||||
version='old',
|
'old', old_revision, comp_args.arch, comp_args.config, '')
|
||||||
revision=old_revision,
|
new_size_dist_info = CodeSizeDistinctInfo(
|
||||||
config=comp_args.config,
|
'new', new_revision, comp_args.arch, comp_args.config, '')
|
||||||
arch=comp_args.arch,
|
size_common_info = CodeSizeCommonInfo(
|
||||||
make_cmd='',
|
detect_arch(), 'size -t')
|
||||||
)
|
result_options = CodeSizeResultInfo(
|
||||||
new_size_version = SimpleNamespace(
|
comp_args.record_dir, comp_args.comp_dir,
|
||||||
version='new',
|
comp_args.markdown, comp_args.stdout)
|
||||||
revision=new_revision,
|
|
||||||
config=comp_args.config,
|
|
||||||
arch=comp_args.arch,
|
|
||||||
make_cmd='',
|
|
||||||
)
|
|
||||||
code_size_common = SimpleNamespace(
|
|
||||||
result_options=SimpleNamespace(
|
|
||||||
result_dir=comp_args.result_dir,
|
|
||||||
with_markdown=comp_args.markdown,
|
|
||||||
stdout=comp_args.stdout,
|
|
||||||
),
|
|
||||||
host_arch=detect_arch(),
|
|
||||||
measure_cmd='size -t',
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("Measure code size between {}:{}-{} and {}:{}-{} by `{}`."
|
logger.info("Measure code size between {}:{}-{} and {}:{}-{} by `{}`."
|
||||||
.format(old_size_version.revision, old_size_version.config,
|
.format(old_size_dist_info.git_rev, old_size_dist_info.config,
|
||||||
old_size_version.arch,
|
old_size_dist_info.arch,
|
||||||
new_size_version.revision, old_size_version.config,
|
new_size_dist_info.git_rev, old_size_dist_info.config,
|
||||||
new_size_version.arch,
|
new_size_dist_info.arch,
|
||||||
code_size_common.measure_cmd.strip().split(' ')[0]))
|
size_common_info.measure_cmd.strip().split(' ')[0]))
|
||||||
CodeSizeComparison(old_size_version, new_size_version,
|
CodeSizeComparison(old_size_dist_info, new_size_dist_info,
|
||||||
code_size_common, logger).get_comparision_results()
|
size_common_info, result_options,
|
||||||
|
logger).get_comparision_results()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user