Skip to content

Commit 1cb1fa2

Browse files
authored
Merge pull request #70 from per1234/report-relative-sizes
libraries/compile-examples, libraries/report-size-deltas: Report relative memory usage data
2 parents f969b20 + f2daf20 commit 1cb1fa2

File tree

2 files changed

+333
-71
lines changed

2 files changed

+333
-71
lines changed

compilesketches/compilesketches.py

Lines changed: 120 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class RunCommandOutput(enum.Enum):
7171
ALWAYS = enum.auto()
7272

7373
not_applicable_indicator = "N/A"
74+
relative_size_report_decimal_places = 2
7475

7576
arduino_cli_installation_path = pathlib.Path.home().joinpath("bin")
7677
arduino_cli_user_directory_path = pathlib.Path.home().joinpath("Arduino")
@@ -88,6 +89,7 @@ class ReportKeys:
8889
sizes = "sizes"
8990
name = "name"
9091
absolute = "absolute"
92+
relative = "relative"
9193
current = "current"
9294
previous = "previous"
9395
delta = "delta"
@@ -837,11 +839,24 @@ def get_sizes_from_output(self, compilation_result):
837839
memory_types = [
838840
{
839841
"name": "flash",
840-
"regex": r"Sketch uses [0-9]+ bytes .*of program storage space\."
842+
# Use capturing parentheses to identify the location of the data in the regular expression
843+
"regex": {
844+
# The regular expression for the absolute memory usage
845+
self.ReportKeys.absolute: r"Sketch uses ([0-9]+) bytes .*of program storage space\.",
846+
# The regular expression for the total memory
847+
self.ReportKeys.maximum: (
848+
r"Sketch uses [0-9]+ bytes .*of program storage space\. Maximum is ([0-9]+) bytes."
849+
)
850+
}
841851
},
842852
{
843853
"name": "RAM for global variables",
844-
"regex": r"Global variables use [0-9]+ bytes .*of dynamic memory"
854+
"regex": {
855+
self.ReportKeys.absolute: r"Global variables use ([0-9]+) bytes .*of dynamic memory",
856+
self.ReportKeys.maximum: (
857+
r"Global variables use [0-9]+ bytes .*of dynamic memory.*\. Maximum is ([0-9]+) bytes."
858+
)
859+
}
845860
}
846861
]
847862

@@ -850,35 +865,64 @@ def get_sizes_from_output(self, compilation_result):
850865
size = {
851866
self.ReportKeys.name: memory_type["name"],
852867
# Set default memory usage value, to be used if memory usage can't be determined
853-
self.ReportKeys.absolute: self.not_applicable_indicator
868+
self.ReportKeys.absolute: self.not_applicable_indicator,
869+
self.ReportKeys.maximum: self.not_applicable_indicator,
870+
self.ReportKeys.relative: self.not_applicable_indicator
854871
}
855872

856873
if compilation_result.success is True:
857874
# Determine memory usage of the sketch by parsing Arduino CLI's output
858-
regex_match = re.search(pattern=memory_type["regex"], string=compilation_result.output)
859-
if regex_match:
860-
size[self.ReportKeys.absolute] = int(
861-
re.search(pattern="[0-9]+", string=regex_match.group(0)).group(0))
862-
else:
863-
# If any of the following:
864-
# - recipe.size.regex is not defined in platform.txt
865-
# - upload.maximum_size is not defined in boards.txt
866-
# flash usage will not be reported in the Arduino CLI output
867-
# If any of the following:
868-
# - recipe.size.regex.data is not defined in platform.txt (e.g., Arduino SAM Boards)
869-
# - recipe.size.regex is not defined in platform.txt
870-
# - upload.maximum_size is not defined in boards.txt
871-
# RAM usage will not be reported in the Arduino CLI output
872-
self.verbose_print(
873-
"::warning::Unable to determine",
874-
memory_type["name"],
875-
"memory usage. The board's platform may not have been configured to provide this information."
876-
)
875+
size_data = self.get_size_data_from_output(compilation_output=compilation_result.output,
876+
memory_type=memory_type,
877+
size_data_type=self.ReportKeys.absolute)
878+
if size_data:
879+
size[self.ReportKeys.absolute] = size_data
880+
881+
size_data = self.get_size_data_from_output(compilation_output=compilation_result.output,
882+
memory_type=memory_type,
883+
size_data_type=self.ReportKeys.maximum)
884+
if size_data:
885+
size[self.ReportKeys.maximum] = size_data
886+
887+
size[self.ReportKeys.relative] = round(
888+
(100 * size[self.ReportKeys.absolute] / size[self.ReportKeys.maximum]),
889+
self.relative_size_report_decimal_places
890+
)
877891

