[Arduino] View code size and assembly code
While developing ArduinoJson, I’ve always been obsessed with code size. Arduinos have such a small amount of Flash (32KB for a Duemilanove) that every byte is important.
Here are two techniques that I use. I tested them on Windows for the AVR platform, they can probably be adapted to other platforms.
We’ll use two tools from the AVR tool chain: avr-nm
and avr-objdump
, they are both provided along with the Arduino IDE.
Finding the .elf file
So let’s assume that you wrote an Arduino sketch, and you want to optimize the size.
Once you compiled it, the first thing you need to do is find the compiled binary file.
The easiest way is to “Enable verbose output during compilation” and look for the path to the .elf
file at the end of the log.
View code size with avr-nm
When optimizing the size of a program, it’s very important to focus your effort on the right place.
Using avr-nm
, you’ll know the size of each function.
Open a command prompt and type:
set PATH=%PATH%;C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\
avr-nm --size-sort -C -r "<path-to-the-elf-file>"
The size of all functions will be printed:
00000094 T __vector_16
00000076 T init
00000072 T pinMode
0000006c T digitalWrite
00000052 t turnOffPWM
00000050 T delay
00000046 T micros
00000028 T loop
0000001e T main
00000014 T digital_pin_to_timer_PGM
00000014 T digital_pin_to_port_PGM
00000014 T digital_pin_to_bit_mask_PGM
00000010 T __do_clear_bss
0000000a T port_to_output_PGM
0000000a T port_to_mode_PGM
00000008 T setup
00000004 B timer0_overflow_count
00000004 B timer0_millis
00000002 W yield
00000002 W initVariant
00000002 t __empty
00000001 b timer0_fract
Functions are printed with the biggest on top of the list and the size is shown in hexadecimal.
View assembly with avr-objdump
When avr-nm
is not enough, you have no choice but use the biggest weapon: the disassembler.
With avr-objdump
you can extract the assembly code from the .elf
file.
In a command prompt, type:
set PATH=%PATH%;C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\
avr-objdump -C -d "<path-to-the-elf-file>"
The assembly code of the whole program will be printed.
Here it what it shows for the loop()
function of the Blink example:
000000e8 <loop>:
e8: 61 e0 ldi r22, 0x01 ; 1
ea: 8d e0 ldi r24, 0x0D ; 13
ec: 0e 94 ba 01 call 0x374 ; 0x374 <digitalWrite>
f0: 68 ee ldi r22, 0xE8 ; 232
f2: 73 e0 ldi r23, 0x03 ; 3
f4: 80 e0 ldi r24, 0x00 ; 0
f6: 90 e0 ldi r25, 0x00 ; 0
f8: 0e 94 f5 00 call 0x1ea ; 0x1ea <delay>
fc: 60 e0 ldi r22, 0x00 ; 0
fe: 8d e0 ldi r24, 0x0D ; 13
100: 0e 94 ba 01 call 0x374 ; 0x374 <digitalWrite>
104: 68 ee ldi r22, 0xE8 ; 232
106: 73 e0 ldi r23, 0x03 ; 3
108: 80 e0 ldi r24, 0x00 ; 0
10a: 90 e0 ldi r25, 0x00 ; 0
10c: 0c 94 f5 00 jmp 0x1ea ; 0x1ea <delay>
We can see the calls to digitalWrite()
and delay()
from the Blink example.
EDIT: as a redditor pointed out, Arduino-Makefile provides commands like make symbol_sizes
and make generate_assembly
for that.