diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/Makefile 280-gcov/Makefile
--- 279-no_numa_pc/Makefile	2004-02-18 15:02:04.000000000 -0800
+++ 280-gcov/Makefile	2004-02-18 16:23:02.000000000 -0800
@@ -67,6 +67,8 @@ endif
 #
 # The O= assigment takes precedence over the KBUILD_OUTPUT environment variable.
 
+GCOV_FLAGS	= -fprofile-arcs -ftest-coverage
+
 
 # KBUILD_SRC is set on invocation of make in OBJ directory
 # KBUILD_SRC is not intended to be used by the regular user (for now)
@@ -288,6 +290,8 @@ export	VERSION PATCHLEVEL SUBLEVEL EXTRA
 export CPPFLAGS NOSTDINC_FLAGS OBJCOPYFLAGS LDFLAGS
 export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE 
 export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
+export CFLAGS_NOGCOV
+
 
 export MODVERDIR := .tmp_versions
 
@@ -669,6 +673,11 @@ depend dep:
 # ---------------------------------------------------------------------------
 # Modules
 
+CFLAGS_NOGCOV := $(CFLAGS)
+ifdef CONFIG_GCOV_ALL
+CFLAGS += $(GCOV_FLAGS)
+endif
+
 ifdef CONFIG_MODULES
 
 # 	By default, build modules as well
@@ -791,6 +800,7 @@ clean: archclean $(clean-dirs)
 	$(call cmd,rmclean)
 	@find . $(RCS_FIND_IGNORE) \
 	 	\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+		-o -name '*.bb' -o -name '*.bbg' -o -name '*.da' \
 		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
 		-type f -print | xargs rm -f
 
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/i386/Kconfig 280-gcov/arch/i386/Kconfig
--- 279-no_numa_pc/arch/i386/Kconfig	2004-02-18 16:22:41.000000000 -0800
+++ 280-gcov/arch/i386/Kconfig	2004-02-18 16:23:02.000000000 -0800
@@ -1291,6 +1291,36 @@ source "fs/Kconfig"
 
 source "arch/i386/oprofile/Kconfig"
 
+menu "GCOV coverage profiling"
+
+config GCOV_PROFILE
+	bool "GCOV coverage profiling"
+	---help---
+	Provide infrastructure for coverage support for the kernel. This
+	will not compile the kernel by default with the necessary flags.
+	To obtain coverage information for the entire kernel, one should
+	enable the subsequent option (Profile entire kernel). If only
+	particular files or directories of the kernel are desired, then
+	one must provide the following compile options for such targets:
+      		"-fprofile-arcs -ftest-coverage" in the CFLAGS. To obtain
+	access to the coverage data one must insmod the gcov-proc kernel
+	module.
+
+config GCOV_ALL
+	bool "GCOV_ALL"
+	depends on GCOV_PROFILE
+	---help---
+	If you say Y here, it will compile the entire kernel with coverage
+	option enabled.
+
+config GCOV_PROC
+	tristate "gcov-proc module"
+	depends on GCOV_PROFILE && PROC_FS
+	---help---
+	This is the gcov-proc module that exposes gcov data through the 
+	/proc filesystem
+
+endmenu
 
 menu "Kernel hacking"
 
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/i386/boot/compressed/Makefile 280-gcov/arch/i386/boot/compressed/Makefile
--- 279-no_numa_pc/arch/i386/boot/compressed/Makefile	2003-03-20 11:25:38.000000000 -0800
+++ 280-gcov/arch/i386/boot/compressed/Makefile	2004-02-18 16:23:02.000000000 -0800
@@ -7,6 +7,7 @@
 targets		:= vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o
 EXTRA_AFLAGS	:= -traditional
 