878892
sizes.append(size)
879893

880894
return sizes
881895

896+
def get_size_data_from_output(self, compilation_output, memory_type, size_data_type):
897+
"""Parse the stdout from the compilation process for a specific datum and return it, or None if not found.
898+
899+
Keyword arguments:
900+
compilation_output -- stdout from the compilation process
901+
memory_type -- dictionary defining a memory type
902+
size_data_type -- the type of size data to get
903+
"""
904+
size_data = None
905+
regex_match = re.search(pattern=memory_type["regex"][size_data_type], string=compilation_output)
906+
if regex_match:
907+
size_data = int(regex_match.group(1))
908+
else:
909+
# If any of the following:
910+
# - recipe.size.regex is not defined in platform.txt
911+
# - upload.maximum_size is not defined in boards.txt
912+
# flash usage will not be reported in the Arduino CLI output
913+
# If any of the following:
914+
# - recipe.size.regex.data is not defined in platform.txt (e.g., Arduino SAM Boards)
915+
# - recipe.size.regex is not defined in platform.txt
916+
# - upload.maximum_size is not defined in boards.txt
917+
# RAM usage will not be reported in the Arduino CLI output
918+
self.verbose_print(
919+
"::warning::Unable to determine the: \"" + size_data_type + "\" value for memory type: \""
920+
+ memory_type["name"]
921+
+ "\". The board's platform may not have been configured to provide this information."
922+
)
923+
924+
return size_data
925+
882926
def do_size_deltas_report(self, compilation_result, current_sizes):
883927
"""Return whether size deltas reporting is enabled.
884928
@@ -934,8 +978,10 @@ def get_size_report(self, current_size, previous_size):
934978
"""
935979
size_report = {
936980
self.ReportKeys.name: current_size[self.ReportKeys.name],
981+
self.ReportKeys.maximum: current_size[self.ReportKeys.maximum],
937982
self.ReportKeys.current: {
938-
self.ReportKeys.absolute: current_size[self.ReportKeys.absolute]
983+
self.ReportKeys.absolute: current_size[self.ReportKeys.absolute],
984+
self.ReportKeys.relative: current_size[self.ReportKeys.relative]
939985
}
940986
}
941987

@@ -949,15 +995,30 @@ def get_size_report(self, current_size, previous_size):
949995
else:
950996
absolute_delta = (current_size[self.ReportKeys.absolute] - previous_size[self.ReportKeys.absolute])
951997

998+
if (
999+
absolute_delta == self.not_applicable_indicator
1000+
or size_report[self.ReportKeys.maximum] == self.not_applicable_indicator
1001+
):
1002+
relative_delta = self.not_applicable_indicator
1003+
else:
1004+
# Calculate from absolute values to avoid rounding errors
1005+
relative_delta = round((100 * absolute_delta / size_report[self.ReportKeys.maximum]),
1006+
self.relative_size_report_decimal_places)
1007+
9521008
# Size deltas reports are enabled
9531009
# Print the memory usage change data to the log
954-
print("Change in", current_size[self.ReportKeys.name] + ":", absolute_delta)
1010+
delta_message = "Change in " + str(current_size[self.ReportKeys.name]) + ": " + str(absolute_delta)
1011+
if relative_delta != self.not_applicable_indicator:
1012+
delta_message += " (" + str(relative_delta) + "%)"
1013+
print(delta_message)
9551014

9561015
size_report[self.ReportKeys.previous] = {
957-
self.ReportKeys.absolute: previous_size[self.ReportKeys.absolute]
1016+
self.ReportKeys.absolute: previous_size[self.ReportKeys.absolute],
1017+
self.ReportKeys.relative: previous_size[self.ReportKeys.relative]
9581018
}
9591019
size_report[self.ReportKeys.delta] = {
960-
self.ReportKeys.absolute: absolute_delta
1020+
self.ReportKeys.absolute: absolute_delta,
1021+
self.ReportKeys.relative: relative_delta
9611022
}
9621023

