Reputation: 4313
I'm developing an application on an STM32F042. I drive everything from a makefile, including my unit tests. I use OpenOCD and ST-LINK to flash the target. My unit tests run on the host and on the target. The host unit test driver returns 0 from main() on success and non-zero on failure, so the makefile knows if the tests pass. The makefile flashes and starts tests on the target, but doesn't know if they succeed or fail. The embedded test application turns on a red LED for fail and green for pass, so I know--now I want to automate this step.
I want to set two breakpoints in the code, one in the failure handler and one at the end of main, and tell OpenOCD to exit with zero or non-zero status if it hits one or the other breakpoint.
So my question boils down to two specific ones:
To set a breakpoint, I need to know the PC value at a specific line of code. How do I get that value from the arm-gcc toolchain?
Can I configure OpenOCD to exit on specific breakpoints, and with specific status?
Upvotes: 3
Views: 1113
Reputation: 4313
Here's what I ended up with. For each target unit test, I start an OpenOCD server and connect to it with gdb. Gdb runs a script that sets two breakpoints, one for success, one for failure. If it hits either breakpoint, it shuts down the OCD server and exits with a code that communicates success and failure to the shell. To run the same tests on the host, I simply compile them as regular executables.
# target unit test binaries
foo_tests.elf bar_tests.elf baz_tests.elf bop_tests.elf: unit_test_runner.ao
# disable optimization for target unit test driver to avoid optimizing
# away functions that serve as breakpoint labels
unit_test_runner.ao: CFLAGS += -O0 -g
# link target unit test binaries for semihosting
%_tests.elf: ARM_LDLIBS += -specs=rdimon.specs -lrdimon
# host unit test binaries
foo_tests bar_time_tests baz_tests bop_tests: unit_test_runner.o
# run target unit test binaries through gdb and OpenOCD; redirecting stderr
# leaves printf output from `assert()' clearly visible on the console
%.tut: %.elf
openocd -f interface/stlink-v2-1.cfg -f target/stm32f0x.cfg 2> [email protected] &
gdb-multiarc -batch-silent -x tut.gdb $< 2> [email protected]
# run host binary
%.run: %
./$*
tests: foo_tests.run bar_time_tests.run baz_tests.run bop_tests.run \
foo_tests.tut bar_time_tests.tut baz_tests.tut bop_tests.tut
target remote localhost:3333
monitor arm semihosting enable # let assert()'s printf() through
monitor reset halt
load
monitor reset init
break success # set breakpoint on function `sucess()'
commands # on hitting this bp, execute the following:
monitor shutdown # shutdown OpenOCD server
quit 0 # exit GDB with success code
end
break failure # set breakpoint on function `sucess()'
commands
monitor shutdown
quit 1 # exit GDB with failure code
end
continue
#include <stdlib.h>
/* These two functions serve as labels where gdb can place
breakpoints. */
void success() {}
void failure() {}
/* Implementation detail for `assert()' macro */
void assertion_failure(const char *file,
int line,
const char *function,
const char *expression)
{
printf("assertion failure in %s:%d (%s): `%s'\n",
file, line, function, expression);
failure();
exit(1);
}
/* This function is necessary for ARM semihosting */
extern void initialise_monitor_handles(void);
int main(int argc, char* argv[])
{
#ifdef __arm__
initialise_monitor_handles();
#endif
tests(); /* client code implements this function */
success();
return 0;
}
Upvotes: 3