Why is my Arduino Profiler Broken?

Or: How do I get a mixed assembly/C listing for my Arduino code?

William F. Dudley
2014 January 5

In writing my article on my Arduino Profiler, I discovered that my simple example was "broken". The manifestation of "broken" was that the profiler wasn't counting cpu cycles spent in big_cpu_fn_1() and big_cpu_fn_2().

Further fiddling about and I discovered that putting a Serial.print() after each call to big_cpu_fn_1() (or its sister) would cause the resumption of counting cycles spent in those functions.

Totally mystified, I decided I really needed to see the assembler code that was output by the compiler, for clearly something special was happening and had no idea what it was.

An hour of googling later, I finally figured out the trick to getting not only an assember listing, but an assembler listing with the original lines of the C program shown as comments in the assember, so you can figure out where you are in the code.

First you have to turn on "verbose compilation". In the Arduino IDE, go to Files −> Preferences, check the box "show verbose output during compilation". Now close preferences. Click on the verify button. You'll see a bunch of stuff scroll by. Scroll back to the beginnning and find where it compiles the module you need to see the listing for. Here's the compilation line for my profiling_example.ino: (Note: I'm using Linux. Your line will look similar but different. Deal with it, you're a programmer.) (I added '\' and line breaks so the page would render nicely. In reality it's all one long line.)

/home/dud/embedded/arduino-1.5.4/hardware/tools/avr/bin/avr-g++ -c -g -Os -w
-fno-exceptions -ffunction-sections -fdata-sections -MMD -mmcu=atmega2560 \
-DF_CPU=16000000L -DARDUINO=154 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR \
-I/home/dud/embedded/arduino-1.5.4/hardware/arduino/avr/cores/arduino \
-I/home/dud/embedded/arduino-1.5.4/hardware/arduino/avr/variants/mega \
/tmp/build311524121236855863.tmp/profiling_example.cpp \
-o /tmp/build311524121236855863.tmp/profiling_example.cpp.o

Copy and paste that line into a file that will be a shell script (batch file for some of you.) Edit the file, changing -g to
-gstabs -Wa,-ahlmsd=/tmp/build311524121236855863.tmp/profiling_example.lst as in the example below: (Adapt the directory path and file name for the lst file to fit with your own specific situation.)

/home/dud/embedded/arduino-1.5.4/hardware/tools/avr/bin/avr-g++ -c -gstabs \
-Wa,-ahlmsd=/tmp/build311524121236855863.tmp/profiling_example.lst -Os -w \
-fno-exceptions -ffunction-sections -fdata-sections -MMD -mmcu=atmega2560 \
-DF_CPU=16000000L -DARDUINO=154 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR \
-I/home/dud/embedded/arduino-1.5.4/hardware/arduino/avr/cores/arduino \
-I/home/dud/embedded/arduino-1.5.4/hardware/arduino/avr/variants/mega \
/tmp/build311524121236855863.tmp/profiling_example.cpp \
-o /tmp/build311524121236855863.tmp/profiling_example.cpp.o

Now run that shell script (batch file). The result file is (in my case), /tmp/build311524121236855863.tmp/profiling_example.lst

Now comes the bit where I had to think. I generated listing files for the code with and without the Serial.print() invocations, and then ran the two different versions through diff. Eventually I came to the conclusion that avr-g++ was putting my big_cpu_fn_1() and big_cpu_fn_2() inline. Then it was optimizing away the PF(2) and PF(3) that were in those functions, leaving me with no profiling anymore.

Lots more googling and I discovered that I could force the compiler to NOT inline my functions, by using __attribute__ ((noinline)) in the function definitions of big_cpu_fn_1() and big_cpu_fn_2(). Once I did that, the profiler example code worked perfectly.

William F. Dudley
2014 January 5

001937 Views