summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-12-02 12:50:31 +0700
committerhathach <[email protected]>2025-12-02 13:01:49 +0700
commitc859744784cc396ae0993a16a1935b10fbd9b797 (patch)
tree6f7c3487dacad035922f1f3c18e5411dd39003da
parenta337a6d337c0cdd50981ba2040aee99966ae3152 (diff)
adding metrics for computing average compiled size
-rw-r--r--examples/device/CMakeLists.txt63
-rw-r--r--examples/dual/CMakeLists.txt10
-rw-r--r--examples/host/CMakeLists.txt20
-rw-r--r--hw/bsp/family_support.cmake6
-rwxr-xr-xtools/build.py4
-rwxr-xr-xtools/get_deps.py2
-rw-r--r--tools/metrics.py134
7 files changed, 193 insertions, 46 deletions
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt
index eb625ea51..660df67cb 100644
--- a/examples/device/CMakeLists.txt
+++ b/examples/device/CMakeLists.txt
@@ -6,31 +6,38 @@ project(tinyusb_device_examples C CXX ASM)
family_initialize_project(tinyusb_device_examples ${CMAKE_CURRENT_LIST_DIR})
# family_add_subdirectory will filter what to actually add based on selected FAMILY
-family_add_subdirectory(audio_4_channel_mic)
-family_add_subdirectory(audio_test)
-family_add_subdirectory(audio_4_channel_mic_freertos)
-family_add_subdirectory(audio_test_freertos)
-family_add_subdirectory(audio_test_multi_rate)
-family_add_subdirectory(board_test)
-family_add_subdirectory(cdc_dual_ports)
-family_add_subdirectory(cdc_msc)
-family_add_subdirectory(cdc_msc_freertos)
-family_add_subdirectory(cdc_uac2)
-family_add_subdirectory(dfu)
-family_add_subdirectory(dfu_runtime)
-family_add_subdirectory(dynamic_configuration)
-family_add_subdirectory(hid_boot_interface)
-family_add_subdirectory(hid_composite)
-family_add_subdirectory(hid_composite_freertos)
-family_add_subdirectory(hid_generic_inout)
-family_add_subdirectory(hid_multiple_interface)
-family_add_subdirectory(midi_test)
-family_add_subdirectory(msc_dual_lun)
-family_add_subdirectory(mtp)
-family_add_subdirectory(net_lwip_webserver)
-family_add_subdirectory(uac2_headset)
-family_add_subdirectory(uac2_speaker_fb)
-family_add_subdirectory(usbtmc)
-family_add_subdirectory(video_capture)
-family_add_subdirectory(video_capture_2ch)
-family_add_subdirectory(webusb_serial)
+set(EXAMPLE_LIST
+ audio_4_channel_mic
+ audio_4_channel_mic_freertos
+ audio_test
+ audio_test_freertos
+ audio_test_multi_rate
+ board_test
+ cdc_dual_ports
+ cdc_msc
+ cdc_msc_freertos
+ cdc_uac2
+ dfu
+ dfu_runtime
+ dynamic_configuration
+ hid_boot_interface
+ hid_composite
+ hid_composite_freertos
+ hid_generic_inout
+ hid_multiple_interface
+ midi_test
+ midi_test_freertos
+ msc_dual_lun
+ mtp
+ net_lwip_webserver
+ uac2_headset
+ uac2_speaker_fb
+ usbtmc
+ video_capture
+ video_capture_2ch
+ webusb_serial
+ )
+
+foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+endforeach ()
diff --git a/examples/dual/CMakeLists.txt b/examples/dual/CMakeLists.txt
index c5e3ffce4..4978f1fab 100644
--- a/examples/dual/CMakeLists.txt
+++ b/examples/dual/CMakeLists.txt
@@ -9,6 +9,12 @@ if (FAMILY STREQUAL "rp2040" AND NOT TARGET tinyusb_pico_pio_usb)
message("Skipping dual host/device mode examples as Pico-PIO-USB is not available")
else ()
# family_add_subdirectory will filter what to actually add based on selected FAMILY
- family_add_subdirectory(host_hid_to_device_cdc)
- family_add_subdirectory(host_info_to_device_cdc)
+ set(EXAMPLE_LIST
+ host_hid_to_device_cdc
+ host_info_to_device_cdc
+ )
+
+ foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+ endforeach ()
endif ()
diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt
index 2783dd84e..f8e0ce692 100644
--- a/examples/host/CMakeLists.txt
+++ b/examples/host/CMakeLists.txt
@@ -6,10 +6,16 @@ project(tinyusb_host_examples C CXX ASM)
family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR})
# family_add_subdirectory will filter what to actually add based on selected FAMILY
-family_add_subdirectory(bare_api)
-family_add_subdirectory(cdc_msc_hid)
-family_add_subdirectory(cdc_msc_hid_freertos)
-family_add_subdirectory(device_info)
-family_add_subdirectory(hid_controller)
-family_add_subdirectory(midi_rx)
-family_add_subdirectory(msc_file_explorer)
+set(EXAMPLE_LIST
+ bare_api
+ cdc_msc_hid
+ cdc_msc_hid_freertos
+ device_info
+ hid_controller
+ midi_rx
+ msc_file_explorer
+ )
+
+foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+endforeach ()
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index 1f91d0910..e7dfc19c8 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -231,12 +231,6 @@ function(family_add_linkermap TARGET)
separate_arguments(LINKERMAP_OPTION_LIST UNIX_COMMAND ${LINKERMAP_OPTION})
endif ()
- if (ARGC GREATER 1)
- separate_arguments(ARG_OPTION_LIST UNIX_COMMAND ${ARGV1})
- list(APPEND LINKERMAP_OPTION_LIST ${ARG_OPTION_LIST})
- endif ()
-
- # target
add_custom_target(${TARGET}-linkermap
COMMAND python ${LINKERMAP_PY} -j -m ${LINKERMAP_OPTION_LIST} $<TARGET_FILE:${TARGET}>.map
VERBATIM
diff --git a/tools/build.py b/tools/build.py
index 5328a987f..692853297 100755
--- a/tools/build.py
+++ b/tools/build.py
@@ -113,8 +113,8 @@ def cmake_board(board, build_args, build_flags_on):
else:
rcmd = run_cmd([
'cmake', 'examples', '-B', build_dir, '-GNinja',
- f'-DBOARD={board}', '-DCMAKE_BUILD_TYPE=MinSizeRel',
- '-DLINKERMAP_OPTION=-q -f tinyusb/src', *build_args, *build_flags
+ f'-DBOARD={board}', '-DCMAKE_BUILD_TYPE=MinSizeRel', '-DLINKERMAP_OPTION=-q -f tinyusb/src',
+ *build_args, *build_flags
])
if rcmd.returncode == 0:
cmd = [
diff --git a/tools/get_deps.py b/tools/get_deps.py
index 47cc5c7dd..5fb7e022c 100755
--- a/tools/get_deps.py
+++ b/tools/get_deps.py
@@ -15,7 +15,7 @@ deps_mandatory = {
'159e31b689577dbf69cf0683bbaffbd71fa5ee10',
'all'],
'tools/linkermap': ['https://github.com/hathach/linkermap.git',
- '1f47651142646398c7746e109ae0481732aeb564',
+ 'ac1228d5bbde1e54cb2e17e928662094ae19c51d',
'all'],
'tools/uf2': ['https://github.com/microsoft/uf2.git',
'c594542b2faa01cc33a2b97c9fbebc38549df80a',
diff --git a/tools/metrics.py b/tools/metrics.py
new file mode 100644
index 000000000..d972d3681
--- /dev/null
+++ b/tools/metrics.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+"""Calculate average size from multiple linker map files."""
+
+import argparse
+import sys
+import os
+
+# Add linkermap module to path
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'linkermap'))
+import linkermap
+
+
+def combine_maps(map_files, filters=None):
+ """Combine multiple map files into a list of json_data.
+
+ Args:
+ map_files: List of paths to linker map files or JSON files
+ filters: List of path substrings to filter object files (default: [])
+
+ Returns:
+ all_json_data: Dictionary with mapfiles list and data from each map file
+ """
+ import json
+
+ filters = filters or []
+ all_json_data = {"mapfiles": [], "data": []}
+
+ for map_file in map_files:
+ if not os.path.exists(map_file):
+ print(f"Warning: {map_file} not found, skipping", file=sys.stderr)
+ continue
+
+ try:
+ if map_file.endswith('.json'):
+ with open(map_file, 'r', encoding='utf-8') as f:
+ json_data = json.load(f)
+ # Apply path filters to JSON data
+ if filters:
+ filtered_files = [
+ f for f in json_data["files"]
+ if f.get("path") and any(filt in f["path"] for filt in filters)
+ ]
+ json_data["files"] = filtered_files
+ else:
+ json_data = linkermap.analyze_map(map_file, filters=filters)
+ all_json_data["mapfiles"].append(map_file)
+ all_json_data["data"].append(json_data)
+ except Exception as e:
+ print(f"Warning: Failed to analyze {map_file}: {e}", file=sys.stderr)
+ continue
+
+ return all_json_data
+
+
+def compute_avg(all_json_data):
+ """Compute average sizes from combined json_data.
+
+ Args:
+ all_json_data: Dictionary with mapfiles and data from combine_maps()
+
+ Returns:
+ json_average: Dictionary with averaged size data
+ """
+ if not all_json_data["data"]:
+ return None
+
+ # Collect all sections preserving order
+ all_sections = []
+ for json_data in all_json_data["data"]:
+ for s in json_data["sections"]:
+ if s not in all_sections:
+ all_sections.append(s)
+
+ # Merge files with the same 'file' value and compute averages
+ file_accumulator = {} # key: file name, value: {"sections": {section: [sizes]}, "totals": [totals]}
+
+ for json_data in all_json_data["data"]:
+ for f in json_data["files"]:
+ fname = f["file"]
+ if fname not in file_accumulator:
+ file_accumulator[fname] = {"sections": {}, "totals": [], "path": f.get("path")}
+ file_accumulator[fname]["totals"].append(f["total"])
+ for section, size in f["sections"].items():
+ if section in file_accumulator[fname]["sections"]:
+ file_accumulator[fname]["sections"][section].append(size)
+ else:
+ file_accumulator[fname]["sections"][section] = [size]
+
+ # Build json_average with averaged values
+ files_average = []
+ for fname, data in file_accumulator.items():
+ avg_total = round(sum(data["totals"]) / len(data["totals"]))
+ avg_sections = {}
+ for section, sizes in data["sections"].items():
+ avg_sections[section] = round(sum(sizes) / len(sizes))
+ files_average.append({
+ "file": fname,
+ "path": data["path"],
+ "sections": avg_sections,
+ "total": avg_total
+ })
+
+ json_average = {
+ "mapfiles": all_json_data["mapfiles"],
+ "sections": all_sections,
+ "files": files_average
+ }
+
+ return json_average
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Calculate average size from linker map files')
+ parser.add_argument('files', nargs='+', help='Path to map file(s)')
+ parser.add_argument('-f', '--filter', dest='filters', action='append', default=[],
+ help='Only include object files whose path contains this substring (can be repeated)')
+ parser.add_argument('-o', '--out', dest='out', default='metrics',
+ help='Output path basename for JSON and Markdown files (default: metrics)')
+ args = parser.parse_args()
+
+ all_json_data = combine_maps(args.files, args.filters)
+ json_average = compute_avg(all_json_data)
+
+ if json_average is None:
+ print("No valid map files found", file=sys.stderr)
+ sys.exit(1)
+
+ linkermap.print_summary(json_average, False)
+ linkermap.write_json(json_average, args.out + '.json')
+ linkermap.write_markdown(json_average, args.out + '.md')
+
+
+if __name__ == '__main__':
+ main()