Skip to content

Commit

Permalink
Enable GCOV code coverage in samples
Browse files Browse the repository at this point in the history
The samples now create two output files for each sample. A normal one
as it did before and another with a _gcov.bin suffix. This latter one
will record code coverage data while running under the simulator and
write out the results just before it exits.  This takes advantage of
the newlib semihosting in pinkySim to allow the gcov instrumentation
to open files on the host and write out the code coverage results.

The run-gcov.sh bash script can be executed (tested on OS X only) to
run arm-none-eabi-gcov over the results and extract summary and
detailed code coverage results for a particular sample.  The sample
must be manually executed under the simulator by the user before
running this script to ascertain the code coverage from that run.
  • Loading branch information
adamgreen committed May 25, 2014
1 parent ef108fa commit dc5b908
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*_tests.exe
*_tests_gcov.exe
*_tests.txt
*_output.txt
*.o
*.d
*.a
Expand Down
75 changes: 62 additions & 13 deletions samples/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ ARMV6M_ASFLAGS := -mcpu=cortex-m0 -mthumb -x assembler-with-cpp -MMD -MP
ARMV6M_DISASMFLAGS := -d -f -M reg-names-std --demangle

# Output directories for intermediate object files.
ARMV6M_OUTPUT := armv6m
GCOV_OUTPUT := gcov
OBJDIR := obj
ARMV6M_OBJDIR := $(OBJDIR)
ARMV6M_OBJDIR := $(ARMV6M_OUTPUT)/$(OBJDIR)
GCOV_OBJDIR := $(GCOV_OUTPUT)/$(OBJDIR)

# Output directories for final libraries.
LIBDIR := lib
ARMV6M_LIBDIR := $(LIBDIR)
ARMV6M_LIBDIR := $(ARMV6M_OUTPUT)/$(LIBDIR)
GCOV_LIBDIR := $(GCOV_OUTPUT)/$(LIBDIR)