+CFLAGS := $(CFLAGS_NOGCOV)
 LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32
 
 $(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/i386/kernel/head.S 280-gcov/arch/i386/kernel/head.S
--- 279-no_numa_pc/arch/i386/kernel/head.S	2004-02-18 16:18:36.000000000 -0800
+++ 280-gcov/arch/i386/kernel/head.S	2004-02-18 16:23:02.000000000 -0800
@@ -508,3 +508,24 @@ ENTRY(cpu_gdt_table)
 	.fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */
 #endif
 
+#ifdef CONFIG_GCOV_PROFILE
+/*
+ * The .ctors-section contains a list of pointers to constructor
+ * functions which are used to initialize gcov structures.
+ *
+ * Because there is no NULL at the end of the constructor list
+ * in the kernel we need the addresses of both the constructor
+ * as well as the destructor list which are supposed to be
+ * adjacent.
+ */
+
+.section ".ctors","aw"
+.globl  __CTOR_LIST__
+.type   __CTOR_LIST__,@object
+__CTOR_LIST__:
+.section ".dtors","aw"
+.globl  __DTOR_LIST__
+.type   __DTOR_LIST__,@object
+__DTOR_LIST__:
+#endif
+
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/Kconfig 280-gcov/arch/ppc/Kconfig
--- 279-no_numa_pc/arch/ppc/Kconfig	2004-02-18 14:56:49.000000000 -0800
+++ 280-gcov/arch/ppc/Kconfig	2004-02-18 16:23:02.000000000 -0800
@@ -1125,6 +1125,36 @@ endmenu
 
 source "lib/Kconfig"
 
+menu "GCOV coverage profiling"
+
+config GCOV_PROFILE
+	bool "GCOV coverage profiling"
+	---help---
+	Provide infrastructure for coverage support for the kernel. This
+	will not compile the kernel by default with the necessary flags.
+	To obtain coverage information for the entire kernel, one should
+	enable the subsequent option (Profile entire kernel). If only
+	particular files or directories of the kernel are desired, then
+	one must provide the following compile options for such targets:
+		"-fprofile-arcs -ftest-coverage" in the CFLAGS. To obtain
+	access to the coverage data one must insmod the gcov-prof kernel
+	module.
+
+config GCOV_ALL
+	bool "GCOV_ALL"
+	depends on GCOV_PROFILE
+	---help---
+	If you say Y here, it will compile the entire kernel with coverage
+	option enabled.
+
+config GCOV_PROC
+        tristate "gcov-proc module"
+        depends on GCOV_PROFILE && PROC_FS
+        ---help---
+        This is the gcov-proc module that exposes gcov data through the
+        /proc filesystem
+
+endmenu
 
 menu "Kernel hacking"
 
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/boot/openfirmware/common.c 280-gcov/arch/ppc/boot/openfirmware/common.c
--- 279-no_numa_pc/arch/ppc/boot/openfirmware/common.c	2002-12-09 18:46:16.000000000 -0800
+++ 280-gcov/arch/ppc/boot/openfirmware/common.c	2004-02-18 16:23:02.000000000 -0800
@@ -30,6 +30,10 @@ struct memchunk {
 
 static struct memchunk *freechunks;
 
+#ifdef CONFIG_GCOV_PROFILE
+void __bb_init_func (void *ptr /* struct bb *blocks */) { }
+#endif
+
 static void *zalloc(void *x, unsigned items, unsigned size)
 {
     void *p;
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/boot/prep/misc.c 280-gcov/arch/ppc/boot/prep/misc.c
--- 279-no_numa_pc/arch/ppc/boot/prep/misc.c	2004-02-04 16:23:52.000000000 -0800
+++ 280-gcov/arch/ppc/boot/prep/misc.c	2004-02-18 16:23:02.000000000 -0800
@@ -71,6 +71,10 @@ extern unsigned long serial_init(int cha
 extern void serial_fixups(void);
 extern unsigned long get_mem_size(void);
 
+#ifdef CONFIG_GCOV_PROFILE
+void __bb_init_func (void *ptr /* struct bb *blocks */) { }
+#endif
+
 void
 writel(unsigned int val, unsigned int address)
 {
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/kernel/Makefile 280-gcov/arch/ppc/kernel/Makefile
--- 279-no_numa_pc/arch/ppc/kernel/Makefile	2004-02-18 14:56:49.000000000 -0800
+++ 280-gcov/arch/ppc/kernel/Makefile	2004-02-18 16:23:03.000000000 -0800
@@ -17,8 +17,8 @@ extra-$(CONFIG_6xx)		+= idle_6xx.o
 extra-$(CONFIG_POWER4)		+= idle_power4.o
 extra-y				+= vmlinux.lds.s
 
-obj-y				:= entry.o traps.o irq.o idle.o time.o misc.o \
-					process.o signal.o ptrace.o align.o \
+obj-y				:= entry.o ptrace.o traps.o irq.o idle.o time.o misc.o \
+					process.o signal.o align.o \
 					semaphore.o syscalls.o setup.o \
 					cputable.o ppc_htab.o
 obj-$(CONFIG_6xx)		+= l2cr.o cpu_setup_6xx.o
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/kernel/entry.S 280-gcov/arch/ppc/kernel/entry.S
--- 279-no_numa_pc/arch/ppc/kernel/entry.S	2004-02-18 14:56:49.000000000 -0800
+++ 280-gcov/arch/ppc/kernel/entry.S	2004-02-18 16:23:03.000000000 -0800
@@ -106,10 +106,26 @@ transfer_to_handler:
 	mfspr	r11,SPRN_HID0
 	mtcr	r11
 BEGIN_FTR_SECTION
+#ifdef CONFIG_GCOV_PROFILE
+	bt-	8,near1_power_save_6xx_restore	/* Check DOZE */
+	b       skip1_power_save_6xx_restore    
+near1_power_save_6xx_restore:
+	b	power_save_6xx_restore
+skip1_power_save_6xx_restore:
+#else
 	bt-	8,power_save_6xx_restore	/* Check DOZE */
+#endif
 END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
 BEGIN_FTR_SECTION
+#ifdef CONFIG_GCOV_PROFILE
+	bt-	9,near2_power_save_6xx_restore	/* Check NAP */
+	b	skip2_power_save_6xx_restore
+near2_power_save_6xx_restore:
+	b	power_save_6xx_restore
+skip2_power_save_6xx_restore:
+#else
 	bt-	9,power_save_6xx_restore	/* Check NAP */
+#endif
 END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
 #endif /* CONFIG_6xx */
 	.globl transfer_to_handler_cont
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/kernel/head.S 280-gcov/arch/ppc/kernel/head.S
--- 279-no_numa_pc/arch/ppc/kernel/head.S	2004-02-18 14:56:49.000000000 -0800
+++ 280-gcov/arch/ppc/kernel/head.S	2004-02-18 16:23:03.000000000 -0800
@@ -1710,3 +1710,25 @@ intercept_table:
  */
 abatron_pteptrs:
 	.space	8
+
+#ifdef CONFIG_GCOV_PROFILE
+/*
+ * The .ctors-section contains a list of pointers to constructor
+ * functions which are used to initialize gcov structures.
+ *  
+ * Because there is no NULL at the end of the constructor list
+ * in the kernel we need the addresses of both the constructor
+ * as well as the destructor list which are supposed to be
+ * adjacent.
+ */ 
+ 
+.section ".ctors","aw"
+.globl  __CTOR_LIST__
+.type   __CTOR_LIST__,@object
+__CTOR_LIST__:
+.section ".dtors","aw"
+.globl  __DTOR_LIST__
+.type   __DTOR_LIST__,@object
+__DTOR_LIST__:
+#endif
+
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc/syslib/prom_init.c 280-gcov/arch/ppc/syslib/prom_init.c
--- 279-no_numa_pc/arch/ppc/syslib/prom_init.c	2004-02-18 14:56:50.000000000 -0800
+++ 280-gcov/arch/ppc/syslib/prom_init.c	2004-02-18 16:23:03.000000000 -0800
@@ -756,7 +756,11 @@ prom_instantiate_rtas(void)
 		 * Actually OF has bugs so we just arbitrarily
 		 * use memory at the 6MB point.
 		 */
+#ifdef CONFIG_GCOV_PROFILE
+		rtas_data = 0x990000;
+#else
 		rtas_data = 6 << 20;
+#endif
 		prom_print(" at ");
 		prom_print_hex(rtas_data);
 	}
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc64/Kconfig 280-gcov/arch/ppc64/Kconfig
--- 279-no_numa_pc/arch/ppc64/Kconfig	2004-02-18 14:56:50.000000000 -0800
+++ 280-gcov/arch/ppc64/Kconfig	2004-02-18 16:23:03.000000000 -0800
@@ -326,6 +326,37 @@ config VIOPATH
 
 source "arch/ppc64/oprofile/Kconfig"
 
+menu "GCOV coverage profiling"
+
+config GCOV_PROFILE
+        bool "GCOV coverage profiling"
+        ---help---
+        Provide infrastructure for coverage support for the kernel. This
+        will not compile the kernel by default with the necessary flags.
+        To obtain coverage information for the entire kernel, one should
+        enable the subsequent option (Profile entire kernel). If only
+        particular files or directories of the kernel are desired, then
+        one must provide the following compile options for such targets:
+                "-fprofile-arcs -ftest-coverage" in the CFLAGS. To obtain
+        access to the coverage data one must insmod the gcov-prof kernel
+        module.
+
+config GCOV_ALL
+        bool "GCOV_ALL"
+        depends on GCOV_PROFILE
+        ---help---
+        If you say Y here, it will compile the entire kernel with coverage
+        option enabled.
+
+config GCOV_PROC
+        tristate "gcov-proc module"
+        depends on GCOV_PROFILE && PROC_FS
+        ---help---
+        This is the gcov-proc module that exposes gcov data through the
+        /proc filesystem
+
+endmenu
+
 menu "Kernel hacking"
 
 config DEBUG_KERNEL
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/ppc64/kernel/head.S 280-gcov/arch/ppc64/kernel/head.S
--- 279-no_numa_pc/arch/ppc64/kernel/head.S	2004-02-18 14:56:50.000000000 -0800
+++ 280-gcov/arch/ppc64/kernel/head.S	2004-02-18 16:23:03.000000000 -0800
@@ -2178,3 +2178,24 @@ stab_array:
 	.globl	cmd_line
 cmd_line:
 	.space	512
+
+#ifdef CONFIG_GCOV_PROFILE
+/*
+ * The .ctors-section contains a list of pointers to constructor
+ * functions which are used to initialize gcov structures.
+ *
+ * Because there is no NULL at the end of the constructor list
+ * in the kernel we need the addresses of both the constructor
+ * as well as the destructor list which are supposed to be
+ * adjacent.
+ */
+
+.section ".ctors","aw"
+.globl  __CTOR_LIST__
+.type   __CTOR_LIST__,@object
+__CTOR_LIST__:
+.section ".dtors","aw"
+.globl  __DTOR_LIST__
+.type   __DTOR_LIST__,@object
+__DTOR_LIST__:
+#endif
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/x86_64/Kconfig 280-gcov/arch/x86_64/Kconfig
--- 279-no_numa_pc/arch/x86_64/Kconfig	2004-02-04 16:23:57.000000000 -0800
+++ 280-gcov/arch/x86_64/Kconfig	2004-02-18 16:23:03.000000000 -0800
@@ -376,6 +376,37 @@ source fs/Kconfig
 
 source "arch/x86_64/oprofile/Kconfig"
 
+menu "GCOV coverage profiling"
+
+config GCOV_PROFILE
+        bool "GCOV coverage profiling"
+        ---help---
+        Provide infrastructure for coverage support for the kernel. This
+        will not compile the kernel by default with the necessary flags.
+        To obtain coverage information for the entire kernel, one should
+        enable the subsequent option (Profile entire kernel). If only
+        particular files or directories of the kernel are desired, then
+        one must provide the following compile options for such targets:
+                "-fprofile-arcs -ftest-coverage" in the CFLAGS. To obtain
+        access to the coverage data one must insmod the gcov-prof kernel
+        module.
+
+config GCOV_ALL
+        bool "GCOV_ALL"
+        depends on GCOV_PROFILE
+        ---help---
+        If you say Y here, it will compile the entire kernel with coverage
+        option enabled.
+
+config GCOV_PROC
+        tristate "gcov-proc module"
+        depends on GCOV_PROFILE && PROC_FS
+        ---help---
+        This is the gcov-proc module that exposes gcov data through the
+        /proc filesystem
+
+endmenu
+
 menu "Kernel hacking"
 
 config DEBUG_KERNEL
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/arch/x86_64/kernel/head.S 280-gcov/arch/x86_64/kernel/head.S
--- 279-no_numa_pc/arch/x86_64/kernel/head.S	2004-01-15 10:41:02.000000000 -0800
+++ 280-gcov/arch/x86_64/kernel/head.S	2004-02-18 16:23:03.000000000 -0800
@@ -372,3 +372,23 @@ ENTRY(idt_table)	
 	.quad 	0
 	.endr
 
+#ifdef CONFIG_GCOV_PROFILE
+/*
+ * The .ctors-section contains a list of pointers to constructor
+ * functions which are used to initialize gcov structures.
+ *
+ * Because there is no NULL at the end of the constructor list
+ * in the kernel we need the addresses of both the constructor
+ * as well as the destructor list which are supposed to be
+ * adjacent.
+ */
+
+.section ".ctors","aw"
+.globl  __CTOR_LIST__
+.type   __CTOR_LIST__,@object
+__CTOR_LIST__:
+.section ".dtors","aw"
+.globl  __DTOR_LIST__
+.type   __DTOR_LIST__,@object
+__DTOR_LIST__:
+#endif
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/drivers/Makefile 280-gcov/drivers/Makefile
--- 279-no_numa_pc/drivers/Makefile	2003-10-01 11:46:32.000000000 -0700
+++ 280-gcov/drivers/Makefile	2004-02-18 16:23:03.000000000 -0800
@@ -49,3 +49,4 @@ obj-$(CONFIG_ISDN_BOOL)		+= isdn/
 obj-$(CONFIG_MCA)		+= mca/
 obj-$(CONFIG_EISA)		+= eisa/
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
+obj-$(CONFIG_GCOV_PROC)		+= gcov/
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/drivers/gcov/Makefile 280-gcov/drivers/gcov/Makefile
--- 279-no_numa_pc/drivers/gcov/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ 280-gcov/drivers/gcov/Makefile	2004-02-18 16:23:03.000000000 -0800
@@ -0,0 +1,8 @@
+#
+# Makefile for GCOV profiling kernel module
+#
+
+obj-$(CONFIG_GCOV_PROC)	+= gcov-proc.o
+
+$(obj)/gcov-proc.o: $(obj)/gcov-proc.c
+
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/drivers/gcov/gcov-proc.c 280-gcov/drivers/gcov/gcov-proc.c
--- 279-no_numa_pc/drivers/gcov/gcov-proc.c	1969-12-31 16:00:00.000000000 -0800
+++ 280-gcov/drivers/gcov/gcov-proc.c	2004-02-18 16:23:03.000000000 -0800
@@ -0,0 +1,713 @@
+/*
+ * This kernel module provides access to coverage data produced by
+ * an instrumented kernel via an entry in the proc file system
+ * at /proc/gcov/.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (c) International Business Machines Corp., 2002
+ *
+ * Author: Hubertus Franke <frankeh@us.ibm.com>
+ *         Rajan Ravindran <rajancr@us.ibm.com>
+ *
+ * 	Bugfixes by Peter.Oberparleiter@de.ibm.com:
+ * 	Changes by Paul Larson
+ * 		Automatically detect gcc version for gcov_type
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>   
+#include <linux/module.h>   
+
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+
+MODULE_LICENSE("GPL");
+#define GCOV_PROF_PROC		"gcov"
+
+static DECLARE_MUTEX_LOCKED(gcov_lock);  
+#define DOWN()  down(&gcov_lock);
+#define UP()    up(&gcov_lock);
+#define PAD8(x)	((x + 7) & ~7)
+
+//#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,4))
+//static inline struct proc_dir_entry *PDE(const struct inode *inode)
+//{
+//	return ((struct proc_dir_entry *) inode->u.generic_ip);
+//}
+//#endif
+
+/* ###################################################################
+   # NOTICE ##########################################################
+   ###################################################################
+
+   GCOV_TYPE defines the count type used by the instrumentation code.
+   Kernels compiled with a gcc version prior to 3.1 should use LONG,
+   otherwise LONG LONG.  */
+
+#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+typedef long long gcov_type;
+#else
+typedef long gcov_type;
+#endif
+
+
+struct bb
+{
+  long zero_word;
+  const char *filename;
+  gcov_type *counts;
+  long ncounts;
+  struct bb *next;
+  const unsigned long *addresses;
+
+  /* Older GCC's did not emit these fields.  */
+  long nwords;
+  const char **functions;
+  const long *line_nums;
+  const char **filenames;
+  char *flags;
+};
+
+extern struct bb *bb_head;
+static struct file_operations proc_gcov_operations;
+extern char   *gcov_kernelpath;
+extern void   (*gcov_callback)(int cmd, struct bb *);
+extern void   do_global_ctors(char *, char *, struct module *, int);
+ 
+static int create_bb_links = 1;
+static int kernel_path_len;
+
+int debug = 0;
+#define PPRINTK(x) do { if (debug) { printk x ; } } while (0)
+
+struct gcov_ftree_node
+{
+	int   isdir;    /* directory or file */
+	char *fname;    /* only the name within the hierachy */
+	struct gcov_ftree_node *sibling;   /* sibling of tree  */
+	struct gcov_ftree_node *files;  /* children of tree */
+	struct gcov_ftree_node *parent; /* parent of current gcov_ftree_node */
+	struct proc_dir_entry  *proc[4];
+	struct bb	      *bb;
+	/* below only valid for leaf nodes == files */
+	unsigned long  offset;	  /* offset in global file */
+	struct gcov_ftree_node *next;   /* next leave node       */
+};
+
+static struct proc_dir_entry  *proc_vmlinux = NULL;
+static struct gcov_ftree_node *leave_nodes = NULL;
+static struct gcov_ftree_node *dumpall_cached_node = NULL;
+static struct gcov_ftree_node tree_root  = 
+	{ 1, GCOV_PROF_PROC, NULL, NULL, NULL,
+	  { NULL, NULL, NULL, NULL} , NULL, 0,NULL };
+static char *endings[3] = { ".bb", ".bbg", ".c" };
+
+
+/* Calculate the header size of an entry in the vmlinux-tracefile which
+   contains the collection of trace data of all instrumented kernel objects.
+
+   An entry header is defined as:
+     0:  length of filename of the respective .da file padded to 8 bytes
+     8:  filename padded to 8 bytes
+
+ */
+
+static inline unsigned long
+hdr_ofs (struct gcov_ftree_node *tptr)
+{
+	return 8 + PAD8(strlen (tptr->bb->filename) + 1);
+}
+
+
+/* Calculate the total size of an entry in the vmlinux-tracefile.
+   An entry consists of the header, an 8 byte word for the number
+   of counts in this entry and the actual array of 8 byte counts.  */
+
+static inline unsigned long
+dump_size(struct gcov_ftree_node *tptr)
+{
+	return (hdr_ofs(tptr) + (tptr->bb->ncounts+1)*8);
+}
+
+
+/* Store a portable representation of VALUE in DEST using BYTES*8-1 bits.
+   Return a non-zero value if VALUE requires more than BYTES*8-1 bits
+   to store (this is adapted code from gcc/gcov-io.h).  */
+
+static int
+store_gcov_type (gcov_type value, void *buf, int offset, int len)
+{
+	const size_t bytes = 8;
+	char dest[10];
+	int upper_bit = (value < 0 ? 128 : 0);
+	size_t i;
+ 
+	if (value < 0) {
+		gcov_type oldvalue = value;
+		value = -value;
+		if (oldvalue != -value)
+		return 1;
+	}
+ 
+	for(i = 0 ;
+	    i < (sizeof (value) < bytes ? sizeof (value) : bytes) ;
+	    i++) {
+		dest[i] = value & (i == (bytes - 1) ? 127 : 255);
+		value = value / 256;
+	}
+ 
+	if (value && value != -1)
+		return 1;
+ 
+	for(; i < bytes ; i++)
+	  dest[i] = 0;
+	dest[bytes - 1] |= upper_bit;
+	copy_to_user(buf,&dest[offset],len);
+	return 0;
+}
+
+
+/* Create a directory entry in the proc file system and fill in
+   the respective fields in the provided tree node. Return a
+   non-zero value on error.  */
+
+int
+create_dir_proc (struct gcov_ftree_node *bt, char *fname) 
+{
+	bt->proc[0] = proc_mkdir(fname, bt->parent->proc[0]);
+	bt->proc[1] = bt->proc[2] = bt->proc[3] = NULL;
+	return (bt->proc[0] == NULL);
+}
+
+
+/* Replace file ending <end> in <fname> with <newend>. Return a new
+   string containing the new filename or NULL on error.  */
+
+static 
+char* replace_ending (const char *fname,char *end, char *newend)
+{
+	char *newfname;
+	char *cptr = strstr(fname,end);
+	int len;
+	if (cptr == NULL) 
+		return NULL;
+	len = cptr - fname;
+	newfname = (char*)kmalloc(len+strlen(newend)+1,GFP_KERNEL);
+	if (newfname == NULL) 
+		return NULL;
+	memcpy(newfname,fname,len);
+	strcpy(newfname+len,newend);
+	return newfname;	
+} 
+	
+
+/* Create a file entry in the proc file system and update the respective
+   fields on the tree node. Optionally try to create links to the
+   source, .bb and .bbg files. Return a non-zero value on error.  */
+
+int
+create_file_proc (struct gcov_ftree_node *bt, struct bb *bptr, char *fname,
+		  const char *fullname) 
+{
+	bt->proc[0]  = create_proc_entry(fname, S_IWUSR | S_IRUGO, 
+					 bt->parent->proc[0]);
+	if (!bt->proc[0]) {
+		PPRINTK(("error creating file proc <%s>\n", fname));
+		return 1;
+	}
+
+	bt->proc[0]->proc_fops = &proc_gcov_operations;
+	bt->proc[0]->size = 8 + (8 * bptr->ncounts);
+
+	if (create_bb_links) {
+		int i;
+		for (i=0;i<3;i++) {
+			char *newfname;
+			char *newfullname;
+			newfname    = replace_ending(fname,".da",endings[i]);
+			newfullname = replace_ending(fullname,".da",endings[i]);
+			if ((newfname) && (newfullname)) {
+				bt->proc[i+1]  = proc_symlink(newfname,bt->parent->proc[0],newfullname);
+			}
+			if (newfname) kfree(newfname);
+			if (newfullname) kfree(newfullname);
+		}
+	} else {
+		bt->proc[1] = bt->proc[2] = bt->proc[3] = NULL; 
+	}
+	return 0;
+}
+
+
+/* Recursively check and if necessary create the file specified by <name>
+   and all its path components, both in the proc file-system as
+   well as in the internal tree structure.  */
+
+void 
+check_proc_fs(const char *fullname, struct gcov_ftree_node *parent, 
+		   char *name, struct bb *bbptr)
+{
+	char dirname[128];
+	char *localname = name;
+	char *tname;
+	int  isdir;
+	struct gcov_ftree_node *tptr;
+
+	tname = strstr(name, "/");
+	if ((isdir = (tname != NULL))) {
+		memcpy(dirname,name,tname-name);
+		dirname[tname-name] = '\0';
+		localname = dirname;
+	}
+
+	/* search the list of files in gcov_ftree_node and 
+	 * see whether file already exists in this directory level */
+	for ( tptr = parent->files ; tptr ; tptr = tptr->sibling) {
+		if (!strcmp(tptr->fname,localname))
+			break;
+	}
+	if (!tptr) {
+		/* no entry yet */
+		tptr = (struct gcov_ftree_node*)
+			kmalloc(sizeof(struct gcov_ftree_node),GFP_KERNEL);
+		tptr->parent  = parent;
+
+		if (!isdir) {
+			if (create_file_proc(tptr, bbptr, localname,fullname)) {
+				kfree(tptr);
+				return;
+			}
+			tptr->bb	 = bbptr;
+			tptr->proc[0]->data = tptr;
+			tptr->next = leave_nodes;
+			leave_nodes = tptr;
+		} else {
+			int len = strlen(dirname)+1;
+			localname = (char*)kmalloc(len,GFP_KERNEL);
+			strncpy(localname,dirname,len);
+			if (create_dir_proc(tptr,localname)) {
+				kfree(tptr);
+				kfree(localname);
+				return;
+			}
+			tptr->bb	 = NULL;
+			tptr->proc[0]->data = NULL;
+			tptr->next       = NULL;
+		}
+		tptr->isdir   = isdir;
+		tptr->fname   = localname;
+		tptr->files   = NULL;
+		tptr->sibling = parent->files;
+		parent->files = tptr;
+	}
+	if (isdir)
+		check_proc_fs(fullname,tptr,tname+1,bbptr);
+}
+
+
+/* Read out tracefile data to user space. Return the number of bytes
+   read.  */
+
+static ssize_t 
+read_gcov(struct file *file, char *buf,
+			 size_t count, loff_t *ppos)
+{
+	unsigned long p = *ppos;
+	ssize_t read;
+	gcov_type ncnt;
+	struct bb *bbptr;
+	gcov_type slen;
+	gcov_type *wptr;
+	struct gcov_ftree_node *treeptr; 
+	struct proc_dir_entry * de;
+	int dumpall;
+	unsigned int hdrofs;
+	unsigned long poffs;
+
+	DOWN();
+
+	read   = 0;
+	hdrofs = 0;
+	poffs  = 0;
+	de = PDE(file->f_dentry->d_inode);
+
+	/* Check whether this is a request to /proc/gcov/vmlinux in
+	   which case we should dump the complete tracefile.  */
+	dumpall = (de == proc_vmlinux);
+
+
+	/* Have treeptr point to the tree node to be dumped.  */
+
+	if (!dumpall)
+		treeptr = (struct gcov_ftree_node*) (de ? de->data : NULL);
+	else {
+		/* dumpall_cached_node will speed up things in case
+		   of a sequential read.  */
+		if (dumpall_cached_node && (p >= dumpall_cached_node->offset)) {
+			treeptr = dumpall_cached_node;
+		}
+		else
+			treeptr = leave_nodes;
+
+		/* Search the tree node that covers the requested
+		   tracefile offset.  */
+		while (treeptr) {
+			struct gcov_ftree_node *next = treeptr->next;
+			if ((next == NULL) || (p < next->offset)) {
+				hdrofs = hdr_ofs(treeptr);
+				poffs  = treeptr->offset;
+				break;
+			}
+			treeptr = next;
+		}
+		dumpall_cached_node = treeptr;
+	}
+
+	bbptr = treeptr ? treeptr->bb : NULL;
+
+	if (bbptr == NULL)
+		goto out;
+
+	ncnt = (gcov_type) bbptr->ncounts;
+	p -= poffs;
+
+	do { 
+		if (p < hdrofs) {
+			/* User wants to read parts of the header.  */
+
+			slen = PAD8(strlen(treeptr->bb->filename)+1);
+
+			if (p >= 8) {
+				/* Read filename */
+				if (slen > (gcov_type) count) slen = count;
+				copy_to_user (buf, &treeptr->bb->filename[p-8],
+					      slen);
+				count-=slen;buf+= slen;read+=slen;p+=slen;
+				continue;
+			}
+			wptr = &slen;
+		} 
+		else if (p < (hdrofs + 8)) {
+			/* User wants to read the number of counts in this
+			   entry.  */
+
+			wptr = &ncnt;
+		}
+		else if (p < (hdrofs) + (unsigned long) (ncnt+1)*8) {
+			/* User wants to read actual counters */
+
+			wptr = &bbptr->counts[((p-hdrofs)/8)-1];
+		}
+		else
+			break;
+
+		/* do we have to write partial word */	
+
+		if ((count < 8) || (p & 0x7)) {
+			/* partial write */
+			unsigned long offset = p & 0x7;
+			unsigned long length = (count+offset)<8?count:(8-offset);
+
+			store_gcov_type(*wptr,buf, offset, length);
+			buf+=length;p+=length;count-=length;read+=length;
+			break;
+		} else {
+			store_gcov_type(*wptr,buf, 0, 8);
+			buf+=8;p+=8;count-=8;read+=8;
+		}
+	} while (count > 0);
+	*ppos = p + poffs;
+out:
+	UP();
+	return read;
+}
+
+
+/* A write to any of our proc file-system entries is interpreted
+   as a request to reset the data from that node.  */
+
+static ssize_t 
+write_gcov(struct file * file, const char * buf,
+		       size_t count, loff_t *ppos)
+{
+	struct bb *ptr;
+	struct proc_dir_entry * de;
+	int resetall, i;
+	struct gcov_ftree_node *tptr; 
+
+	DOWN();
+
+	de = PDE(file->f_dentry->d_inode);
+
+	if (de == NULL) { 
+		count = 0;
+		goto out;
+	}
+
+	/* Check for a write to /proc/gcov/vmlinux */
+	resetall = (de == proc_vmlinux);
+
+	if (resetall) {
+		/* Reset all nodes */
+		for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
+		{
+       			int i;
+			if (ptr->counts == NULL) continue;
+			for (i = 0; i < ptr->ncounts; i++) 
+				ptr->counts[i]=0;
+		}
+	} else {
+		/* Reset a single node */
+		tptr = (struct gcov_ftree_node*)(de->data);
+		if (tptr == NULL)
+			goto out;
+		ptr = tptr->bb; 
+		if (ptr->ncounts != 0) {
+			for (i = 0; i < ptr->ncounts; i++) 
+				ptr->counts[i]=0;
+		}
+	}
+out:
+	UP();
+	return count;
+}
+
+
+/* This struct identifies the functions to be used for proc file-system
+   interaction.  */
+
+static struct file_operations proc_gcov_operations = {
+	read:	read_gcov,
+	write:	write_gcov
+};
+
+
+/* Recursively remove a node and all its children from the internal
+   data tree and from the proc file-system.  */
+
+void 
+cleanup_node(struct gcov_ftree_node *node, int delname, int del_in_parent)
+{
+	struct gcov_ftree_node *next,*tptr;
+	struct proc_dir_entry *par_proc;
+
+	PPRINTK(("parent n:%p p:%p f:%p s:%p <%s>\n", node, 
+		node->parent, node->files, node->sibling, node->fname));
+	if ((tptr = node->parent)) { 
+		if (del_in_parent) {
+			/* Remove node from parent's list of children */
+			struct gcov_ftree_node *cptr,*prev_cptr;
+			for ( prev_cptr = NULL, cptr = tptr->files; cptr && (cptr != node);
+			      prev_cptr = cptr, cptr = cptr->sibling); 
+			if (prev_cptr == NULL)
+				tptr->files = cptr->sibling;
+			else
+				prev_cptr->sibling = cptr->sibling;
+		}
+		par_proc = (struct proc_dir_entry*)(tptr->proc[0]);
+	} else
+		par_proc = &proc_root;
+
+	if (node->isdir) {
+		/* In case of a directory, clean up all child nodes.  */
+		next = node->files;
+		node->files = NULL;
+		for (tptr = next ; tptr; ) {
+			next = tptr->sibling;
+			cleanup_node(tptr,1,0);
+			tptr = next;
+		}
+		remove_proc_entry(node->fname, par_proc);
+		if (delname) kfree(node->fname);
+	} else {
+		/* Remove file entry and optional links.  */
+		remove_proc_entry(node->fname, par_proc);
+		if (create_bb_links) {
+			int i;
+			for (i=0;i<3;i++) {
+				char *newfname;
+				if (node->proc[i+1] == NULL) continue;
+				newfname    = replace_ending(node->fname,".da",endings[i]);
+				if (newfname) {
+					PPRINTK(("remove_proc_entry <%s>\n", node->fname));
+					remove_proc_entry(newfname, par_proc);
+					kfree(newfname);
+				}
+			}
+		}     
+	}
+	/* free the data */
+	if (node != &tree_root) 
+		kfree(node);
+}
+
+
+/* Create a tree node for the given bb struct and initiate the
+   creation of a corresponding proc file-system entry.  */
+
+static void
+create_node_tree(struct bb *bbptr)
+{
+	const char *tmp;
+	const char *filename = bbptr->filename;
+	char *modname;
+	int len;
+
+	PPRINTK(("kernelpath <%s> <%s>\n", gcov_kernelpath, filename));
+
+	/* Check whether this is a file located in the kernel source
+	   directory.  */
+	if (!strncmp (filename, gcov_kernelpath, kernel_path_len))
+	{
+		/* Remove kernel path and create relative proc-file-system
+		   entry.  */
+		tmp = filename + kernel_path_len+1;
+		if (*tmp == '0') return; 
+		check_proc_fs(filename, &tree_root, (char*)tmp, bbptr);
+	} 
+	else {
+		/* Insert entry to module sub-directory.  */
+		len = strlen(filename);
+ 		modname = (char *)kmalloc (len + 7, GFP_KERNEL);
+		strcpy(modname, "module");
+		strcat (modname, filename);
+		check_proc_fs(filename, &tree_root, modname, bbptr);
+	}
+}
+
+
+/* This function will be used as gcov_callback, i.e. it is
+   called from constructor and destructor code of all instrumented
+   object files. It updates the local tree structure and the proc
+   file-system entries.  */
+
+static void 
+gcov_cleanup(int cmd, struct bb *bbptr)
+{
+	unsigned long offset = 0;
+	struct gcov_ftree_node *tptr;
+	struct gcov_ftree_node *parent;
+	struct gcov_ftree_node *prev_cptr;
+
+	DOWN(); 
+	switch (cmd) {
+	case 0:
+		/* remove leave node */
+		prev_cptr = NULL;
+		for (tptr = leave_nodes; tptr ; prev_cptr = tptr, tptr = tptr->next) {
+			if (tptr->bb == bbptr) break;
+		}
+		if (!tptr) {
+			PPRINTK(("Can't find module in /proc/gcov\n"));
+			UP();
+			return;
+		}
+		if (prev_cptr)
+			prev_cptr->next = tptr->next;
+		else
+			leave_nodes = tptr->next;
+		dumpall_cached_node = NULL;
+
+
+		/* Find highest level node without further siblings */
+	
+		parent = tptr->parent;
+		do {
+			if (parent->files->sibling != NULL) break;
+			tptr = parent;
+			parent = parent->parent;
+		} while (parent);
+		cleanup_node(tptr,0,1);
+
+		/* Update the offsets at which a certain node can
+		   be found in the tracefile.  */
+		for (tptr = leave_nodes; tptr; tptr = tptr->next) {
+			tptr->offset = offset; 
+			offset += dump_size(tptr);
+		}
+		break;
+
+	case 1:
+		/* insert node */
+		create_node_tree(bbptr);
+
+		/* Update the offsets at which a certain node can
+		   be found in the tracefile.  */
+		for (tptr = leave_nodes; tptr; tptr = tptr->next) {
+			tptr->offset = offset; 
+			offset += dump_size(tptr);
+		}
+
+		break;
+	}
+	UP();
+}
+
+
+/* Initialize the data structure by calling the constructor code
+   of all instrumented object files and creating the proc
+   file-system entries.  */
+
+int 
+init_module(void)
+{
+	struct bb *bbptr;
+	unsigned long offset = 0;
+	struct gcov_ftree_node *tptr; 
+
+	PPRINTK(("init module <%s>\n\n", GCOV_PROF_PROC));
+
+	do_global_ctors(NULL, NULL, NULL, 0);
+	
+	tree_root.proc[0] = proc_mkdir(GCOV_PROF_PROC, 0);
+	kernel_path_len = strlen(gcov_kernelpath);
+
+	for (bbptr = bb_head; bbptr ; bbptr = bbptr->next) {
+		create_node_tree(bbptr);
+	}
+
+	/* Fill in the offset at which a certain node can
+	   be found in the tracefile.  */
+	for (tptr = leave_nodes; tptr; tptr = tptr->next) {
+		tptr->offset = offset; 
+		offset += dump_size(tptr);
+	}
+
+	proc_vmlinux = create_proc_entry("vmlinux",S_IWUSR | S_IRUGO, 
+					 tree_root.proc[0]);
+	if (proc_vmlinux)
+		proc_vmlinux->proc_fops = &proc_gcov_operations;
+
+	gcov_callback = gcov_cleanup;
+	UP();
+	return 0;
+}
+
+
+void 
+cleanup_module(void)
+{
+	PPRINTK(("remove module <%s>\n\n", GCOV_PROF_PROC));
+	gcov_callback = NULL;
+	DOWN();
+	cleanup_node(&tree_root,0,0); 
+}
+
+//module_init(gcov_init_module);
+//module_exit(gcov_cleanup_module);
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/include/linux/module.h 280-gcov/include/linux/module.h
--- 279-no_numa_pc/include/linux/module.h	2004-02-04 16:24:33.000000000 -0800
+++ 280-gcov/include/linux/module.h	2004-02-18 16:23:03.000000000 -0800
@@ -263,6 +263,11 @@ struct module
 	/* The command line arguments (may be mangled).  People like
 	   keeping pointers to this stuff */
 	char *args;
+
+#ifdef CONFIG_GCOV_PROFILE
+	const char *ctors_start;        /* Pointer to start of .ctors-section */
+	const char *ctors_end;          /* Pointer to end of .ctors-section */
+#endif
 };
 
 /* FIXME: It'd be nice to isolate modules during init, too, so they
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/init/main.c 280-gcov/init/main.c
--- 279-no_numa_pc/init/main.c	2004-02-18 16:19:31.000000000 -0800
+++ 280-gcov/init/main.c	2004-02-18 16:23:03.000000000 -0800
@@ -114,6 +114,10 @@ static char *execute_command;
 /* Setup configured maximum number of CPUs to activate */
 static unsigned int max_cpus = NR_CPUS;
 
+#if defined(CONFIG_GCOV_PROFILE) && (defined(CONFIG_PPC32) || defined(CONFIG_PPC64))
+void __bb_fork_func (void) { }
+#endif
+
 /*
  * Setup routine for controlling SMP activation
  *
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/kernel/Makefile 280-gcov/kernel/Makefile
--- 279-no_numa_pc/kernel/Makefile	2004-02-18 16:19:31.000000000 -0800
+++ 280-gcov/kernel/Makefile	2004-02-18 16:23:03.000000000 -0800
@@ -8,6 +8,12 @@ obj-y     = sched.o fork.o exec_domain.o
 	    signal.o sys.o kmod.o workqueue.o pid.o \
 	    rcupdate.o intermodule.o extable.o params.o posix-timers.o
 
+ifdef CONFIG_GCOV_PROFILE
+obj-y += gcov.o
+export-objs += gcov.o
+CFLAGS_gcov.o := -DGCOV_PATH='"$(TOPDIR)"'
+endif
+
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
 obj-$(CONFIG_SMP) += cpu.o
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/kernel/gcov.c 280-gcov/kernel/gcov.c
--- 279-no_numa_pc/kernel/gcov.c	1969-12-31 16:00:00.000000000 -0800
+++ 280-gcov/kernel/gcov.c	2004-02-18 16:23:03.000000000 -0800
@@ -0,0 +1,158 @@
+/*
+ * Coverage support under Linux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (c) International Business Machines Corp., 2002
+ *
+ * Author: Hubertus Franke <frankeh@us.ibm.com>
+ *         Rajan Ravindran <rajancr@us.ibm.com>
+ *
+ * Modified by <Peter.Oberparleiter@de.ibm.com>
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/completion.h>
+
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+
+struct bb
+{
+  long zero_word;
+  const char *filename;
+  long *counts;
+  long ncounts;
+  struct bb *next;
+  const unsigned long *addresses;
+
+  /* Older GCC's did not emit these fields.  */
+  long nwords;
+  const char **functions;
+  const long *line_nums;
+  const char **filenames;
+  char *flags;
+};
+
+struct bb *bb_head;
+struct module *bb_context_address;
+void (*gcov_callback)(int cmd, struct bb *bbptr) = NULL;
+
+#ifdef GCOV_PATH
+char *gcov_kernelpath = GCOV_PATH;
+#else
+char *gcov_kernelpath = __FILE__;
+#endif
+
+
+void
+__bb_init_func (struct bb *blocks)
+{
+  if (blocks->zero_word)
+    return;
+
+  /* Set up linked list.  */
+  blocks->zero_word = 1;
+
+  /* Store the address of the module of which this object-file is a part
+     of (set in do_global_ctors). */
+  blocks->addresses = (unsigned long *) bb_context_address;
+
+  blocks->next = bb_head;
+  bb_head = blocks;
+
+  if (gcov_callback && bb_context_address) 
+    (*gcov_callback)(1,blocks);
+}
+
+/* Call constructors for all kernel objects and dynamic modules. This function
+ * is called both during module initialization and when the gcov kernel
+ * module is insmod'ed. The list of constructors is compiled into the
+ * kernel at &__CTOR_LIST__ to &__DTOR_LIST__ (labels are defined in
+ * head.S). In the case of a dynamic module the list is located at
+ * ctors_start to ctors_end.
+ *
+ * The constructors in turn call __bb_init_func, reporting the respective
+ * struct bb for each object file.
+ */
+
+void
+do_global_ctors (char *ctors_start, char *ctors_end, struct module *addr, int mod_flag)
+{
+  extern char __CTOR_LIST__;
+  extern char __DTOR_LIST__;
+  typedef void (*func_ptr)(void) ;
+  func_ptr *constructor_ptr=NULL;
+ 
+  if (!mod_flag) {
+    /* Set start and end ptr from global kernel constructor list. */
+    ctors_start = &__CTOR_LIST__;
+    ctors_end = &__DTOR_LIST__;
+    bb_context_address = NULL;
+  } else {
+    /* Set context to current module address. */
+    bb_context_address = addr;
+  }
+
+  if (!ctors_start)
+    return;
+
+  /* Call all constructor functions until either the end of the
+     list is reached or until a NULL is encountered. */
+  for (constructor_ptr = (func_ptr *) ctors_start;
+       (constructor_ptr != (func_ptr *) ctors_end) &&
+         (*constructor_ptr != NULL);
+       constructor_ptr++) {
+    	(*constructor_ptr) ();
+  }
+}        
+
+
+/* When a module is unloaded, this function is called to remove
+ * the respective bb entries from our list. context specifies
+ * the address of the module that is unloaded. */
+
+void
+remove_bb_link (struct module *context)
+{
+  struct bb *bbptr;
+  struct bb *prev = NULL;
+
+  /* search for all the module's bbptrs */
+  for (bbptr = bb_head; bbptr ; bbptr = bbptr->next) {
+    if (bbptr->addresses == (unsigned long *) context) {
+      if (gcov_callback)
+        (*gcov_callback)(0,bbptr);
+      if (prev == NULL) 
+        bb_head = bbptr->next;
+      else
+        prev->next = bbptr->next;
+    }
+    else
+      prev = bbptr;
+  }
+}
+
+EXPORT_SYMBOL(bb_head);
+EXPORT_SYMBOL(__bb_init_func);
+EXPORT_SYMBOL(do_global_ctors);
+EXPORT_SYMBOL(gcov_kernelpath);
+EXPORT_SYMBOL(gcov_callback);
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/kernel/module.c 280-gcov/kernel/module.c
--- 279-no_numa_pc/kernel/module.c	2004-02-18 14:57:23.000000000 -0800
+++ 280-gcov/kernel/module.c	2004-02-18 16:23:03.000000000 -0800
@@ -83,6 +83,11 @@ int unregister_module_notifier(struct no
 }
 EXPORT_SYMBOL(unregister_module_notifier);
 
+#ifdef CONFIG_GCOV_PROFILE
+extern void remove_bb_link (struct module *);
+extern void do_global_ctors (char *, char *, struct module *, int);
+#endif
+
 /* We require a truly strong try_module_get() */
 static inline int strong_try_module_get(struct module *mod)
 {
@@ -1087,6 +1092,11 @@ static void free_module(struct module *m
 	/* Arch-specific cleanup. */
 	module_arch_cleanup(mod);
 
+#ifdef CONFIG_GCOV_PROFILE
+	if (mod->ctors_start && mod->ctors_end)
+		remove_bb_link(mod);
+#endif
+
 	/* Module unload stuff */
 	module_unload_free(mod);
 
@@ -1588,6 +1598,13 @@ static struct module *load_module(void _
 	/* Module has been moved. */
 	mod = (void *)sechdrs[modindex].sh_addr;
 
+#ifdef CONFIG_GCOV_PROFILE
+	modindex = find_sec(hdr, sechdrs, secstrings, ".ctors");
+	mod->ctors_start = (char *)sechdrs[modindex].sh_addr;
+	mod->ctors_end   = (char *)(mod->ctors_start +
+				sechdrs[modindex].sh_size);
+#endif
+
 	/* Now we've moved module, initialize linked lists, etc. */
 	module_unload_init(mod);
 
@@ -1755,6 +1772,12 @@ sys_init_module(void __user *umod,
 
 	/* Start the module */
 	ret = mod->init();
+
+#ifdef CONFIG_GCOV_PROFILE
+	if (mod->ctors_start && mod->ctors_end) {
+		do_global_ctors(mod->ctors_start, mod->ctors_end, mod, 1);
+	}
+#endif
 	if (ret < 0) {
 		/* Init routine failed: abort.  Try to protect us from
                    buggy refcounters. */
diff -purN -X /home/mbligh/.diff.exclude 279-no_numa_pc/scripts/Makefile.build 280-gcov/scripts/Makefile.build
--- 279-no_numa_pc/scripts/Makefile.build	2003-10-14 15:50:40.000000000 -0700
+++ 280-gcov/scripts/Makefile.build	2004-02-18 16:23:03.000000000 -0800
@@ -128,7 +128,16 @@ cmd_cc_i_c       = $(CPP) $(c_flags)   -
 quiet_cmd_cc_o_c = CC $(quiet_modtag)  $@
 
 ifndef CONFIG_MODVERSIONS
-cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+new1_c_flags = $(c_flags:-I%=-I$(TOPDIR)/%)
+new2_c_flags = $(new1_c_flags:-Wp%=)
+PWD = $(TOPDIR)
+
+quiet_cmd_cc_o_c = CC $(quiet_modtag)  $@
+cmd_cc_o_c = $(CC) $(c_flags) -E -o $@ $< \
+		&& cd $(dir $<) \
+		&& $(CC) $(new2_c_flags) -c -o $(notdir $@) $(notdir $<) \
+		&& cd $(TOPDIR)
+#cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
 
 else
 # When module versioning is enabled the following steps are executed:
@@ -143,12 +152,21 @@ else
 #   replace the unresolved symbols __crc_exported_symbol with
 #   the actual value of the checksum generated by genksyms
 
-cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
+new1_c_flags = $(c_flags:-I%=-I$(TOPDIR)/%)
+new2_c_flags = $(new1_c_flags:-Wp%=)
+PWD = $(TOPDIR)
+
+quiet_cmd_cc_o_c = CC $(quiet_modtag)  $@
+cmd_cc_o_c = $(CC) $(c_flags) -E -o $@ $< \
+		&& cd $(dir $<) \
+		&& $(CC) $(new2_c_flags) -c -o .tmp_$(@F) $(notdir $<) \
+		&& cd $(TOPDIR)
+#cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
 cmd_modversions =							\
 	if ! $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then	\
 		mv $(@D)/.tmp_$(@F) $@;					\
 	else								\
-		$(CPP) -D__GENKSYMS__ $(c_flags) $<			\
+		$(CPP) -D__GENKSYMS__ $(new2_c_flags) $<		\
 		| $(GENKSYMS)						\
 		> $(@D)/.tmp_$(@F:.o=.ver);				\
 									\