9631024
return size_report
@@ -1013,19 +1074,33 @@ def get_sizes_summary_report(self, sketch_report_list):
10131074
sizes_summary_report.append(
10141075
{
10151076
self.ReportKeys.name: size_report[self.ReportKeys.name],
1077+
self.ReportKeys.maximum: size_report[self.ReportKeys.maximum],
10161078
self.ReportKeys.delta: {
10171079
self.ReportKeys.absolute: {
10181080
self.ReportKeys.minimum: size_report[self.ReportKeys.delta][
10191081
self.ReportKeys.absolute],
10201082
self.ReportKeys.maximum: size_report[self.ReportKeys.delta][
10211083
self.ReportKeys.absolute]
1022-
}
1084+
},
1085+
self.ReportKeys.relative: {
1086+
self.ReportKeys.minimum: size_report[self.ReportKeys.delta][
1087+
self.ReportKeys.relative],
1088+
self.ReportKeys.maximum: size_report[self.ReportKeys.delta][
1089+
self.ReportKeys.relative]
1090+
},
10231091
}
10241092
}
10251093
)
10261094
else:
10271095
size_summary_report_index = size_summary_report_index_list[0]
10281096

1097+
if (
1098+
sizes_summary_report[size_summary_report_index][
1099+
self.ReportKeys.maximum] == self.not_applicable_indicator
1100+
):
1101+
sizes_summary_report[size_summary_report_index][
1102+
self.ReportKeys.maximum] = size_report[self.ReportKeys.maximum]
1103+
10291104
if (
10301105
sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
10311106
self.ReportKeys.absolute][self.ReportKeys.minimum] == self.not_applicable_indicator
@@ -1034,10 +1109,18 @@ def get_sizes_summary_report(self, sketch_report_list):
10341109
self.ReportKeys.absolute][self.ReportKeys.minimum] = size_report[self.ReportKeys.delta][
10351110
self.ReportKeys.absolute]
10361111

1112+
sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
1113+
self.ReportKeys.relative][self.ReportKeys.minimum] = size_report[self.ReportKeys.delta][
1114+
self.ReportKeys.relative]
1115+
10371116
sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
10381117
self.ReportKeys.absolute][self.ReportKeys.maximum] = size_report[self.ReportKeys.delta][
10391118
self.ReportKeys.absolute]
10401119

1120+
sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
1121+
self.ReportKeys.relative][self.ReportKeys.maximum] = size_report[self.ReportKeys.delta][
1122+
self.ReportKeys.relative]
1123+
10411124
elif size_report[self.ReportKeys.delta][self.ReportKeys.absolute] != (
10421125
self.not_applicable_indicator
10431126
):
@@ -1049,6 +1132,11 @@ def get_sizes_summary_report(self, sketch_report_list):
10491132
size_report[self.ReportKeys.delta][self.ReportKeys.absolute]
10501133
)
10511134

1135+
sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
1136+
self.ReportKeys.relative][self.ReportKeys.minimum] = (
1137+
size_report[self.ReportKeys.delta][self.ReportKeys.relative]
1138+
)
1139+
10521140
if (size_report[self.ReportKeys.delta][self.ReportKeys.absolute]
10531141
> sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
10541142
self.ReportKeys.absolute][self.ReportKeys.maximum]):
@@ -1057,6 +1145,11 @@ def get_sizes_summary_report(self, sketch_report_list):
10571145
size_report[self.ReportKeys.delta][self.ReportKeys.absolute]
10581146
)
10591147

1148+
sizes_summary_report[size_summary_report_index][self.ReportKeys.delta][
1149+
self.ReportKeys.relative][self.ReportKeys.maximum] = (
1150+
size_report[self.ReportKeys.delta][self.ReportKeys.relative]
1151+
)
1152+
10601153
return sizes_summary_report
10611154

10621155
def create_sketches_report_file(self, sketches_report):

0 commit comments

Comments
 (0)