# Start out with empty pre-req lists. Add modules as we go.
ALL_TARGETS :=
Expand All @@ -77,7 +81,9 @@ DEPS :=
# Useful macros.
objs = $(addprefix $2/,$(addsuffix .o,$(basename $(wildcard $1/*.c $1/*.cpp $1/*.S))))
armv6m_objs = $(call objs,$1,$(ARMV6M_OBJDIR))
add_deps = $(patsubst %.o,%.d,$(ARMV6M_$1_OBJ))
gcov_objs = $(call objs,$1,$(GCOV_OBJDIR))
obj_to_gcda = $(patsubst %.o,%.gcda,$1)
add_deps = $(patsubst %.o,%.d,$(ARMV6M_$1_OBJ) $(GCOV_$1_OBJ))
includes = $(patsubst %,-I%,$1)
define build_lib
@echo Building $@
Expand All @@ -89,6 +95,11 @@ define link_exe
$Q $(MAKEDIR)
$Q $($1_LD) $($1_LDFLAGS) $^ -o $@
endef
define link_gcov_exe
@echo Building $@
$Q $(MAKEDIR)
$Q $($1_LD) $($1_LDFLAGS) $^ -lgcov -o $@
endef
define disasm_exe
@echo Extracting disassembly to $@
$Q $(MAKEDIR)
Expand All @@ -102,26 +113,42 @@ endef
define make_library # ,LIBRARY,src_dirs,libname.a,includes
ARMV6M_$1_OBJ := $(foreach i,$2,$(call armv6m_objs,$i))
ARMV6M_$1_LIB := $(ARMV6M_LIBDIR)/$3
GCOV_$1_OBJ := $(foreach i,$2,$(call gcov_objs,$i))
GCOV_$1_LIB := $(GCOV_LIBDIR)/$3
DEPS += $$(call add_deps,$1)
ALL_TARGETS += $$(ARMV6M_$1_LIB)
ALL_TARGETS += $$(ARMV6M_$1_LIB) $$(GCOV_$1_LIB)
$$(ARMV6M_$1_LIB) : INCLUDES := $4
$$(GCOV_$1_LIB) : INCLUDES := $4
$$(ARMV6M_$1_LIB) : $$(ARMV6M_$1_OBJ)
$$(call build_lib,ARMV6M)
$$(GCOV_$1_LIB) : $$(GCOV_$1_OBJ)
$$(call build_lib,ARMV6M)
endef
define make_sample # ,APP2BUILD,app_src_dirs,includes,other_libs
ARMV6M_$1_APP_OBJ := $(foreach i,$2,$(call armv6m_objs,$i))
ARMV6M_$1_APP_ELF := $(ARMV6M_OBJDIR)/$1.elf
ARMV6M_$1_APP_DISASM := $(ARMV6M_OBJDIR)/$1.disasm
ARMV6M_$1_APP_ELF := $(ARMV6M_OUTPUT)/$1.elf
ARMV6M_$1_APP_DISASM := $(ARMV6M_OUTPUT)/$1.disasm
ARMV6M_$1_APP_BIN := $1.bin
GCOV_$1_APP_OBJ := $(foreach i,$2,$(call gcov_objs,$i))
GCOV_$1_APP_ELF := $(GCOV_OUTPUT)/$1.elf
GCOV_$1_APP_DISASM := $(GCOV_OUTPUT)/$1.disasm
GCOV_$1_APP_BIN := $1_gcov.bin
DEPS += $$(call add_deps,$1_APP)
ALL_TARGETS += $$(ARMV6M_$1_APP_DISASM) $$(ARMV6M_$1_APP_BIN)
ALL_TARGETS += $$(ARMV6M_$1_APP_DISASM) $$(ARMV6M_$1_APP_BIN) $$(GCOV_$1_APP_DISASM) $$(GCOV_$1_APP_BIN)
$$(ARMV6M_$1_APP_ELF) : INCLUDES := $3
$$(ARMV6M_$1_APP_ELF) : $$(ARMV6M_$1_APP_OBJ) $4
$$(GCOV_$1_APP_ELF) : INCLUDES := $3
$$(ARMV6M_$1_APP_ELF) : $$(ARMV6M_$1_APP_OBJ) $(addprefix $(ARMV6M_LIBDIR)/,libstartup.a $4)
$$(call link_exe,ARMV6M)
$$(ARMV6M_$1_APP_DISASM) : $$(ARMV6M_$1_APP_ELF)
$$(call disasm_exe,ARMV6M)
$$(ARMV6M_$1_APP_BIN) : $$(ARMV6M_$1_APP_ELF)
$$(call create_bin,ARMV6M)
$$(GCOV_$1_APP_ELF) : $$(GCOV_$1_APP_OBJ) $(addprefix $(GCOV_LIBDIR)/,libstartup.a $4)
$$(call link_gcov_exe,ARMV6M)
$$(GCOV_$1_APP_DISASM) : $$(GCOV_$1_APP_ELF)
$$(call disasm_exe,ARMV6M)
$$(GCOV_$1_APP_BIN) : $$(GCOV_$1_APP_ELF)
$$(call create_bin,ARMV6M)
endef


Expand All @@ -131,15 +158,15 @@ $(eval $(call make_library,LIBSTARTUP,libstartup,libstartup.a,../include))

#######################################
# FileTest_Sample
$(eval $(call make_sample,FileTest_Sample,FileTest,include,$(ARMV6M_LIBSTARTUP_LIB)))
$(eval $(call make_sample,FileTest_Sample,FileTest,include,))

#######################################
# CommandLine_Sample
$(eval $(call make_sample,CommandLine_Sample,CommandLine,include,$(ARMV6M_LIBSTARTUP_LIB)))
$(eval $(call make_sample,CommandLine_Sample,CommandLine,include,))

#######################################
# StdIO_Sample
$(eval $(call make_sample,StdIO_Sample,StdIO,include,$(ARMV6M_LIBSTARTUP_LIB)))
$(eval $(call make_sample,StdIO_Sample,StdIO,include,))


#######################################
Expand All @@ -149,8 +176,8 @@ all : $(ALL_TARGETS)

clean :
@echo Cleaning samples
$Q $(REMOVE_DIR) $(OBJDIR) $(QUIET)
$Q $(REMOVE_DIR) $(LIBDIR) $(QUIET)
$Q $(REMOVE_DIR) $(ARMV6M_OUTPUT) $(QUIET)
$Q $(REMOVE_DIR) $(GCOV_OUTPUT) $(QUIET)
$Q $(REMOVE) *.bin $(QUIET)


Expand All @@ -160,11 +187,33 @@ $(ARMV6M_OBJDIR)/%.o : %.c
$Q $(MAKEDIR)
$Q $(ARMV6M_GCC) $(ARMV6M_GCCFLAGS) $(call includes,$(INCLUDES)) -c $< -o $@

$(ARMV6M_OBJDIR)/%.o : %.cpp
@echo Compiling $<
$Q $(MAKEDIR)
$Q $(ARMV6M_GPP) $(ARMV6M_GPPFLAGS) $(call includes,$(INCLUDES)) -c $< -o $@

$(ARMV6M_OBJDIR)/%.o : %.S
@echo Assembling $<
$Q $(MAKEDIR)
$Q $(ARMV6M_AS) $(ARMV6M_ASFLAGS) $(call includes,$(INCLUDES)) -c $< -o $@

$(GCOV_OBJDIR)/%.o : %.c
@echo Compiling $<
$Q $(MAKEDIR)
$Q $(REMOVE) $(call obj_to_gcda,$@) $(QUIET)
$Q $(ARMV6M_GCC) $(ARMV6M_GCCFLAGS) -fprofile-arcs -ftest-coverage $(call includes,$(INCLUDES)) -c $< -o $@

$(GCOV_OBJDIR)/%.o : %.cpp
@echo Compiling $<
$Q $(MAKEDIR)
$Q $(REMOVE) $(call obj_to_gcda,$@) $(QUIET)
$Q $(ARMV6M_GPP) $(ARMV6M_GPPFLAGS) -fprofile-arcs -ftest-coverage $(call includes,$(INCLUDES)) -c $< -o $@

$(GCOV_OBJDIR)/%.o : %.S
@echo Assembling $<
$Q $(MAKEDIR)
$Q $(ARMV6M_AS) $(ARMV6M_ASFLAGS) $(call includes,$(INCLUDES)) -c $< -o $@


# *** Pull in header dependencies if not performing a clean build. ***
ifneq "$(findstring clean,$(MAKECMDGOALS))" "clean"
Expand Down
58 changes: 58 additions & 0 deletions samples/run-gcov.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#! /usr/bin/env bash
# Copyright 2014 Adam Green (http://mbed.org/users/AdamGreen/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

displayUsage() {
echo Usage: run-gcov SampleName
echo Example: run-gcov FileTest
exit 1
}

# Check command line parameters.
if [ "$1" == "" ] ; then
displayUsage
fi

ARM_GCOV=arm-none-eabi-gcov
SAMPLE_NAME=$1
TEXT_OUT=${SAMPLE_NAME}_output.txt
GCOV_RESULTS=gcov/$SAMPLE_NAME
SAMPLE_OBJDIR=gcov/obj/$SAMPLE_NAME
LIBSTARTUP_OBJDIR=gcov/obj/libstartup

# Find sample and libstartup object files.
pushd $SAMPLE_OBJDIR >/dev/null 2>&1
SAMPLE_OBJECTS=`ls *.o`
popd >/dev/null 2>&1
pushd $LIBSTARTUP_OBJDIR >/dev/null 2>&1
LIBSTARTUP_OBJECTS=`ls *.o`
popd >/dev/null 2>&1

rm $TEXT_OUT >/dev/null 2>&1
mkdir -p $GCOV_RESULTS >/dev/null 2>&1

# Iterate through each object and extract code coverage results.
for obj in $SAMPLE_OBJECTS ; do
$ARM_GCOV --object-directory=$SAMPLE_OBJDIR $obj >>$TEXT_OUT
done
for obj in $LIBSTARTUP_OBJECTS ; do
$ARM_GCOV --object-directory=$LIBSTARTUP_OBJDIR $obj >>$TEXT_OUT
done

# Get final output.
mv $TEXT_OUT $GCOV_RESULTS
mv *.gcov $GCOV_RESULTS
../mri/CppUTest/scripts/filterGcov.sh $GCOV_RESULTS/$TEXT_OUT /dev/null $GCOV_RESULTS/$TEXT_OUT
cat $GCOV_RESULTS/$TEXT_OUT
echo Detailed code coverage results can be found in $GCOV_RESULTS

0 comments on commit dc5b908

Please sign in to comment.