MCU作为一种集成了处理器、存储器和外设接口等功能的微控制器,其资源包括存储器、计算能力、输入输出端口等。然而,大多数MCU的资源都是有限的,特别是在一些资源受限的应用中,如小型便携设备或低功耗系统。在这些情况下,优化占用空间变得至关重要,以确保系统的性能、功能和可靠性,同时最大限度地减少资源的使用。Zephyr提供了一系列工具用于分析Zephyr镜像占用的Flash和内存大小,主要涵盖的是在编译期可确定的占用存储大小:
- RAM占用:数据段和BSS段
- ROM占用:代码段和只读内容
- 函数堆栈占用
本文的所有示例都基于mm_feather的shell示例进行展示,首先进行目标进行编译
west init -m https://github.com/zephyrproject-rtos/zephyr --mr v3.4.0 zephyr_v3.4.0
cd zephyr_v3.4.0
west build -b mm_feather zephyr/samples/subsys/shell/shell_module/
ram_report 链接到标题
这是zephyr构建系统自带的一个工具,在构建镜像完成后执行下面命令
west build -t ram_report
结果将以树形列出所有已编译对象及其 RAM 使用情况,其中包含每个符号的字节数及其使用的百分比。通过对这些数据的查看和分析找到哪些模块在大量占用RAM,并进行针对性的修改
Path Size %
==============================================================================================================
Root 14628 100.00%
├── (hidden) 0 0.00%
├── (no paths) 4138 28.29%
│ ├── _cpus_active 4 0.03%
│ ├── _kernel 56 0.38%
│ ├── _sw_isr_table 1280 8.75%
│ ├── buf.0 1 0.01%
│ ├── dynamic_regions.0 12 0.08%
│ ├── g_rtcXtalFreq 4 0.03%
│ ├── g_xtalFreq 4 0.03%
│ ├── in_use.0 2 0.01%
│ ├── k_sys_work_q 232 1.59%
│ ├── optreset 4 0.03%
│ ├── shell_init_work.2 16 0.11%
│ ├── tail.1 2 0.01%
│ ├── tx_busy.1 1 0.01%
│ ├── wait_q.0 8 0.05%
│ ├── z_idle_threads 200 1.37%
│ ├── z_interrupt_stacks 2112 14.44%
│ └── z_main_thread 200 1.37%
├── arch 5 0.03%
│ └── arm 5 0.03%
│ └── core 5 0.03%
│ └── aarch32 5 0.03%
│ └── mpu 5 0.03%
│ ├── arm_core_mpu.c 4 0.03%
│ │ └── log_dynamic_mpu 4 0.03%
│ └── arm_mpu.c 1 0.01%
│ └── static_regions_num 1 0.01%
├── drivers 242 1.65%
│ ├── clock_control 6 0.04%
│ │ └── clock_control_mcux_ccm.c 6 0.04%
│ │ ├── __devstate_dts_ord_53 2 0.01%
│ │ └── log_dynamic_clock_control 4 0.03%
│ ├── gpio 126 0.86%
│ │ └── gpio_mcux_igpio.c 126 0.86%
│ │ ├── __devstate_dts_ord_124 2 0.01%
│ │ ├── __devstate_dts_ord_157 2 0.01%
│ │ ├── __devstate_dts_ord_190 2 0.01%
│ │ ├── __devstate_dts_ord_219 2 0.01%
│ │ ├── __devstate_dts_ord_252 2 0.01%
│ │ ├── __devstate_dts_ord_326 2 0.01%
│ │ ├── __devstate_dts_ord_45 2 0.01%
│ │ ├── __devstate_dts_ord_62 2 0.01%
│ │ ├── __devstate_dts_ord_91 2 0.01%
│ │ ├── mcux_igpio_0_data 12 0.08%
│ │ ├── mcux_igpio_1_data 12 0.08%
│ │ ├── mcux_igpio_2_data 12 0.08%
│ │ ├── mcux_igpio_3_data 12 0.08%
│ │ ├── mcux_igpio_4_data 12 0.08%
│ │ ├── mcux_igpio_5_data 12 0.08%
│ │ ├── mcux_igpio_6_data 12 0.08%
│ │ ├── mcux_igpio_7_data 12 0.08%
│ │ └── mcux_igpio_8_data 12 0.08%
│ ├── serial 94 0.64%
│ │ └── uart_mcux_lpuart.c 94 0.64%
│ │ ├── __devstate_dts_ord_283 2 0.01%
│ │ ├── __devstate_dts_ord_284
└── subsys 6816 46.60%
├── logging 2254 15.41%
│ ├── log_core.c 2242 15.33%
│ │ ├── backend_attached 1 0.01%
│ │ ├── buf32 1024 7.00%
│ │ ├── buffered_cnt 4 0.03%
│ │ ├── curr_log_buffer 4 0.03%
│ │ ├── dropped_cnt 4 0.03%
│ │ ├── initialized 4 0.03%
│ │ ├── last_failure_report 8 0.05%
例如我们的硬件并没有使用gpio 4~8,而镜像分析结果含有mcux_igpio_4_data~mcux_igpio_8_data,那么我们可以通过设备树将其移除。 例如我们看到log_core.c的buf32占用RAM比较大,我们可以通过查看代码分析看是否可以适当缩小buf32
rom_report 链接到标题
这是zephyr构建系统自带的一个工具,在构建镜像完成后执行下面命令
west build -t rom_report
结果将以树形列出所有已编译对象及其 ROM 使用情况,其中包含每个符号的字节数及其使用的百分比。通过对这些数据的查看和分析找到哪些模块在大量占用ROM,并进行针对性的修改
Root 75428 100.00%
├── (hidden) 16305 21.62%
├── (no paths) 2677 3.55%
│ ├── CSWTCH.483 20 0.03%
│ ├── __aeabi_idiv0 2 0.00%
│ ├── __device_dts_ord_124 24 0.03%
│ ├── __device_dts_ord_157 24 0.03%
│ ├── __device_dts_ord_190 24 0.03%
│ ├── __device_dts_ord_219 24 0.03%
...
└── ZEPHYR_BASE 54156 71.80%
├── arch 3034 4.02%
│ └── arm 3034 4.02%
│ └── core 3034 4.02%
│ └── aarch32 3034 4.02%
│ ├── cortex_m 1908 2.53%
│ │ ├── fault.c 1748 2.32%
│ │ │ ├── bus_fault.constprop.0 396 0.53%
│ │ │ ├── mem_manage_fault 424 0.56%
│ │ │ ├── usage_fault.constprop.0 308 0.41%
│ │ │ ├── z_arm_fault 604 0.80%
│ │ │ └── z_arm_fault_init 16 0.02%
│ │ ├── irq_init.c 24 0.03%
│ │ │ └── z_arm_interrupt_init 24 0.03%
│ │ ├── thread_abort.c 44 0.06%
│ │ │ └── z_impl_k_thread_abort 44 0.06%
│ │ └── timing.c 92 0.12%
│ │ ├── arch_timing_counter_get 12 0.02%
│ │ ├── arch_timing_init 60 0.08%
│ │ └── arch_timing_start 20 0.03%
...
├── drivers 8576 11.37%
│ ├── clock_control 158 0.21%
│ │ └── clock_control_mcux_ccm.c 158 0.21%
│ │ ├── __devstate_dts_ord_53 2 0.00%
│ │ ├── __init___device_dts_ord_53 8 0.01%
│ │ ├── log_const_clock_control 8 0.01%
│ │ ├── log_dynamic_clock_control 4 0.01%
│ │ ├── mcux_ccm_driver_api 28 0.04%
│ │ ├── mcux_ccm_get_subsys_rate 104 0.14%
│ │ └── mcux_ccm_on 4 0.01%
│ ├── console 88 0.12%
│ │ └── uart_console.c 88 0.12%
│ │ ├── __init_uart_console_init 8 0.01%
│ │ ├── console_out 40 0.05%
│ │ └── uart_console_init 40 0.05%
│ ├── gpio 6124 8.12%
│ │ └── gpio_mcux_igpio.c 6124 8.12%
│ │ ├── __devstate_dts_ord_124 2 0.00%
│ │ ├── __devstate_dts_ord_157 2 0.00%
│ │ ├── __devstate_dts_ord_190 2 0.00%
通过对百分比可以找到哪些模块/符号占用空间大,再进行有针对性的优化
puncover 链接到标题
puncover是zephyr导入的一个第三方Python工具包,可以通过网页查看ROM,RAM和函数堆栈的使用情况,首先需要安装puncover
pip install git+https://github.com/HBehrens/puncover --user
如果要想在结果中看到函数堆栈的信息,需要在编译的时候开启CONFIG_STACK_USAGE=y
west build -b mm_feather zephyr/samples/subsys/shell/shell_module/ -- -DCONFIG_STACK_USAGE=y
安装完后执行
west build -t puncover
可以看到提示
parsing stack usages starting at /mnt/e/westz/zephyr_v3.4.0/build
- Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
浏览器访问http://127.0.0.1:5000/,通过选择不同的链接可以看到每个函数的stack,code大小,可以看到静态数组的大小
问题处理 链接到标题
执行时会报错
Traceback (most recent call last): File “/home/frank/.local/bin/puncover”, line 8, in sys.exit(main()) File “/home/frank/.local/lib/python3.8/site-packages/puncover/puncover.py”, line 55, in main gcc_tools_base = os.path.join(find_arm_tools_location(), ‘bin/arm-none-eabi-’) File “/usr/lib/python3.8/posixpath.py”, line 76, in join a = os.fspath(a) TypeError: expected str, bytes or os.PathLike object, not NoneType
这是因为puncover依赖arm-none-eabi-objdump,需要将对应的工具链加入到环境变量
export PATH=/mnt/e/westz/gcc-arm-none-eabi-9-2020-q2-update/bin:$PATH
pahole 链接到标题
Poke-a-hole (pahole) 是一种目标文件分析工具,用于分析结构体的大小,以及由于编译器将数据元素与 CPU 字大小对齐而导致的漏洞。Zephyr通过导入dwarves来支持该分析,因此使用前需要先安装dwarves
sudo apt-get install dwarves
使用下面命令进行分析
west build -t pahole
可以得到类似下面的log
/* Used at: /mnt/e/westz/zephyr_v3.4.0/modules/hal/nxp/mcux/mcux-sdk/devices/MIMXRT1062/drivers/fsl_clock.c */
/* <7b728> /mnt/e/westz/zephyr_v3.4.0/modules/hal/nxp/mcux/mcux-sdk/devices/MIMXRT1062/drivers/fsl_clock.h:1261 */
struct _clock_video_pll_config {
uint8_t loopDivider; /* 0 1 */
uint8_t postDivider; /* 1 1 */
/* XXX 2 bytes hole, try to pack */
uint32_t numerator; /* 4 4 */
uint32_t denominator; /* 8 4 */
uint8_t src; /* 12 1 */
/* size: 16, cachelines: 1, members: 5 */
/* sum members: 11, holes: 1, sum holes: 2 */
/* padding: 3 */
/* last cacheline: 16 bytes */
};
含义说明
uint8_t loopDivider; /* 0 1 */
loopDivider
在结构体struct _clock_video_pll_config
中的偏移地址为0,占用内存为1字节
/* XXX 2 bytes hole, try to pack */
此处有2个字节的空洞
/* size: 16, cachelines: 1, members: 5 */
结构体总计占用16个字节,占用一行cacheline,有5个成员
/* sum members: 11, holes: 1, sum holes: 2 */
结构体成员总计使用11个字节,有一个空洞,空洞总计占用2个字节
/* padding: 3 */
结构体对齐需要padding 3个字节
/* last cacheline: 16 bytes */
最后一行cacheline 使用了16个字节
参考 链接到标题
https://docs.zephyrproject.org/3.4.0/develop/optimizations/tools.html