summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Williams <iawillia@us.ibm.com>2012-01-10 18:10:40 -0600
committerA. Patrick Williams III <iawillia@us.ibm.com>2012-02-07 15:46:33 -0600
commite457b5df08897236374bd8851d8169188e9dc1f8 (patch)
treea9c7392ea093ace963796340eb7da3074b743444
parent859335d953a59c25de64a414c344d0a22d0911cb (diff)
downloadtalos-hostboot-e457b5df08897236374bd8851d8169188e9dc1f8.tar.gz
talos-hostboot-e457b5df08897236374bd8851d8169188e9dc1f8.zip
GCOV support
Change-Id: I73a446754cd03178055459eb75c7b2f87b51b0f3 Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/635 Tested-by: Jenkins Server Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
-rw-r--r--.gitignore3
-rw-r--r--config.mk56
-rw-r--r--makefile30
-rw-r--r--obj/.gitignore1
-rw-r--r--src/build/debug/Hostboot/Gcov.pm375
-rwxr-xr-xsrc/build/debug/Hostboot/_DebugFramework.pm14
-rw-r--r--src/include/kernel/syscalls.H1
-rw-r--r--src/include/usr/gcov.h102
-rw-r--r--src/kernel/start.S11
-rw-r--r--src/kernel/taskmgr.C16
-rw-r--r--src/makefile6
-rw-r--r--src/sys/makefile2
-rw-r--r--src/sys/prof/gcov.C24
-rw-r--r--src/sys/prof/idletask.C41
-rw-r--r--src/sys/prof/makefile39
15 files changed, 699 insertions, 22 deletions
diff --git a/.gitignore b/.gitignore
index ebb565cd6..274911ced 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@
*.list
*.dep
*.o.hash
+*.gcno
+*.gcda
+*.gcov
*.swp
customrc
*.CKP
diff --git a/config.mk b/config.mk
index 7f1a5fb47..9c518b290 100644
--- a/config.mk
+++ b/config.mk
@@ -34,12 +34,16 @@ OBJDIR = ${ROOTPATH}/obj/modules/${MODULE}
BEAMDIR = ${ROOTPATH}/obj/beam/${MODULE}
GENDIR = ${ROOTPATH}/obj/genfiles
IMGDIR = ${ROOTPATH}/img
-
+GCOVNAME = ${MODULE}.lcov
EXTRACOMMONFLAGS += -fPIC -Bsymbolic -Bsymbolic-functions
ifdef STRICT
EXTRACOMMONFLAGS += -Weffc++
endif
CUSTOMFLAGS += -D__HOSTBOOT_MODULE=${MODULE}
+ifdef HOSTBOOT_PROFILE
+vpath %.C ${ROOTPATH}/src/sys/prof
+OBJS += gcov.o
+endif
LIBS += $(addsuffix .so, $(addprefix lib, ${MODULE}))
MODULE_INIT = ${ROOTPATH}/obj/core/module_init.o
EXTRAINCDIR += ${ROOTPATH}/src/include/usr ${GENDIR}
@@ -49,7 +53,9 @@ BEAMDIR = ${ROOTPATH}/obj/beam/core
GENDIR = ${ROOTPATH}/obj/genfiles
IMGDIR = ${ROOTPATH}/img
EXTRAINCDIR += ${GENDIR}
+GCOVNAME = $(notdir $(shell pwd)).lcov
endif
+GCOVDIR = ${ROOTPATH}/obj/gcov
__internal__comma= ,
__internal__empty=
@@ -72,6 +78,10 @@ endif
endif
endif
+ifdef HOSTBOOT_PROFILE
+CUSTOMFLAGS += --coverage
+endif
+
TRACEPP = ${ROOTPATH}/src/build/trace/tracepp
CUSTOM_LINKER_EXE = ${ROOTPATH}/src/build/linker/linker
CUSTOM_LINKER = i686-mcp6-jail ${CUSTOM_LINKER_EXE}
@@ -83,6 +93,7 @@ CXX = ${TRACEPP} ${CXX_RAW}
LD = ppc64-mcp6-ld
OBJDUMP = ppc64-mcp6-objdump
+GCOV = ppc64-mcp6-gcov
APYFIPSHDR = apyfipshdr
APYRUHHDR = apyruhhdr
@@ -103,6 +114,16 @@ ASMFLAGS = ${COMMONFLAGS} -mcpu=power7
CXXFLAGS = ${CFLAGS} -nostdinc++ -fno-rtti -fno-exceptions -Wall
LDFLAGS = --nostdlib --sort-common ${COMMONFLAGS}
+ifdef HOSTBOOT_PROFILE
+ PROFILE_FLAGS_FILTER = $(if $(findstring gcov,$(2)),\
+ $(filter-out --coverage,$(1)),\
+ $(1))
+else
+ PROFILE_FLAGS_FILTER = $(1)
+endif
+
+FLAGS_FILTER = $(call PROFILE_FLAGS_FILTER, $(1), $(2))
+
ifdef USE_PYTHON
TESTGEN = ${ROOTPATH}/src/usr/cxxtest/cxxtestgen.py
else
@@ -142,7 +163,8 @@ endif
${OBJDIR}/%.o ${OBJDIR}/%.list : %.C
mkdir -p ${OBJDIR}
- ${CXX} -c ${CXXFLAGS} $< -o $@ ${INCFLAGS} -iquote .
+ ${CXX} -c $(call FLAGS_FILTER, ${CXXFLAGS}, $<) $< \
+ -o $@ ${INCFLAGS} -iquote .
${OBJDUMP} -dCS $@ > $(basename $@).list
# Compiling *.cc files
@@ -156,9 +178,11 @@ ${OBJDIR}/%.o ${OBJDIR}/%.list : %.c
# Override to use C++ compiler in place of C compiler
# CC_OVERRIDE is set in the makefile of the component
ifndef CC_OVERRIDE
- ${CC} -c ${CFLAGS} $< -o $@ ${INCFLAGS} -iquote .
+ ${CC} -c $(call FLAGS_FILTER, ${CFLAGS}, $<) $< \
+ -o $@ ${INCFLAGS} -iquote .
else
- ${CXX} -c ${CXXFLAGS} $< -o $@ ${INCFLAGS} -iquote .
+ ${CXX} -c $(call FLAGS_FILTER, ${CXXFLAGS}, $<) $< \
+ -o $@ ${INCFLAGS} -iquote .
endif
${OBJDUMP} -dCS $@ > $(basename $@).list
@@ -169,7 +193,8 @@ ${OBJDIR}/%.o : %.S
${OBJDIR}/%.dep : %.C
mkdir -p ${OBJDIR}; \
rm -f $@; \
- ${CXX_RAW} -M ${CXXFLAGS} $< -o $@.$$$$ ${INCFLAGS} -iquote .; \
+ ${CXX_RAW} -M $(call FLAGS_FILTER, ${CXXFLAGS}, $<) $< \
+ -o $@.$$$$ ${INCFLAGS} -iquote .; \
sed 's,\($*\)\.o[ :]*,${OBJDIR}/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
@@ -183,7 +208,8 @@ ${OBJDIR}/%.dep : %.cc
${OBJDIR}/%.dep : %.c
mkdir -p ${OBJDIR}; \
rm -f $@; \
- ${CC_RAW} -M ${CFLAGS} $< -o $@.$$$$ ${INCFLAGS} -iquote .; \
+ ${CC_RAW} -M $(call FLAGS_FILTER, ${CFLAGS}, $<) $< \
+ -o $@.$$$$ ${INCFLAGS} -iquote .; \
sed 's,\($*\)\.o[ :]*,${OBJDIR}/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
@@ -257,6 +283,9 @@ ${IMGDIR}/hbotStringFile : ${IMAGES}
%.gen_pass:
cd ${basename $@} && ${MAKE} gen_pass
+%.gcov_pass:
+ cd ${basename $@} && ${MAKE} gcov_pass -ik
+
%.clean:
cd ${basename $@} && ${MAKE} clean
@@ -298,12 +327,25 @@ ${BEAMDIR}/%.beam : %.S
BEAMOBJS = $(addprefix ${BEAMDIR}/, ${OBJS:.o=.beam})
beam: ${SUBDIRS:.d=.beamdir} ${BEAMOBJS}
+gcov_pass:
+ mkdir -p ${GCOVDIR}
+ ${MAKE} GCOV_PASS
+
+GCOV_PASS: ${SUBDIRS:.d=.gcov_pass}
+ifdef OBJS
+ cp ${OBJECTS:.o=.gcno} ${OBJECTS:.o=.gcda} .
+ lcov --directory . -c -o ${GCOVDIR}/${GCOVNAME} \
+ --gcov-tool ${GCOV} --ignore-errors source
+ rm ${OBJS:.o=.gcno} ${OBJS:.o=.gcda} -f
+endif
+
cleanud :
rm -f ${UD_OBJS}
clean: cleanud ${SUBDIRS:.d=.clean}
(rm -f ${OBJECTS} ${OBJECTS:.o=.dep} ${OBJECTS:.o=.list} \
- ${OBJECTS:.o=.o.hash} ${BEAMOBJS} ${LIBRARIES} \
+ ${OBJECTS:.o=.o.hash} ${OBJECTS:.o=.gcno} ${OBJECTS:.o=.gcda} \
+ ${BEAMOBJS} ${LIBRARIES} \
${IMAGES} ${IMAGES:.bin=.list} ${IMAGES:.bin=.syms} \
${IMAGES:.bin=.bin.modinfo} ${IMAGES:.ruhx=.lid} \
${IMAGES:.ruhx=.lidhdr} ${IMAGES:.bin=_extended.bin} \
diff --git a/makefile b/makefile
index 1294562ef..25fbfc25c 100644
--- a/makefile
+++ b/makefile
@@ -1,3 +1,25 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: makefile $
+#
+# IBM CONFIDENTIAL
+#
+# COPYRIGHT International Business Machines Corp. 2010 - 2012
+#
+# p1
+#
+# Object Code Only (OCO) source materials
+# Licensed Internal Code Source Materials
+# IBM HostBoot Licensed Internal Code
+#
+# The source code for this program is not published or other-
+# wise divested of its trade secrets, irrespective of what has
+# been deposited with the U.S. Copyright Office.
+#
+# Origin: 30
+#
+# IBM_PROLOG_END
SUBDIRS = src.d
ROOTPATH = .
@@ -11,3 +33,11 @@ docs: src/build/doxygen/doxygen.conf
citest:
src/build/citest/cxxtest-start.sh
+
+gcov:
+ rm -rf obj/gcov/*
+ make gcov_pass
+ find obj/gcov/ -size 0c | xargs rm # Delete empty files.
+ genhtml obj/gcov/*.lcov -o obj/gcov/html
+ echo "View GCOV results with: firefox obj/gcov/html/index.html"
+
diff --git a/obj/.gitignore b/obj/.gitignore
index 925110f4e..e42a99290 100644
--- a/obj/.gitignore
+++ b/obj/.gitignore
@@ -1 +1,2 @@
*.C
+gcov/*
diff --git a/src/build/debug/Hostboot/Gcov.pm b/src/build/debug/Hostboot/Gcov.pm
new file mode 100644
index 000000000..2ebcc3bb1
--- /dev/null
+++ b/src/build/debug/Hostboot/Gcov.pm
@@ -0,0 +1,375 @@
+#!/usr/bin/perl
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/build/debug/Hostboot/Gcov.pm $
+#
+# IBM CONFIDENTIAL
+#
+# COPYRIGHT International Business Machines Corp. 2012
+#
+# p1
+#
+# Object Code Only (OCO) source materials
+# Licensed Internal Code Source Materials
+# IBM HostBoot Licensed Internal Code
+#
+# The source code for this program is not published or other-
+# wise divested of its trade secrets, irrespective of what has
+# been deposited with the U.S. Copyright Office.
+#
+# Origin: 30
+#
+# IBM_PROLOG_END
+
+use strict;
+use File::Path;
+use File::Basename;
+
+package Hostboot::Gcov;
+use Exporter;
+our @EXPORT_OK = ('main');
+
+# NOTE:
+#
+# Neither the in-memory structures or the resulting file format is well
+# documented for GCOV. I was able to piece together enough of this to
+# make it work for our purposes by looking at gcov-io.h and libgcov.c
+# from the gcc source and gcov/gcc_3_4.c from the linux source.
+#
+# Since this is a Perl script only used internally for debug, I do not see
+# any risk for contamination. If we decided to give Hostboot to external
+# vendors than this Perl script would be distributed as source which should
+# not lead us into any issues.
+#
+# If you are personally concerned about contamination by reading this
+# code you are hereby warned of the potential. Proceed at your own choice.
+
+use constant GCOV_EXTENDED_IMAGE_ADDRESS => (1024 * 1024 * 1024);
+use constant GCOV_INFO_HEAD_SYMBOLNAME => "_gcov_info_head";
+
+use constant GCOV_INFO_VERSION_OFFSET => 0;
+use constant GCOV_INFO_NEXT_OFFSET => GCOV_INFO_VERSION_OFFSET + 8;
+use constant GCOV_INFO_TIMESTAMP_OFFSET => GCOV_INFO_NEXT_OFFSET + 8;
+use constant GCOV_INFO_FILENAME_OFFSET => GCOV_INFO_TIMESTAMP_OFFSET + 8;
+use constant GCOV_INFO_NFUNCTIONS_OFFSET => GCOV_INFO_FILENAME_OFFSET + 8;
+use constant GCOV_INFO_FUNCTIONS_OFFSET => GCOV_INFO_NFUNCTIONS_OFFSET + 8;
+use constant GCOV_INFO_CTRMASK_OFFSET => GCOV_INFO_FUNCTIONS_OFFSET + 8;
+use constant GCOV_INFO_COUNTS_OFFSET => GCOV_INFO_CTRMASK_OFFSET + 8;
+
+use constant GCOV_FNINFO_IDENT_OFFSET => 0;
+use constant GCOV_FNINFO_CHECKSUM_OFFSET => GCOV_FNINFO_IDENT_OFFSET + 4;
+use constant GCOV_FNINFO_NCTRS_OFFSET => GCOV_FNINFO_CHECKSUM_OFFSET + 4;
+
+use constant GCOV_CTRINFO_COUNT_OFFSET => 0;
+use constant GCOV_CTRINFO_VALUEPTR_OFFSET => GCOV_CTRINFO_COUNT_OFFSET + 8;
+
+use constant GCOV_GCDA_MAGIC_VALUE => 0x67636461;
+use constant GCOV_FUNCTION_TAG => 0x01000000;
+use constant GCOV_COUNTERS_TAG => 0x01a10000;
+
+# In memory format:
+# GCC creates a 'gcov_info' structure for each .o file. The info
+# structure has a next pointer to form a chain. In Hostboot we have
+# organized the chains so that the pointer to the first entry is
+# stored at [modulename]_gcov_info_head (where modulename = "core" for
+# kernel and basic system libraries).
+#
+# The gcov_info has a version id (for the gcc compiled with), a
+# compile timestamp, c-string with the name of the .gcda file to be
+# generated, a count of the number of functions in the object, a
+# pointer to a set of function descriptors, a "counter mask" and a
+# set of counter descriptors.
+#
+# GCOV supports multiple types of counters. The only one we are
+# interested in is the "ARCS" counter, which describes the number of
+# times a particular branch is executed. The other counters are for,
+# for instance, profiling the value of a particular variable. The
+# "counter mask" specifies which counters are instrumented, which
+# determines the size of some of the array structures, but we only
+# parse the ARCS-counter type (we do properly calculate sizes if
+# needed).
+#
+# Each function descriptor contains an identity number and checksum
+# pair so the processing tools can match data to code information.
+# Also the function descriptor has an "n_counters" array which
+# determines for each counter type how many counters are instrumented.
+# Again, we are only concerned with the ARCS counter type.
+#
+# The counter descriptor is a size and pointer to array of counter
+# values. If there were 3 functions in the object each with n_counter
+# values of [3, 5, 2], then the size of the counter descriptor would be
+# 3+5+2 = 10. The values are arranged such that the first function has
+# the first 3 values, second one has the next 5, etc. The relationship
+# between function descriptor / "n_counters" and counter descriptor
+# values was not obvious from reading the gcov-io.h.
+#
+# For more details on these structures search the internet for gcov-io.h
+# or ask the building block team for the source code to the compiler we
+# are currently using. The offsets of all of these structures are all
+# documented in Perl constants above so you should only need this if
+# something breaks.
+#
+# .gcda file format:
+# The gcov tools expect a .gcda (gcov data) file as input, containing the
+# instrumented counter values, to go along with the .gcno (gcov note)
+# file created by the compiler. The format is documented in gcov-io.h
+# as well but was again not obvious to decipher.
+#
+# Here is a distilled file format description. Each entity is an u32.
+#
+# file : magic version stamp {function counts}*
+# function: f_header ident checksum
+# counts: c_header count*
+# count: lo hi
+# f_header: F_TAG(=0x01000000) F_LENGTH(=2)
+# c_header: C_TAG(=0x01a10000) C_LENGTH(=count_length*2)
+#
+# The file has three u32 of header followed by any number of function
+# descriptor and count set pairs. The function descriptor is the
+# identity and checksum of the function. The count set is an array of
+# uint64_ts, containing instrumented counts, for the preceeding function.
+
+# Global of where we want the output to go.
+our $output_dir;
+our $debug_mode;
+BEGIN
+{
+ $debug_mode = 0;
+ $output_dir = "";
+}
+return 1;
+
+sub main
+{
+ # Pick a new output directory based on the time.
+ $output_dir = sprintf "gcov.output.%d/", time;
+ File::Path::mkpath($output_dir);
+
+ # Find all the hostboot modules.
+ my @modules = getModules();
+
+ # Search for the gcov_info object for each module and parse.
+ foreach my $module (@modules)
+ {
+ parseModuleGcov($module);
+ }
+
+ my $pwd = `pwd`;
+ chomp $pwd;
+ ::userDisplay "GCOV output written to: $pwd/$output_dir\n";
+}
+
+sub parseModuleGcov
+{
+ my $module = shift;
+ ::userDisplay "Extracting GCOV info for ".$module."\n";
+
+ # Search for gcov_info chain symbol.
+ my ($gcov_info, $unused) =
+ ::findSymbolAddress($module.GCOV_INFO_HEAD_SYMBOLNAME);
+
+ userDebug("\tFound info at 0x" . (sprintf "%x", $gcov_info) . "\n");
+
+ # TODO: We don't support extended image modules yet because the VMM
+ # debug tools don't exist yet.
+ if ($gcov_info > GCOV_EXTENDED_IMAGE_ADDRESS)
+ {
+ ::userDisplay "\tUnable to parse extended image modules. Skipped.\n";
+ return;
+ }
+
+ # Check that we found the gcov_info chain.
+ if ($gcov_info == 0)
+ {
+ ::userDisplay "\tUnable to find gcov_info chain. Skipped.\n";
+ return;
+ }
+
+ # Parse info chain.
+ parseGcovInfo(::read64($gcov_info));
+}
+
+sub parseGcovInfo
+{
+ my $info_ptr = shift;
+ return if (0 eq $info_ptr);
+
+ my $filename = ::readStr(::read64($info_ptr + GCOV_INFO_FILENAME_OFFSET));
+ userDebug("\tFile = ".$filename."\n");
+
+ my $version = ::read32($info_ptr + GCOV_INFO_VERSION_OFFSET);
+ my $stamp = ::read32($info_ptr + GCOV_INFO_TIMESTAMP_OFFSET);
+
+ my $func_count = ::read32($info_ptr + GCOV_INFO_NFUNCTIONS_OFFSET);
+ userDebug("\tFunction Count = ".$func_count."\n");
+
+ my $funcs = ::read64($info_ptr + GCOV_INFO_FUNCTIONS_OFFSET);
+ userDebug("\tFunc Address = ".(sprintf "%x", $funcs)."\n");
+
+ my $ctrmask = ::read32($info_ptr + GCOV_INFO_CTRMASK_OFFSET);
+ if ($ctrmask % 2) # Check that COUNTER_ARCS is turned on.
+ {
+ # COUNTER_ARCS is on. Create file, find arc-values array,
+ # parse functions.
+
+ my $fd = createGcovFile($filename, $version, $stamp);
+
+ my $arcs_ptr = ::read64($info_ptr + GCOV_INFO_COUNTS_OFFSET +
+ GCOV_CTRINFO_VALUEPTR_OFFSET);
+ parseGcovFuncs($fd, $funcs, $func_count, $ctrmask, $arcs_ptr);
+
+ close $fd;
+ }
+ else
+ {
+ userDebug("COUNTER_ARCS is missing!\n");
+ }
+
+ # Look for next .o in gcov_info chain, parse.
+ my $next = ::read64($info_ptr + GCOV_INFO_NEXT_OFFSET);
+ parseGcovInfo($next);
+}
+
+sub parseGcovFuncs
+{
+ my $fd = shift;
+ my $func_ptr = shift;
+ my $func_count = shift;
+ my $mask = shift;
+ my $val_ptr = shift;
+
+ my $fn_offset = 0;
+
+ # Need to calculate the number of counters based on the bits on in
+ # the 'mask'. This is used to determine the size of the function
+ # descriptor object.
+ my $counters = 0;
+ {
+ my $_mask = $mask;
+
+ while (0 != $_mask)
+ {
+ $counters++;
+ $_mask = ($_mask >> 1);
+ }
+ }
+
+ userDebug("\tCounters = ".$counters."\n");
+
+ # Round up the counter count to the nearest two for alignment of the
+ # function descriptor object.
+ if ($counters % 2)
+ {
+ $counters++;
+ }
+ my $func_size = GCOV_FNINFO_CHECKSUM_OFFSET + 4 * $counters;
+
+ userDebug("\tFunction size = ".$func_size."\n");
+
+ # Iterate through the functions and parse.
+ for(my $function = 0; $function < $func_count; $function++)
+ {
+ my $func_off = ($func_ptr + $func_size * $function);
+ my $ident = ::read32($func_off + GCOV_FNINFO_IDENT_OFFSET);
+ my $chksum = ::read32($func_off + GCOV_FNINFO_CHECKSUM_OFFSET);
+
+ userDebug("Ident = ".(sprintf "%x", $ident)."\n");
+ userDebug("Chksum = ".(sprintf "%x", $chksum)."\n");
+
+ print $fd pack('l', GCOV_FUNCTION_TAG); # Write function tag.
+ print $fd pack('l', 2); # Write size = 2.
+ print $fd pack('l', $ident); # Write ident.
+ print $fd pack('l', $chksum); # Write checksum.
+
+ my $nctr_val = ::read32($func_off + GCOV_FNINFO_NCTRS_OFFSET);
+ userDebug("N-Counters = ".$nctr_val."\n");
+
+ print $fd pack('l', GCOV_COUNTERS_TAG); # Write counter tag.
+ print $fd pack('l', $nctr_val * 2); # Write counter length.
+
+ # Read each counter value, output.
+ for(my $v_idx = 0; $v_idx < $nctr_val; $v_idx++)
+ {
+ my $val = ::read64($val_ptr + 8*($fn_offset + $v_idx));
+ userDebug("\tValue[$v_idx] = ".$val."\n");
+
+ print $fd pack('l', $val & 0xFFFFFFFF); # Write lower word.
+ print $fd pack('l', $val >> 32) ; # Write upper word.
+ }
+
+ # We used up a number of counters, so move the offset forward for
+ # the next function.
+ $fn_offset += $nctr_val;
+ }
+
+}
+
+# The *.gcda filename found in the gcov_info struct is an absolute path to
+# the corresponding .o file (not the .C file). This is of the form:
+# ${HOSTBOOTROOT}/src/usr/module/${ROOTPATH}/obj/${MODULE}/foo.gcda .
+# Since we might not even be running this on the same machine, we need to put
+# the output into the "output_dir" but we need to strip off a lot of stuff.
+# The path is going to have an obj in it somewhere so we key off that
+# as the location for where the output file will go.
+sub createGcovFile
+{
+ my $name = shift;
+ my $version = shift;
+ my $stamp = shift;
+
+ # Change *./../obj/ into obj/, prepend output_dir.
+ $name =~ s/.*\/obj\//obj\//;
+ $name = $output_dir.$name;
+
+ # Make sure everything after 'obj/' exists (create subdirs).
+ my $dir = File::Basename::dirname($name);
+ File::Path::mkpath($dir);
+
+ # Create file.
+ open(my $GCOVFILE, "> $name");
+ binmode($GCOVFILE);
+
+ # Write out header.
+ print $GCOVFILE pack('l', GCOV_GCDA_MAGIC_VALUE);
+ print $GCOVFILE pack('l', $version);
+ print $GCOVFILE pack('l', $stamp);
+
+ return $GCOVFILE;
+}
+
+# Search the module list for each code module (lib*.so). Also add "core"
+# for the kernel instrumentation.
+sub getModules
+{
+ my @modules = ::listModules();
+ my @result = ( "core" );
+
+ foreach my $mod (@modules)
+ {
+ if ($mod =~ m/lib.*\.so$/)
+ {
+ $mod =~ s/lib(.*)\.so/$1/;
+ push @result, $mod;
+ }
+ }
+
+ return @result;
+}
+
+sub userDebug
+{
+ return if (!$debug_mode);
+
+ my $string = shift;
+ ::userDisplay $string;
+}
+
+# Debug tool help info.
+sub helpInfo
+{
+ my %info = (
+ name => "Gcov",
+ intro => [ "Extracts the GCOV information."],
+ );
+}
diff --git a/src/build/debug/Hostboot/_DebugFramework.pm b/src/build/debug/Hostboot/_DebugFramework.pm
index 071174021..ae8ef6573 100755
--- a/src/build/debug/Hostboot/_DebugFramework.pm
+++ b/src/build/debug/Hostboot/_DebugFramework.pm
@@ -46,7 +46,7 @@ our @EXPORT = ( 'callToolModule', 'callToolModuleHelp', 'callToolModuleHelpInfo'
'parseToolOpts', 'determineImagePath',
'findSymbolAddress', 'findSymbolTOCAddress',
'findSymbolByAddress',
- 'findModuleByAddress',
+ 'findModuleByAddress', 'listModules',
'littleendian',
'read64', 'read32', 'read16', 'read8', 'readStr',
'write64', 'write32', 'write16', 'write8'
@@ -396,6 +396,18 @@ sub findModuleByAddress
return $modName;
}
+# @sub listModules
+#
+# Returns a list of all module names.
+#
+# @return list of modules found in the modinfo file.
+sub listModules
+{
+ parseModuleFile();
+
+ return keys %moduleAddress;
+}
+
# @sub littleendian
#
# Utility function to determine if the current machine is little or big
diff --git a/src/include/kernel/syscalls.H b/src/include/kernel/syscalls.H
index e09171596..dbcfaf175 100644
--- a/src/include/kernel/syscalls.H
+++ b/src/include/kernel/syscalls.H
@@ -38,6 +38,7 @@ namespace Systemcalls
* appropriate system call handler.
*
* @note TASK_MIGRATE_TO_MASTER value must be kept in sync with start.S.
+ * @note TASK_END value must be kept in sync with start.S.
*/
enum SysCalls
{
diff --git a/src/include/usr/gcov.h b/src/include/usr/gcov.h
new file mode 100644
index 000000000..dd249e1d5
--- /dev/null
+++ b/src/include/usr/gcov.h
@@ -0,0 +1,102 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/include/usr/gcov.h $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2012
+//
+// p1
+//
+// Object Code Only (OCO) source materials
+// Licensed Internal Code Source Materials
+// IBM HostBoot Licensed Internal Code
+//
+// The source code for this program is not published or other-
+// wise divested of its trade secrets, irrespective of what has
+// been deposited with the U.S. Copyright Office.
+//
+// Origin: 30
+//
+// IBM_PROLOG_END
+#ifndef __USR_GCOV_H
+#define __USR_GCOV_H
+
+/** @file gcov.h
+ * @brief Header file to generate gcov_info chain for each module.
+ *
+ * Each gcov-instrumented module needs to have its own gcov_info chain
+ * and associated linking function. Each .o file will have its own
+ * gcov_info object which is added to the chain when the module is loaded
+ * (static initializers) by calling the __gcov_init function.
+ *
+ * We make the gcov_info chain unique per-module, instead of a single
+ * global chain, so that we don't have bad pointers if a module was
+ * loaded and then unloaded.
+ */
+
+#include <stddef.h>
+
+/** @struct gcov_info
+ * @brief Structure generated by gcc. Do not use.
+ *
+ * This structure is automatically generated and instances of it created by
+ * gcc when the --coverage compile option is used. We don't need to
+ * manipulate this structure from code except to fix up the chains as objects
+ * are added to the chain. The rest of this structure is parsed by the
+ * Gcov.pm debug tool.
+ */
+struct gcov_info
+{
+ unsigned int version; // Purposefully chose 'unsigned int' to match gcc.
+ gcov_info* next;
+ // Really there is more after here in the structure, but this is all
+ // we care about from an in-memory perspective.
+};
+
+// Preprocessor magic to create a variable name based off the module name.
+// GCOV_INFO_OBJ() will create a post-processed name like
+// 'foobar_gcov_info_head' or 'core_gcov_info_head'.
+#ifdef __HOSTBOOT_MODULE
+ #define __GCOV_PREFIX __HOSTBOOT_MODULE
+#else
+ #define __GCOV_PREFIX core
+#endif
+
+#define __GCOV_INFO_OBJ(X,Y) X ## Y
+#define _GCOV_INFO_OBJ(X,Y) __GCOV_INFO_OBJ(X,Y)
+#define GCOV_INFO_OBJ() _GCOV_INFO_OBJ(__GCOV_PREFIX, _gcov_info_head)
+
+/** Pointer to the beginning of the gcov_info chain for this module. */
+gcov_info* GCOV_INFO_OBJ() = NULL;
+
+/** Function called by module loading to add the object gcov_info instance
+ * to the chain.
+ */
+extern "C"
+void __gcov_init(gcov_info* i_info)
+{
+ // Atomically push i_info onto the gcov_info_head stack.
+ do
+ {
+ i_info->next = GCOV_INFO_OBJ();
+ } while (!__sync_bool_compare_and_swap(&GCOV_INFO_OBJ(),
+ i_info->next, i_info));
+}
+
+/** Unneeded function but must be defined to compile.
+ *
+ * This function appears to be typically used by libgcov.so when instrumented
+ * on a real linux-based system. It can be used to merge counters across
+ * multiple runs or when a 'fork' occurs. It doesn't appear that this
+ * function ever gets called for us but the unresolved symbol is added to
+ * the module (by gcc) so we've created a stub here to pass compile.
+ */
+extern "C"
+void __gcov_merge_add()
+{
+ while(1);
+}
+
+#endif
diff --git a/src/kernel/start.S b/src/kernel/start.S
index bcb770a58..1deaf46f1 100644
--- a/src/kernel/start.S
+++ b/src/kernel/start.S
@@ -614,6 +614,17 @@ userspace_task_entry:
ld r2, 8(r4)
bctr
+ ;// @fn task_end_stub
+ ;// Stub to call a TASK_END syscall in the event that a task 'returns' from
+ ;// its entry point. We cannot call task_end() directly because profiling
+ ;// inserts garbage code into the task_end C function.
+.global task_end_stub
+task_end_stub:
+ li r3, 2 ;// TASK_END -> r3 (syscall number)
+ li r4, 0 ;// NULL -> r4 (status value)
+ sc
+
+
STD_INTERRUPT_NOADDR(hype_emu_assist)
.section .data
diff --git a/src/kernel/taskmgr.C b/src/kernel/taskmgr.C
index b71bd2f25..2ff6ce4ad 100644
--- a/src/kernel/taskmgr.C
+++ b/src/kernel/taskmgr.C
@@ -36,14 +36,7 @@
#include <assert.h>
extern "C" void userspace_task_entry();
-
-void TaskManager::idleTaskLoop(void* unused)
-{
- while(1)
- {
- setThreadPriorityLow();
- }
-}
+extern "C" void task_end_stub();
task_t* TaskManager::getCurrentTask()
{
@@ -103,10 +96,9 @@ task_t* TaskManager::_createTask(TaskManager::task_fn_t t,
task->context.nip = reinterpret_cast<void*>(&userspace_task_entry);
task->context.gprs[4] = reinterpret_cast<uint64_t>(t);
- // Set up LR to be the entry point for task_end in case a task
- // 'returns' from its entry point. By the Power ABI, the entry
- // point address is in (func_ptr)[0].
- task->context.lr = reinterpret_cast<uint64_t*>(&task_end)[0];
+ // Set up LR to be the entry point for task_end_stub in case a task
+ // 'returns' from its entry point.
+ task->context.lr = reinterpret_cast<uint64_t>(&task_end_stub);
// Set up GRP[13] as task structure reserved.
task->context.gprs[13] = (uint64_t)task;
diff --git a/src/makefile b/src/makefile
index ddd9dc6ca..cf3ea5520 100644
--- a/src/makefile
+++ b/src/makefile
@@ -29,7 +29,11 @@ EXTRA_LIDS = dslid
BASE_OBJECTS = console.o spinlock.o string.o string_ext.o stdlib.o ctype.o \
assert.o stdio.o builtins.o vfs_init.o heapmgr.o pagemgr.o \
- math.o barrier.o idebug.o intmsghandler.o
+ math.o barrier.o idebug.o intmsghandler.o idletask.o
+
+ifdef HOSTBOOT_PROFILE
+BASE_OBJECTS += gcov.o
+endif
DIRECT_BOOT_OBJECTS = start.o kernel.o taskmgr.o cpumgr.o syscall.o \
scheduler.o exception.o vmmmgr.o timemgr.o \
diff --git a/src/sys/makefile b/src/sys/makefile
index f26ea333a..a930b8b6e 100644
--- a/src/sys/makefile
+++ b/src/sys/makefile
@@ -22,6 +22,6 @@
# IBM_PROLOG_END
ROOTPATH = ../..
-SUBDIRS = init.d vfs.d
+SUBDIRS = init.d vfs.d prof.d
include ${ROOTPATH}/config.mk
diff --git a/src/sys/prof/gcov.C b/src/sys/prof/gcov.C
new file mode 100644
index 000000000..7f011d51f
--- /dev/null
+++ b/src/sys/prof/gcov.C
@@ -0,0 +1,24 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/sys/prof/gcov.C $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2012
+//
+// p1
+//
+// Object Code Only (OCO) source materials
+// Licensed Internal Code Source Materials
+// IBM HostBoot Licensed Internal Code
+//
+// The source code for this program is not published or other-
+// wise divested of its trade secrets, irrespective of what has
+// been deposited with the U.S. Copyright Office.
+//
+// Origin: 30
+//
+// IBM_PROLOG_END
+
+#include <usr/gcov.h>
diff --git a/src/sys/prof/idletask.C b/src/sys/prof/idletask.C
new file mode 100644
index 000000000..843f26628
--- /dev/null
+++ b/src/sys/prof/idletask.C
@@ -0,0 +1,41 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/sys/prof/idletask.C $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2012
+//
+// p1
+//
+// Object Code Only (OCO) source materials
+// Licensed Internal Code Source Materials
+// IBM HostBoot Licensed Internal Code
+//
+// The source code for this program is not published or other-
+// wise divested of its trade secrets, irrespective of what has
+// been deposited with the U.S. Copyright Office.
+//
+// Origin: 30
+//
+// IBM_PROLOG_END
+
+/** @file idletask.C
+ * @brief Code for the kernel idle-loop.
+ *
+ * This code exists here under the sys/prof directory to ensure that it does
+ * not get instrumented with code coverage. The idle threads run stack-less
+ * and bad things happen if they have been instrumented.
+ */
+
+#include <kernel/taskmgr.H>
+#include <arch/ppc.H>
+
+void TaskManager::idleTaskLoop(void* unused)
+{
+ while(1)
+ {
+ setThreadPriorityLow();
+ }
+}
diff --git a/src/sys/prof/makefile b/src/sys/prof/makefile
new file mode 100644
index 000000000..ee42459e4
--- /dev/null
+++ b/src/sys/prof/makefile
@@ -0,0 +1,39 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/sys/vfs/makefile $
+#
+# IBM CONFIDENTIAL
+#
+# COPYRIGHT International Business Machines Corp. 2010 - 2011
+#
+# p1
+#
+# Object Code Only (OCO) source materials
+# Licensed Internal Code Source Materials
+# IBM HostBoot Licensed Internal Code
+#
+# The source code for this program is not published or other-
+# wise divested of its trade secrets, irrespective of what has
+# been deposited with the U.S. Copyright Office.
+#
+# Origin: 30
+#
+# IBM_PROLOG_END
+
+ROOTPATH = ../../..
+
+# If code coverage profiling is enabled we need to add the gcov object to the
+# build list. This is triggered via the HOSTBOOT_PROFILE variable. We then
+# undefine that variable to ensure that none of the code in this directory is
+# instrumented itself. This is the directory where code which is unsafe to
+# instrument should go.
+
+ifdef HOSTBOOT_PROFILE
+OBJS = gcov.o
+undefine HOSTBOOT_PROFILE
+endif
+
+OBJS += idletask.o
+
+include ${ROOTPATH}/config.mk
OpenPOWER on IntegriCloud