From e457b5df08897236374bd8851d8169188e9dc1f8 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Tue, 10 Jan 2012 18:10:40 -0600 Subject: 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 --- .gitignore | 3 + config.mk | 56 ++++- makefile | 30 +++ obj/.gitignore | 1 + src/build/debug/Hostboot/Gcov.pm | 375 ++++++++++++++++++++++++++++ src/build/debug/Hostboot/_DebugFramework.pm | 14 +- src/include/kernel/syscalls.H | 1 + src/include/usr/gcov.h | 102 ++++++++ src/kernel/start.S | 11 + src/kernel/taskmgr.C | 16 +- src/makefile | 6 +- src/sys/makefile | 2 +- src/sys/prof/gcov.C | 24 ++ src/sys/prof/idletask.C | 41 +++ src/sys/prof/makefile | 39 +++ 15 files changed, 699 insertions(+), 22 deletions(-) create mode 100644 src/build/debug/Hostboot/Gcov.pm create mode 100644 src/include/usr/gcov.h create mode 100644 src/sys/prof/gcov.C create mode 100644 src/sys/prof/idletask.C create mode 100644 src/sys/prof/makefile 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 + +/** @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 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(&userspace_task_entry); task->context.gprs[4] = reinterpret_cast(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(&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(&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 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 +#include + +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 -- cgit v1.2.1