From: Ashok k. <ash...@gm...> - 2007-01-09 15:49:28
|
Hi, i have downloaded linux kernel 2.4.21 and gcov patch for the same version. i applied patch. I am sure,there is no error in applying patch. i compiled the kernel and i booted with new gcov image. but i am not able to load any other modules(any usb related modules only i checked), because of unresolved symbol " __b_init_func" this symbol __b_init_func is introduced by gcov-2.4.21 patch while installing gcov-proc.o or any other modules, i am getting this following error. /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: unresolved symbol __bb_init_func /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov- proc.o: insmod /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o failed /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: insmod gcov-proc failed i did grep on System.map file, it is giving following output c013b2c0 T __bb_init_func c0495812 R __kstrtab___bb_init_func c04a2638 R __ksymtab___bb_init_func and i did "cat /proc/ksym | grep __b_init " , it is giving following output c013b2c0 __bb_init_func_Re16fd186 i am facing this problem on gcov-2.4.20.patch also i have attached the patch here, please have a look and tell me what could be the problem ------------------------------------------------------------------------------------------------------------------------------------- # Makefile | 15 # arch/i386/config.in | 10 # arch/i386/kernel/head.S | 23 + # arch/ppc/boot/chrp/main.c | 10 # arch/ppc/config.in | 11 # arch/ppc/kernel/entry.S | 9 # arch/ppc/kernel/head.S | 22 + # arch/ppc/kernel/prom_init.c | 4 # arch/ppc64/config.in | 10 # arch/ppc64/kernel/head.S | 21 + # arch/s390/config.in | 9 # arch/s390/kernel/head.S | 20 + # arch/s390x/config.in | 9 # arch/s390x/kernel/head.S | 20 + # arch/x86_64/config.in | 10 # arch/x86_64/kernel/head.S | 21 + # drivers/Makefile | 2 # drivers/gcov/Makefile | 12 # drivers/gcov/gcov-proc.c | 723 ++++++++++++++++++++++++++++++++++++++++++++ # include/linux/module.h | 2 # kernel/Makefile | 6 # kernel/gcov.c | 163 +++++++++ # kernel/module.c | 23 + # 23 files changed, 1151 insertions(+), 4 deletions(-) diff -Naurp linux-2.4.21/Makefile linux-2.4.21-gcov/Makefile --- linux-2.4.21/Makefile Fri Jun 13 09:51:39 2003 +++ linux-2.4.21-gcov/Makefile Wed Jun 25 17:42:29 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 21 -EXTRAVERSION = +EXTRAVERSION = -gcov KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -19,6 +19,7 @@ FINDHPATH = $(HPATH)/asm $(HPATH)/linux HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +GCOV_FLAGS = -fprofile-arcs -ftest-coverage CROSS_COMPILE = # @@ -317,7 +318,11 @@ include/config/MARKER: scripts/split-inc linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS)) $(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER +ifdef CONFIG_GCOV_ALL + $(MAKE) CFLAGS="$(CFLAGS) $(GCOV_FLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@) +else $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@) +endif $(TOPDIR)/include/linux/version.h: include/linux/version.h $(TOPDIR)/include/linux/compile.h: include/linux/compile.h @@ -390,7 +395,11 @@ modules: $(patsubst %, _mod_%, $(SUBDIRS .PHONY: $(patsubst %, _mod_%, $(SUBDIRS)) $(patsubst %, _mod_%, $(SUBDIRS)) : include/linux/version.h include/config/MARKER +ifdef CONFIG_GCOV_ALL + $(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(GCOV_FLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules +else $(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules +endif .PHONY: modules_install modules_install: _modinst_ $(patsubst %, _modinst_%, $(SUBDIRS)) _modinst_post @@ -442,7 +451,7 @@ modules modules_install: dummy endif clean: archclean - find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' \) -type f -print \ + find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' -o -name '*.bb' -o -name '*.bbg' \) -type f -print \ | grep -v lxdialog/ | xargs rm -f rm -f $(CLEAN_FILES) rm -rf $(CLEAN_DIRS) @@ -458,7 +467,7 @@ distclean: mrproper rm -f core `find . \( -not -type d \) -and \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ - -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -type f -print` TAGS tags + -o -name '.*.rej' -o -name '.SUMS' -o -name '*.bb' -o -name '*.bbg' -o -size 0 \) -type f -print` TAGS tags backup: mrproper cd .. && tar cf - linux/ | gzip -9 > backup.gz diff -Naurp linux-2.4.21/arch/i386/config.in linux-2.4.21-gcov /arch/i386/config.in --- linux-2.4.21/arch/i386/config.in Fri Jun 13 09:51:29 2003 +++ linux-2.4.21-gcov/arch/i386/config.in Wed Jun 25 17:42:29 2003 @@ -484,4 +484,14 @@ fi endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/i386/kernel/head.S linux-2.4.21-gcov /arch/i386/kernel/head.S --- linux-2.4.21/arch/i386/kernel/head.S Fri Jun 13 09:51:29 2003 +++ linux-2.4.21-gcov/arch/i386/kernel/head.S Wed Jun 25 17:42:29 2003 @@ -446,3 +446,26 @@ ENTRY(gdt_table) .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + + + #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 -Naurp linux-2.4.21 /arch/ppc/boot/chrp/main.c linux-2.4.21-gcov /arch/ppc/boot/chrp/main.c --- linux-2.4.21/arch/ppc/boot/chrp/main.c Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/boot/chrp/main.c Wed Jun 25 17:42:29 2003 @@ -37,12 +37,20 @@ char *avail_high; #define RAM_FREE ((unsigned long)(_end+0x1000)&~0xFFF) #define PROG_START 0x00010000 +#ifdef CONFIG_GCOV_ALL +#define PROG_SIZE 0x00900000 /* 9MB */ +#else #define PROG_SIZE 0x00400000 /* 4MB */ +#endif #define SCRATCH_SIZE (128 << 10) static char scratch[SCRATCH_SIZE]; /* 1MB of scratch space for gunzip */ +#ifdef CONFIG_GCOV_ALL +void __bb_init_func (void *ptr /* struct bb *blocks */) { } +#endif + void chrpboot(int a1, int a2, void *prom) { @@ -78,7 +86,7 @@ chrpboot(int a1, int a2, void *prom) begin_avail = avail_high = avail_ram; end_avail = scratch + sizeof(scratch); printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len); - gunzip(dst, 0x400000, im, &len); + gunzip(dst, PROG_SIZE - PROG_START, im, &len); printf("done %u bytes\n\r", len); printf("%u bytes of heap consumed, max in use %u\n\r", avail_high - begin_avail, heap_max); diff -Naurp linux-2.4.21/arch/ppc/config.in linux-2.4.21-gcov /arch/ppc/config.in --- linux-2.4.21/arch/ppc/config.in Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/config.in Wed Jun 25 17:42:29 2003 @@ -389,6 +389,17 @@ source drivers/media/Config.in source fs/Config.in + +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu + mainmenu_option next_comment comment 'Sound' tristate 'Sound card support' CONFIG_SOUND diff -Naurp linux-2.4.21/arch/ppc/kernel/entry.S linux-2.4.21-gcov /arch/ppc/kernel/entry.S --- linux-2.4.21/arch/ppc/kernel/entry.S Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/kernel/entry.S Wed Jun 25 17:42:29 2003 @@ -263,8 +263,17 @@ ret_from_fork: bl schedule_tail lwz r0,TASK_PTRACE(r2) andi. r0,r0,PT_TRACESYS +#ifdef CONFIG_GCOV_PROFILE + bnel- syscall_trace_from_fork + b ret_from_fork_to_except +syscall_trace_from_fork: + b syscall_trace +ret_from_fork_to_except: + b ret_from_except +#else bnel- syscall_trace b ret_from_except +#endif .globl ret_from_intercept ret_from_intercept: diff -Naurp linux-2.4.21/arch/ppc/kernel/head.S linux-2.4.21-gcov /arch/ppc/kernel/head.S --- linux-2.4.21/arch/ppc/kernel/head.S Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/kernel/head.S Wed Jun 25 17:42:29 2003 @@ -1726,3 +1726,25 @@ intercept_table: abatron_pteptrs: .space 8 #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 -Naurp linux-2.4.21/arch/ppc/kernel/prom_init.c linux-2.4.21-gcov /arch/ppc/kernel/prom_init.c --- linux-2.4.21/arch/ppc/kernel/prom_init.c Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/kernel/prom_init.c Wed Jun 25 17:42:29 2003 @@ -661,7 +661,11 @@ prom_instantiate_rtas(void) * Actually OF has bugs so we just arbitrarily * use memory at the 6MB point. */ +#ifdef CONFIG_GCOV_ALL + rtas_data = 0x990000; +#else rtas_data = 6 << 20; +#endif prom_print(" at "); prom_print_hex(rtas_data); } diff -Naurp linux-2.4.21/arch/ppc64/config.in linux-2.4.21-gcov /arch/ppc64/config.in --- linux-2.4.21/arch/ppc64/config.in Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc64/config.in Wed Jun 25 17:42:29 2003 @@ -255,3 +255,13 @@ if [ "$CONFIG_DUMP" = "y" -o "$CONFIG_DU dep_bool ' LKCD GZIP compression' CONFIG_DUMP_COMPRESS_GZIP $CONFIG_DUMP fi endmenu + +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu diff -Naurp linux-2.4.21/arch/ppc64/kernel/head.S linux-2.4.21-gcov /arch/ppc64/kernel/head.S --- linux-2.4.21/arch/ppc64/kernel/head.S Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc64/kernel/head.S Wed Jun 25 17:42:29 2003 @@ -2111,3 +2111,24 @@ hardware_int_paca0: .globl stab_array stab_array: .space 4096 * (48 - 1) + +#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 -Naurp linux-2.4.21/arch/s390/config.in linux-2.4.21-gcov /arch/s390/config.in --- linux-2.4.21/arch/s390/config.in Thu Nov 28 17:53:11 2002 +++ linux-2.4.21-gcov/arch/s390/config.in Wed Jun 25 17:42:29 2003 @@ -75,4 +75,13 @@ comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/s390/kernel/head.S linux-2.4.21-gcov /arch/s390/kernel/head.S --- linux-2.4.21/arch/s390/kernel/head.S Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/s390/kernel/head.S Wed Jun 25 17:42:30 2003 @@ -671,3 +671,23 @@ _stext: basr %r13,0 .Lstart: .long start_kernel .Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +#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 -Naurp linux-2.4.21/arch/s390x/config.in linux-2.4.21-gcov /arch/s390x/config.in --- linux-2.4.21/arch/s390x/config.in Thu Nov 28 17:53:11 2002 +++ linux-2.4.21-gcov/arch/s390x/config.in Wed Jun 25 17:42:30 2003 @@ -79,4 +79,13 @@ comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/s390x/kernel/head.S linux-2.4.21-gcov /arch/s390x/kernel/head.S --- linux-2.4.21 /arch/s390x/kernel/head.S Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/s390x/kernel/head.S Wed Jun 25 17:42:30 2003 @@ -677,3 +677,23 @@ _stext: basr %r13,0 .Ldw: .quad 0x0002000180000000,0x0000000000000000 .Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +#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 -Naurp linux-2.4.21/arch/x86_64/config.in linux-2.4.21-gcov /arch/x86_64/config.in --- linux-2.4.21/arch/x86_64/config.in Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/x86_64/config.in Wed Jun 25 17:42:30 2003 @@ -249,4 +249,14 @@ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; th fi endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/x86_64/kernel/head.S linux-2.4.21-gcov /arch/x86_64/kernel/head.S --- linux-2.4.21/arch/x86_64/kernel/head.S Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/x86_64/kernel/head.S Wed Jun 25 17:42:30 2003 @@ -365,3 +365,24 @@ ENTRY(idt_table) .quad 0 .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 -Naurp linux-2.4.21/drivers/Makefile linux-2.4.21-gcov/drivers/Makefile --- linux-2.4.21/drivers/Makefile Thu Nov 28 17:53:12 2002 +++ linux-2.4.21-gcov /drivers/Makefile Wed Jun 25 17:42:30 2003 @@ -49,4 +49,6 @@ subdir-$(CONFIG_ACPI) += acpi subdir-$(CONFIG_BLUEZ) += bluetooth +subdir-$(CONFIG_GCOV_PROC) += gcov + include $(TOPDIR)/Rules.make diff -Naurp linux-2.4.21/drivers/gcov/Makefile linux-2.4.21-gcov /drivers/gcov/Makefile --- linux-2.4.21/drivers/gcov/Makefile Wed Dec 31 18:00:00 1969 +++ linux-2.4.21-gcov/drivers/gcov/Makefile Wed Jun 25 17:42:30 2003 @@ -0,0 +1,12 @@ +# +# Makefile for GCOV profiling kernel module +# + +export-objs := gcov-proc.o + +obj-$(CONFIG_GCOV_PROC) += gcov-proc.o + +include $(TOPDIR)/Rules.make + +gcov-proc.o: gcov-proc.c + diff -Naurp linux-2.4.21/drivers/gcov/gcov-proc.c linux-2.4.21-gcov /drivers/gcov/gcov-proc.c --- linux-2.4.21/drivers/gcov/gcov-proc.c Wed Dec 31 18:00:00 1969 +++ linux-2.4.21-gcov /drivers/gcov/gcov-proc.c Wed Jun 25 17:45:11 2003 @@ -0,0 +1,723 @@ +/* + * 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 <fr...@us...> + * + * 2002-06-21 Modified by Pet...@de...: + * - now uses copy_to_user() instead of memcpy() to + * copy from kernel to user space + * - corrected initialization of gcov_ftree_node->offset value + * - implemented partial write in read_gcov() + * - added some comments + * - introduced "gcov_type" instead of "long" for counter type + * (needed for kernels compiled with gcc versions >= 3.1) + * - corrected offset error when reading out counter values + * - gcov_ftree_mode->offset is now adjusted each time + * a module is (un)loaded + * - renamed gcov.c to gcov-proc.c to prevent confusion with + * kernel/gcov.c and gcc/gcov.c + */ + +#if CONFIG_MODVERSIONS==1 +#define MODVERSIONS +#include <linux/modversions.h> +#endif + + +#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> + +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) + + +/* ################################################################### + # 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; + struct inode *inode; + int dumpall; + unsigned int hdrofs; + unsigned long poffs; + + MOD_INC_USE_COUNT; + DOWN(); + + read = 0; + hdrofs = 0; + poffs = 0; + inode = file->f_dentry->d_inode; + de = (struct proc_dir_entry *) inode->u.generic_ip; + + /* 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(); + MOD_DEC_USE_COUNT; + 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; + struct inode *inode; + int resetall, i; + struct gcov_ftree_node *tptr; + + MOD_INC_USE_COUNT; + DOWN(); + + inode = file->f_dentry->d_inode; + de = (struct proc_dir_entry *) inode->u.generic_ip; + 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(); + MOD_DEC_USE_COUNT; + 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() +{ + 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() +{ + PPRINTK(("remove module <%s>\n\n", GCOV_PROF_PROC)); + gcov_callback = NULL; + DOWN(); + cleanup_node(&tree_root,0,0); +} diff -Naurp linux-2.4.21/include/linux/module.h linux-2.4.21-gcov /include/linux/module.h --- linux-2.4.21/include/linux/module.h Fri Aug 2 19:39:45 2002 +++ linux-2.4.21-gcov/include/linux/module.h Wed Jun 25 17:42:30 2003 @@ -90,6 +90,8 @@ struct module const char *archdata_start; /* arch specific data for module */ const char *archdata_end; const char *kernel_data; /* Reserved for kernel internal use */ + const char *ctors_start; /* Pointer to start of .ctors-section */ + const char *ctors_end; /* Pointer to end of .ctors-section */ }; struct module_info diff -Naurp linux-2.4.21/kernel/Makefile linux-2.4.21-gcov/kernel/Makefile --- linux-2.4.21/kernel/Makefile Sun Sep 16 23:22:40 2001 +++ linux-2.4.21-gcov/kernel/Makefile Wed Jun 25 17:42:30 2003 @@ -16,6 +16,12 @@ obj-y = sched.o dma.o fork.o exec_do sysctl.o acct.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o context.o +ifdef CONFIG_GCOV_PROFILE +obj-y += gcov.o +export-objs += gcov.o +CFLAGS_gcov.o := -DGCOV_PATH='"$(TOPDIR)"' +endif + obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o diff -Naurp linux-2.4.21/kernel/gcov.c linux-2.4.21-gcov/kernel/gcov.c --- linux-2.4.21/kernel/gcov.c Wed Dec 31 18:00:00 1969 +++ linux-2.4.21-gcov/kernel/gcov.c Wed Jun 25 17:42:30 2003 @@ -0,0 +1,163 @@ +/* + * 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 <fr...@us...> + * + * 2002-04-24 Modified by Pet...@de...: + * The kernel constructor list is now identified by two labels + * __CTOR_LIST__ and __DTOR_LIST__ instead of a trailing NULL. + * More than one constructor in dynamic modules are now + * supported by iterating all of the .ctors-section, + * remove_bb_link was adjusted accordingly to remove all + * struct bb entries of a module in bb_head. + */ + +#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 -Naurp linux-2.4.21/kernel/module.c linux-2.4.21-gcov/kernel/module.c --- linux-2.4.21/kernel/module.c Fri Jun 13 09:51:39 2003 +++ linux-2.4.21-gcov/kernel/module.c Wed Jun 25 17:42:30 2003 @@ -57,6 +57,10 @@ struct module *module_list = &kernel_mod #endif /* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */ +extern void remove_bb_link (struct module *); +extern void do_global_ctors (char *, char *, struct module *, int); + + /* inter_module functions are always available, even when the kernel is * compiled without modules. Consumers of inter_module_xxx routines * will always work, even when both are built into the kernel, this @@ -498,6 +502,18 @@ sys_init_module(const char *name_user, s goto err3; } +#ifdef CONFIG_GCOV_PROFILE + /* In case this module was insmodded using a modified version of + insmod, call the module constructors to register the instrumentation + data with the internal gcov structure. */ + if (mod_member_present(mod, ctors_start) && + mod_member_present(mod, ctors_end) && + mod->ctors_start && + mod->ctors_end) { + do_global_ctors(mod->ctors_start, mod->ctors_end, mod, 1); + } +#endif + if (module_arch_init(mod)) goto err3; @@ -616,6 +632,13 @@ sys_delete_module(const char *name_user) if (mod->refs != NULL) goto out; +#ifdef CONFIG_GCOV_PROFILE + /* Remove entry for this module in internal gcov structure. */ + if (mod_member_present(mod, ctors_start) && + mod_member_present(mod, ctors_end)) { + remove_bb_link(mod); + } +#endif spin_lock(&unload_lock); if (!__MOD_IN_USE(mod)) { mod->flags |= MOD_DELETED; --------------------------------------------------------------------------------------------------------------------------------------------------------------- thanks Ashok |
From: Ashok k. <ash...@gm...> - 2007-01-11 03:58:32
|
---------- Forwarded message ---------- From: Ashok kumar <ash...@gm...> Date: Jan 9, 2007 9:19 PM Subject: unresolved symbol problem on GCOV To: ltp...@li... Hi, i have downloaded linux kernel 2.4.21 and gcov patch for the same version. i applied patch. I am sure,there is no error in applying patch. i compiled the kernel and i booted with new gcov image. but i am not able to load any other modules(any usb related modules only i checked), because of unresolved symbol " __b_init_func" this symbol __b_init_func is introduced by gcov-2.4.21 patch while installing gcov-proc.o or any other modules, i am getting this following error. /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: unresolved symbol __bb_init_func /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov- proc.o: insmod /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o failed /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: insmod gcov-proc failed i did grep on System.map file, it is giving following output c013b2c0 T __bb_init_func c0495812 R __kstrtab___bb_init_func c04a2638 R __ksymtab___bb_init_func and i did "cat /proc/ksym | grep __b_init " , it is giving following output c013b2c0 __bb_init_func_Re16fd186 i am facing this problem on gcov-2.4.20.patch also thanks Ashok |
From: Peter O. <obe...@go...> - 2007-01-11 08:19:47
|
Ashok kumar wrote: > while installing gcov-proc.o or any other modules, i am getting this > following error. > /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: unresolved > symbol __bb_init_func That version of the gcov-kernel patch does not work if CONFIG_MODVERSIONS=y is specified. Try recompiling your kernel with CONFIG_MODVERSIONS=n. Alternatively you can change all occurrences of EXPORT_SYMBOL in the gcov-kernel patch to EXPORT_SYMBOL_NOVERS, though other problems may occur at a later stage. Regards, Peter Oberparleiter |
From: Ashok k. <ash...@gm...> - 2007-01-12 15:15:25
|
On 1/11/07, Peter Oberparleiter <obe...@go...> wrote: > > Ashok kumar wrote: > > while installing gcov-proc.o or any other modules, i am getting this > > following error. > > /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: unresolved > > symbol __bb_init_func > > That version of the gcov-kernel patch does not work if > CONFIG_MODVERSIONS=y is specified. Try recompiling your kernel with > CONFIG_MODVERSIONS=n. > > Alternatively you can change all occurrences of EXPORT_SYMBOL in the > gcov-kernel patch to EXPORT_SYMBOL_NOVERS, though other problems may occur > at a later stage. yes, this really helps me. thanks Ashok Regards, > Peter Oberparleiter > |
From: Ashok k. <ash...@gm...> - 2007-01-11 03:56:07
|
Hi, i have downloaded linux kernel 2.4.21 and gcov patch for the same version. i applied patch. I am sure,there is no error in applying patch. i compiled the kernel and i booted with new gcov image. but i am not able to load any other modules(any usb related modules only i checked), because of unresolved symbol " __b_init_func" this symbol __b_init_func is introduced by gcov-2.4.21 patch while installing gcov-proc.o or any other modules, i am getting this following error. /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: unresolved symbol __bb_init_func /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov- proc.o: insmod /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o failed /lib/modules/2.4.21-gcov/kernel/drivers/gcov/gcov-proc.o: insmod gcov-proc failed i did grep on System.map file, it is giving following output c013b2c0 T __bb_init_func c0495812 R __kstrtab___bb_init_func c04a2638 R __ksymtab___bb_init_func and i did "cat /proc/ksym | grep __b_init " , it is giving following output c013b2c0 __bb_init_func_Re16fd186 i am facing this problem on gcov-2.4.20.patch also i have attached the patch here, please have a look and tell me what could be the problem ------------------------------------------------------------------------------------------------------------------------------------- # Makefile | 15 # arch/i386/config.in | 10 # arch/i386/kernel/head.S | 23 + # arch/ppc/boot/chrp/main.c | 10 # arch/ppc/config.in | 11 # arch/ppc/kernel/entry.S | 9 # arch/ppc/kernel/head.S | 22 + # arch/ppc/kernel/prom_init.c | 4 # arch/ppc64/config.in | 10 # arch/ppc64/kernel/head.S | 21 + # arch/s390/config.in | 9 # arch/s390/kernel/head.S | 20 + # arch/s390x/config.in | 9 # arch/s390x/kernel/head.S | 20 + # arch/x86_64/config.in | 10 # arch/x86_64/kernel/head.S | 21 + # drivers/Makefile | 2 # drivers/gcov/Makefile | 12 # drivers/gcov/gcov-proc.c | 723 ++++++++++++++++++++++++++++++++++++++++++++ # include/linux/module.h | 2 # kernel/Makefile | 6 # kernel/gcov.c | 163 +++++++++ # kernel/module.c | 23 + # 23 files changed, 1151 insertions(+), 4 deletions(-) diff -Naurp linux-2.4.21/Makefile linux-2.4.21-gcov/Makefile --- linux-2.4.21/Makefile Fri Jun 13 09:51:39 2003 +++ linux-2.4.21-gcov/Makefile Wed Jun 25 17:42:29 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 21 -EXTRAVERSION = +EXTRAVERSION = -gcov KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -19,6 +19,7 @@ FINDHPATH = $(HPATH)/asm $(HPATH)/linux HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +GCOV_FLAGS = -fprofile-arcs -ftest-coverage CROSS_COMPILE = # @@ -317,7 +318,11 @@ include/config/MARKER: scripts/split-inc linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS)) $(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER +ifdef CONFIG_GCOV_ALL + $(MAKE) CFLAGS="$(CFLAGS) $(GCOV_FLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@) +else $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@) +endif $(TOPDIR)/include/linux/version.h: include/linux/version.h $(TOPDIR)/include/linux/compile.h: include/linux/compile.h @@ -390,7 +395,11 @@ modules: $(patsubst %, _mod_%, $(SUBDIRS .PHONY: $(patsubst %, _mod_%, $(SUBDIRS)) $(patsubst %, _mod_%, $(SUBDIRS)) : include/linux/version.h include/config/MARKER +ifdef CONFIG_GCOV_ALL + $(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(GCOV_FLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules +else $(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules +endif .PHONY: modules_install modules_install: _modinst_ $(patsubst %, _modinst_%, $(SUBDIRS)) _modinst_post @@ -442,7 +451,7 @@ modules modules_install: dummy endif clean: archclean - find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' \) -type f -print \ + find . \( -name '*.[oas]' -o -name core -o -name '.*.flags' -o -name '*.bb' -o -name '*.bbg' \) -type f -print \ | grep -v lxdialog/ | xargs rm -f rm -f $(CLEAN_FILES) rm -rf $(CLEAN_DIRS) @@ -458,7 +467,7 @@ distclean: mrproper rm -f core `find . \( -not -type d \) -and \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ - -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -type f -print` TAGS tags + -o -name '.*.rej' -o -name '.SUMS' -o -name '*.bb' -o -name '*.bbg' -o -size 0 \) -type f -print` TAGS tags backup: mrproper cd .. && tar cf - linux/ | gzip -9 > backup.gz diff -Naurp linux-2.4.21/arch/i386/config.in linux-2.4.21-gcov /arch/i386/config.in --- linux-2.4.21/arch/i386/config.in Fri Jun 13 09:51:29 2003 +++ linux-2.4.21-gcov/arch/i386/config.in Wed Jun 25 17:42:29 2003 @@ -484,4 +484,14 @@ fi endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/i386/kernel/head.S linux-2.4.21-gcov /arch/i386/kernel/head.S --- linux-2.4.21/arch/i386/kernel/head.S Fri Jun 13 09:51:29 2003 +++ linux-2.4.21-gcov/arch/i386/kernel/head.S Wed Jun 25 17:42:29 2003 @@ -446,3 +446,26 @@ ENTRY(gdt_table) .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + + + #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 -Naurp linux-2.4.21 /arch/ppc/boot/chrp/main.c linux-2.4.21-gcov /arch/ppc/boot/chrp/main.c --- linux-2.4.21/arch/ppc/boot/chrp/main.c Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/boot/chrp/main.c Wed Jun 25 17:42:29 2003 @@ -37,12 +37,20 @@ char *avail_high; #define RAM_FREE ((unsigned long)(_end+0x1000)&~0xFFF) #define PROG_START 0x00010000 +#ifdef CONFIG_GCOV_ALL +#define PROG_SIZE 0x00900000 /* 9MB */ +#else #define PROG_SIZE 0x00400000 /* 4MB */ +#endif #define SCRATCH_SIZE (128 << 10) static char scratch[SCRATCH_SIZE]; /* 1MB of scratch space for gunzip */ +#ifdef CONFIG_GCOV_ALL +void __bb_init_func (void *ptr /* struct bb *blocks */) { } +#endif + void chrpboot(int a1, int a2, void *prom) { @@ -78,7 +86,7 @@ chrpboot(int a1, int a2, void *prom) begin_avail = avail_high = avail_ram; end_avail = scratch + sizeof(scratch); printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len); - gunzip(dst, 0x400000, im, &len); + gunzip(dst, PROG_SIZE - PROG_START, im, &len); printf("done %u bytes\n\r", len); printf("%u bytes of heap consumed, max in use %u\n\r", avail_high - begin_avail, heap_max); diff -Naurp linux-2.4.21/arch/ppc/config.in linux-2.4.21-gcov /arch/ppc/config.in --- linux-2.4.21/arch/ppc/config.in Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/config.in Wed Jun 25 17:42:29 2003 @@ -389,6 +389,17 @@ source drivers/media/Config.in source fs/Config.in + +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu + mainmenu_option next_comment comment 'Sound' tristate 'Sound card support' CONFIG_SOUND diff -Naurp linux-2.4.21/arch/ppc/kernel/entry.S linux-2.4.21-gcov /arch/ppc/kernel/entry.S --- linux-2.4.21/arch/ppc/kernel/entry.S Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/kernel/entry.S Wed Jun 25 17:42:29 2003 @@ -263,8 +263,17 @@ ret_from_fork: bl schedule_tail lwz r0,TASK_PTRACE(r2) andi. r0,r0,PT_TRACESYS +#ifdef CONFIG_GCOV_PROFILE + bnel- syscall_trace_from_fork + b ret_from_fork_to_except +syscall_trace_from_fork: + b syscall_trace +ret_from_fork_to_except: + b ret_from_except +#else bnel- syscall_trace b ret_from_except +#endif .globl ret_from_intercept ret_from_intercept: diff -Naurp linux-2.4.21/arch/ppc/kernel/head.S linux-2.4.21-gcov /arch/ppc/kernel/head.S --- linux-2.4.21/arch/ppc/kernel/head.S Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/kernel/head.S Wed Jun 25 17:42:29 2003 @@ -1726,3 +1726,25 @@ intercept_table: abatron_pteptrs: .space 8 #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 -Naurp linux-2.4.21/arch/ppc/kernel/prom_init.c linux-2.4.21-gcov /arch/ppc/kernel/prom_init.c --- linux-2.4.21/arch/ppc/kernel/prom_init.c Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc/kernel/prom_init.c Wed Jun 25 17:42:29 2003 @@ -661,7 +661,11 @@ prom_instantiate_rtas(void) * Actually OF has bugs so we just arbitrarily * use memory at the 6MB point. */ +#ifdef CONFIG_GCOV_ALL + rtas_data = 0x990000; +#else rtas_data = 6 << 20; +#endif prom_print(" at "); prom_print_hex(rtas_data); } diff -Naurp linux-2.4.21/arch/ppc64/config.in linux-2.4.21-gcov /arch/ppc64/config.in --- linux-2.4.21/arch/ppc64/config.in Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc64/config.in Wed Jun 25 17:42:29 2003 @@ -255,3 +255,13 @@ if [ "$CONFIG_DUMP" = "y" -o "$CONFIG_DU dep_bool ' LKCD GZIP compression' CONFIG_DUMP_COMPRESS_GZIP $CONFIG_DUMP fi endmenu + +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu diff -Naurp linux-2.4.21/arch/ppc64/kernel/head.S linux-2.4.21-gcov /arch/ppc64/kernel/head.S --- linux-2.4.21/arch/ppc64/kernel/head.S Fri Jun 13 09:51:31 2003 +++ linux-2.4.21-gcov/arch/ppc64/kernel/head.S Wed Jun 25 17:42:29 2003 @@ -2111,3 +2111,24 @@ hardware_int_paca0: .globl stab_array stab_array: .space 4096 * (48 - 1) + +#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 -Naurp linux-2.4.21/arch/s390/config.in linux-2.4.21-gcov /arch/s390/config.in --- linux-2.4.21/arch/s390/config.in Thu Nov 28 17:53:11 2002 +++ linux-2.4.21-gcov/arch/s390/config.in Wed Jun 25 17:42:29 2003 @@ -75,4 +75,13 @@ comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/s390/kernel/head.S linux-2.4.21-gcov /arch/s390/kernel/head.S --- linux-2.4.21 /arch/s390/kernel/head.S Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/s390/kernel/head.S Wed Jun 25 17:42:30 2003 @@ -671,3 +671,23 @@ _stext: basr %r13,0 .Lstart: .long start_kernel .Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +#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 -Naurp linux-2.4.21/arch/s390x/config.in linux-2.4.21-gcov /arch/s390x/config.in --- linux-2.4.21/arch/s390x/config.in Thu Nov 28 17:53:11 2002 +++ linux-2.4.21-gcov/arch/s390x/config.in Wed Jun 25 17:42:30 2003 @@ -79,4 +79,13 @@ comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/s390x/kernel/head.S linux-2.4.21-gcov /arch/s390x/kernel/head.S --- linux-2.4.21 /arch/s390x/kernel/head.S Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/s390x/kernel/head.S Wed Jun 25 17:42:30 2003 @@ -677,3 +677,23 @@ _stext: basr %r13,0 .Ldw: .quad 0x0002000180000000,0x0000000000000000 .Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +#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 -Naurp linux-2.4.21/arch/x86_64/config.in linux-2.4.21-gcov /arch/x86_64/config.in --- linux-2.4.21/arch/x86_64/config.in Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/x86_64/config.in Wed Jun 25 17:42:30 2003 @@ -249,4 +249,14 @@ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; th fi endmenu +mainmenu_option next_comment +comment 'GCOV coverage profiling' + +bool 'GCOV kernel' CONFIG_GCOV_PROFILE +if [ "$CONFIG_GCOV_PROFILE" != "n" ]; then + bool ' GCOV kernel profiler' CONFIG_GCOV_ALL + tristate ' gcov-proc module' CONFIG_GCOV_PROC +fi +endmenu + source lib/Config.in diff -Naurp linux-2.4.21/arch/x86_64/kernel/head.S linux-2.4.21-gcov /arch/x86_64/kernel/head.S --- linux-2.4.21/arch/x86_64/kernel/head.S Fri Jun 13 09:51:32 2003 +++ linux-2.4.21-gcov/arch/x86_64/kernel/head.S Wed Jun 25 17:42:30 2003 @@ -365,3 +365,24 @@ ENTRY(idt_table) .quad 0 .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 -Naurp linux-2.4.21/drivers/Makefile linux-2.4.21-gcov/drivers/Makefile --- linux-2.4.21/drivers/Makefile Thu Nov 28 17:53:12 2002 +++ linux-2.4.21-gcov /drivers/Makefile Wed Jun 25 17:42:30 2003 @@ -49,4 +49,6 @@ subdir-$(CONFIG_ACPI) += acpi subdir-$(CONFIG_BLUEZ) += bluetooth +subdir-$(CONFIG_GCOV_PROC) += gcov + include $(TOPDIR)/Rules.make diff -Naurp linux-2.4.21/drivers/gcov/Makefile linux-2.4.21-gcov /drivers/gcov/Makefile --- linux-2.4.21/drivers/gcov/Makefile Wed Dec 31 18:00:00 1969 +++ linux-2.4.21-gcov/drivers/gcov/Makefile Wed Jun 25 17:42:30 2003 @@ -0,0 +1,12 @@ +# +# Makefile for GCOV profiling kernel module +# + +export-objs := gcov-proc.o + +obj-$(CONFIG_GCOV_PROC) += gcov-proc.o + +include $(TOPDIR)/Rules.make + +gcov-proc.o: gcov-proc.c + diff -Naurp linux-2.4.21/drivers/gcov/gcov-proc.c linux-2.4.21-gcov /drivers/gcov/gcov-proc.c --- linux-2.4.21/drivers/gcov/gcov-proc.c Wed Dec 31 18:00:00 1969 +++ linux-2.4.21-gcov /drivers/gcov/gcov-proc.c Wed Jun 25 17:45:11 2003 @@ -0,0 +1,723 @@ +/* + * 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 < fr...@us...> + * + * 2002-06-21 Modified by Pet...@de...: + * - now uses copy_to_user() instead of memcpy() to + * copy from kernel to user space + * - corrected initialization of gcov_ftree_node->offset value + * - implemented partial write in read_gcov() + * - added some comments + * - introduced "gcov_type" instead of "long" for counter type + * (needed for kernels compiled with gcc versions >= 3.1) + * - corrected offset error when reading out counter values + * - gcov_ftree_mode->offset is now adjusted each time + * a module is (un)loaded + * - renamed gcov.c to gcov-proc.c to prevent confusion with + * kernel/gcov.c and gcc/gcov.c + */ + +#if CONFIG_MODVERSIONS==1 +#define MODVERSIONS +#include <linux/modversions.h> +#endif + + +#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> + +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) + + +/* ################################################################### + # 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; + struct inode *inode; + int dumpall; + unsigned int hdrofs; + unsigned long poffs; + + MOD_INC_USE_COUNT; + DOWN(); + + read = 0; + hdrofs = 0; + poffs = 0; + inode = file->f_dentry->d_inode; + de = (struct proc_dir_entry *) inode->u.generic_ip; + + /* 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(); + MOD_DEC_USE_COUNT; + 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; + struct inode *inode; + int resetall, i; + struct gcov_ftree_node *tptr; + + MOD_INC_USE_COUNT; + DOWN(); + + inode = file->f_dentry->d_inode; + de = (struct proc_dir_entry *) inode->u.generic_ip; + 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(); + MOD_DEC_USE_COUNT; + 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() +{ + 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() +{ + PPRINTK(("remove module <%s>\n\n", GCOV_PROF_PROC)); + gcov_callback = NULL; + DOWN(); + cleanup_node(&tree_root,0,0); +} diff -Naurp linux-2.4.21/include/linux/module.h linux-2.4.21-gcov /include/linux/module.h --- linux-2.4.21/include/linux/module.h Fri Aug 2 19:39:45 2002 +++ linux-2.4.21-gcov/include/linux/module.h Wed Jun 25 17:42:30 2003 @@ -90,6 +90,8 @@ struct module const char *archdata_start; /* arch specific data for module */ const char *archdata_end; const char *kernel_data; /* Reserved for kernel internal use */ + const char *ctors_start; /* Pointer to start of .ctors-section */ + const char *ctors_end; /* Pointer to end of .ctors-section */ }; struct module_info diff -Naurp linux-2.4.21/kernel/Makefile linux-2.4.21-gcov/kernel/Makefile --- linux-2.4.21/kernel/Makefile Sun Sep 16 23:22:40 2001 +++ linux-2.4.21-gcov/kernel/Makefile Wed Jun 25 17:42:30 2003 @@ -16,6 +16,12 @@ obj-y = sched.o dma.o fork.o exec_do sysctl.o acct.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o context.o +ifdef CONFIG_GCOV_PROFILE +obj-y += gcov.o +export-objs += gcov.o +CFLAGS_gcov.o := -DGCOV_PATH='"$(TOPDIR)"' +endif + obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o diff -Naurp linux-2.4.21/kernel/gcov.c linux-2.4.21-gcov/kernel/gcov.c --- linux-2.4.21/kernel/gcov.c Wed Dec 31 18:00:00 1969 +++ linux-2.4.21-gcov/kernel/gcov.c Wed Jun 25 17:42:30 2003 @@ -0,0 +1,163 @@ +/* + * 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 < fr...@us...> + * + * 2002-04-24 Modified by Pet...@de...: + * The kernel constructor list is now identified by two labels + * __CTOR_LIST__ and __DTOR_LIST__ instead of a trailing NULL. + * More than one constructor in dynamic modules are now + * supported by iterating all of the .ctors-section, + * remove_bb_link was adjusted accordingly to remove all + * struct bb entries of a module in bb_head. + */ + +#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 -Naurp linux-2.4.21/kernel/module.c linux-2.4.21-gcov/kernel/module.c --- linux-2.4.21/kernel/module.c Fri Jun 13 09:51:39 2003 +++ linux-2.4.21-gcov/kernel/module.c Wed Jun 25 17:42:30 2003 @@ -57,6 +57,10 @@ struct module *module_list = &kernel_mod #endif /* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */ +extern void remove_bb_link (struct module *); +extern void do_global_ctors (char *, char *, struct module *, int); + + /* inter_module functions are always available, even when the kernel is * compiled without modules. Consumers of inter_module_xxx routines * will always work, even when both are built into the kernel, this @@ -498,6 +502,18 @@ sys_init_module(const char *name_user, s goto err3; } +#ifdef CONFIG_GCOV_PROFILE + /* In case this module was insmodded using a modified version of + insmod, call the module constructors to register the instrumentation + data with the internal gcov structure. */ + if (mod_member_present(mod, ctors_start) && + mod_member_present(mod, ctors_end) && + mod->ctors_start && + mod->ctors_end) { + do_global_ctors(mod->ctors_start, mod->ctors_end, mod, 1); + } +#endif + if (module_arch_init(mod)) goto err3; @@ -616,6 +632,13 @@ sys_delete_module(const char *name_user) if (mod->refs != NULL) goto out; +#ifdef CONFIG_GCOV_PROFILE + /* Remove entry for this module in internal gcov structure. */ + if (mod_member_present(mod, ctors_start) && + mod_member_present(mod, ctors_end)) { + remove_bb_link(mod); + } +#endif spin_lock(&unload_lock); if (!__MOD_IN_USE(mod)) { mod->flags |= MOD_DELETED; --------------------------------------------------------------------------------------------------------------------------------------------------------------- thanks Ashok |