All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
@ 2009-02-26 15:09 Tej
  2009-02-27 19:02 ` Gianluca Guida
  0 siblings, 1 reply; 8+ messages in thread
From: Tej @ 2009-02-26 15:09 UTC (permalink / raw)
  To: Gianluca Guida; +Cc: George Dunlap, xen-devel, Keir Fraser

[-- Attachment #1: Type: text/plain, Size: 1618 bytes --]

On Thu, Feb 19, 2009 at 8:41 PM, Gianluca Guida
<gianluca.guida@eu.citrix.com> wrote:
>
> On Feb 19, 2009, at 3:53 PM, George Dunlap wrote:
>
>> So I think that it probably would be useful.  Unfortunately, I don't
>> have time in the near future to look at either of these (new "xcov"
>> functionality, or fixing xenoprofile).  Gianluca's been doing some
>> interesting work with testing.  I'll ask him if he's interested in
>> looking at it.
>
> Definitely. Getting code coverage reports, whatever the tools, may help a
> lot when trying to debug a bizarre memory corruption or even just to check
> that a test you're writing does what you actually want.
>
> Keep me in the loop and feel free to ask questions, I'll start reading
> patches and experimenting with it. IMHO, I think that 64bit hypervisor
> support will be by far more useful, since it's the most used version of the
> hypervisor, nowadays.

Sending Patches for the 64 bit Hypervisor, We have tested patches on
AMD-64 (Athlon(tm) 64 X2 Dual Core Processor 4600+) machine with
gcc-4.2 & gcc-3.4. Please refer the previous GCOV RFC in same mail for
more info on Hypervisor profiling.
We have added support to 32bit and 64bit Kernel.

In addition to patches for hypervisor profiling, we did a little work
on *lcov* to work with hypervisor.
README could be useful, for naive lcov user. locv-diff.patch show our
change in lcov scripts.

any comments, feedback and suggestion are more than welcome

P.S. a lcov screenshot with hypervisor profling has been attached.


thanks
-tej & team

>
> Thanks,
> Gianluca
>
>

[-- Attachment #2: xen-3.3-gcov-v2.patch --]
[-- Type: application/octet-stream, Size: 17606 bytes --]

diff -r 5dbe4ef883fa xen/Rules.mk
--- a/xen/Rules.mk	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/Rules.mk	Thu Feb 26 17:07:50 2009 +0530
@@ -8,6 +8,10 @@
 perfc_arrays  ?= n
 crash_debug   ?= n
 frame_pointer ?= n
+
+# xen source path to get update in .gcno files
+xen_src := $(CURDIR)
+xentree := $(if $(xen_src),$(xen_src),$(CURDIR))
 
 XEN_ROOT=$(BASEDIR)/..
 include $(XEN_ROOT)/Config.mk
@@ -56,6 +60,7 @@
 ALL_OBJS-y               += $(BASEDIR)/arch/$(TARGET_ARCH)/built_in.o
 
 CFLAGS-y                += -g -D__XEN__
+CFLAGS-y                += -fprofile-arcs -ftest-coverage
 CFLAGS-$(XSM_ENABLE)    += -DXSM_ENABLE
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_ENABLE -DXSM_MAGIC=0xf97cff8c
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_DEVELOP -DFLASK_BOOTPARAM -DFLASK_AVC_STATS
@@ -117,7 +122,9 @@
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C $* clean
 
 %.o: %.c $(HDRS) Makefile
-	$(CC) $(CFLAGS) -c $< -o $@
+	$(CC) $(CFLAGS) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+
 
 %.o: %.S $(AHDRS) Makefile
 	$(CC) $(AFLAGS) -c $< -o $@
diff -r 5dbe4ef883fa xen/arch/x86/mm/hap/Makefile
--- a/xen/arch/x86/mm/hap/Makefile	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/arch/x86/mm/hap/Makefile	Thu Feb 26 17:07:50 2009 +0530
@@ -4,8 +4,17 @@
 obj-y += guest_walk_4level.o
 obj-y += p2m-ept.o
 
+LN = /bin/ln
+xen_src := $(CURDIR)
+xentree := $(if $(xen_src),$(xen_src),$(CURDIR))
+
 guest_levels  = $(subst level,,$(filter %level,$(subst ., ,$(subst _, ,$(1)))))
 guest_walk_defns = -DGUEST_PAGING_LEVELS=$(call guest_levels,$(1))
 
+num = $(call guest_levels,$(@F))level.c
+
 guest_walk_%level.o: guest_walk.c $(HDRS) Makefile
-	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c $< -o $@
+	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s guest_walk.c guest_walk_$(num)
+
diff -r 5dbe4ef883fa xen/arch/x86/mm/shadow/Makefile
--- a/xen/arch/x86/mm/shadow/Makefile	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/arch/x86/mm/shadow/Makefile	Thu Feb 26 17:07:50 2009 +0530
@@ -1,5 +1,10 @@
 obj-$(x86_32) += common.o guest_2.o guest_3.o
 obj-$(x86_64) += common.o guest_2.o guest_3.o guest_4.o
 
+LN = /bin/ln
+num=$*.c
+
 guest_%.o: multi.c $(HDRS) Makefile
-	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
+	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s multi.c guest_$(num)
diff -r 5dbe4ef883fa xen/arch/x86/x86_32/entry.S
--- a/xen/arch/x86/x86_32/entry.S	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/arch/x86/x86_32/entry.S	Thu Feb 26 17:07:50 2009 +0530
@@ -703,6 +703,7 @@
         .long do_sysctl             /* 35 */
         .long do_domctl
         .long do_kexec_op
+        .long do_gcovprof_op
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/4)
         .long do_ni_hypercall
         .endr
@@ -750,6 +751,7 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec_op          */
+        .byte 3 /* do_gcovprof_op	*/
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
         .endr
diff -r 5dbe4ef883fa xen/arch/x86/x86_64/entry.S
--- a/xen/arch/x86/x86_64/entry.S	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/arch/x86/x86_64/entry.S	Thu Feb 26 17:07:50 2009 +0530
@@ -692,6 +692,7 @@
         .quad do_sysctl             /* 35 */
         .quad do_domctl
         .quad do_kexec_op
+        .quad do_gcovprof_op
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
         .quad do_ni_hypercall
         .endr
@@ -739,6 +740,7 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec             */
+        .byte 3 /* do_gcovprof_op       */
         .byte 1 /* do_xsm_op            */
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
diff -r 5dbe4ef883fa xen/common/Makefile
--- a/xen/common/Makefile	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/common/Makefile	Thu Feb 26 17:07:50 2009 +0530
@@ -27,6 +27,7 @@
 obj-y += vsprintf.o
 obj-y += xmalloc.o
 obj-y += rcupdate.o
+obj-y += xen-gcov-core.o
 
 obj-$(perfc)       += perfc.o
 obj-$(crash_debug) += gdbstub.o
@@ -42,6 +43,8 @@
 
 subdir-y += libelf
 
+CFLAGS += -DSRC_PATH='"$(BASEDIR)"'
+
 # Object file contains changeset and compiler information.
 version.o: $(BASEDIR)/include/xen/compile.h
 
diff -r 5dbe4ef883fa xen/common/xen-gcov-core.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/common/xen-gcov-core.c	Thu Feb 26 17:07:50 2009 +0530
@@ -0,0 +1,242 @@
+/**
+ *  xen-gcov-core.c: arch dependent code for hypervisor profiling
+ *
+ *  Written by tej parkash and team(tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <xen/init.h>
+#include <xen/kernel.h>
+#include <xen/guest_access.h>
+#include <xen/sched.h>
+#include <xen/event.h>
+#include <xen/string.h>
+#include <public/xen-gcov.h>
+#include <xen/paging.h>
+#include <xsm/xsm.h>
+#include <xen/config.h>
+#include <xen/lib.h>
+
+
+#define XEN_GCOV_CORE 	"xen-gcov-core:"
+
+static int num_xen_files;
+gcov_unsigned_t gcov_version = 0;
+struct gcov_info *bb_head;
+
+struct ctor_list {
+	unsigned long num;
+	ctor_t ctor[];
+};
+extern struct ctor_list __CTOR_LIST__;
+
+
+static inline int
+counter_active(struct gcov_info *bb, unsigned int type)
+{
+        return (1 << type) & bb->ctr_mask;
+}
+
+static inline unsigned int
+get_fn_stride(struct gcov_info *bb)
+{
+        unsigned int stride;
+
+        stride = sizeof(struct gcov_fn_info) + sizeof(unsigned int);
+        if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int)) {
+                stride += __alignof__(struct gcov_fn_info) - 1;
+                stride &= ~(__alignof__(struct gcov_fn_info) - 1);
+        }
+        return stride;
+}
+
+static inline struct gcov_fn_info *
+get_fn_info(struct gcov_info *bb, unsigned int func)
+{
+        return (struct gcov_fn_info *)
+                ((char *)bb->functions + func * get_fn_stride(bb));
+}
+
+static void reset_gcda(struct gcov_info *bb)
+{
+
+        unsigned int i;
+        struct gcov_ctr_info *ctr;
+        ctr = bb->counts;
+        for (i=0; i < GCOV_COUNTERS; i++)
+                if (counter_active(bb, i)) {
+                        memset(ctr->values, 0, ctr->num * sizeof(gcov_type));
+                        ctr++;
+                }
+}
+
+/* Function to reset the bb counter. */
+static int reset_bb(XEN_GUEST_HANDLE(void) arg, int val)
+{
+        struct gcov_info *tmp=bb_head;
+	char bb_file[100];
+        if ( val == 0){
+		/* reset single data file */
+        	if ( copy_from_guest(&bb_file, arg, 1) )
+                	return -EFAULT;
+                for(;tmp!=NULL;tmp=tmp->next){
+                        if(!strcmp(tmp->filename,bb_file)){
+                                reset_gcda(tmp);
+                                break;
+                        }
+                        else
+                                continue;
+                }
+        }
+        else {
+		/* reset all data files */
+                for(;tmp!=NULL;tmp=tmp->next)
+                        reset_gcda(tmp);
+        }
+	return 0;
+}
+
+
+/*copy bb_head info */
+
+static int start_prof(XEN_GUEST_HANDLE(void) arg, int val)
+{
+	/*Add the code to copy the no of files to be add to xen */
+	struct xen_gcov_info bb_tmp;
+	struct gcov_info *tmp = bb_head;
+	struct gcov_fn_info *func;
+	struct gcov_ctr_info *ctr;
+	struct xen_gcov_ctr_info  *xctr;
+	struct xen_gcov_info *bb = &bb_tmp;
+        int i,j;
+
+        if ( copy_from_guest(&bb_tmp, arg, 1) )
+	        return -EFAULT;
+        for(;tmp!=NULL&&bb!=NULL;tmp=tmp->next){
+		ctr = tmp->counts;		
+		xctr = bb->counts;	
+		memcpy(bb->counts[0].values, ctr->values, ctr->num*sizeof(gcov_type));
+		xctr->num = ctr->num;
+		xctr->merge = ctr->merge;
+		bb->version  	= tmp->version;
+		bb->stamp    	= tmp->stamp;
+		safe_strcpy(bb->file, tmp->filename);
+                bb->ctr_mask    = tmp->ctr_mask;
+		bb->n_functions	= tmp->n_functions;
+		for(i=0;i<tmp->n_functions;i++) {
+			func = get_fn_info(tmp,i);
+			bb->functions[i].ident=func->ident;
+			bb->functions[i].checksum=func->checksum;
+				for (j=0;j<GCOV_COUNTERS;j++) {
+						bb->functions[i].n_ctrs[j]=func->n_ctrs[j];
+				}
+		}
+		/*increment the bb_head pointers*/
+	        bb=bb->next;
+        }
+	if ( copy_to_guest(arg, &bb_tmp, 1) )
+        	return -EFAULT;
+        return 0;
+}
+
+
+static int var_info(XEN_GUEST_HANDLE(void) arg,int val)
+{
+	struct gcov_variables gcov_var;
+        struct gcov_variables *tmp=&gcov_var;
+	struct gcov_info *bb;
+	int i;
+	if( copy_from_guest(&gcov_var, arg, 1) )
+		return -EFAULT;
+	if ( val == 0 ) {
+		safe_strcpy(tmp->src_path,SRC_PATH);	
+		tmp->g_version=gcov_version;
+		tmp->n_files=num_xen_files;
+	} else {
+		for(i=0,bb=bb_head;bb!=NULL;bb=bb->next,i++){
+			tmp->ctr_num[i]=bb->counts->num;
+			tmp->n_funcs[i]=bb->n_functions;
+		}
+	}
+
+	if( copy_to_guest(arg, &gcov_var, 1) )
+		return 	-EFAULT;
+	return 0;
+}
+
+/*internal hyercall functions*/
+int do_gcovprof_op(int op, XEN_GUEST_HANDLE(void) arg, int val)
+{
+        int ret = 0;
+        if ((op < 0) || (op > GCOVPROF_last_op)) {
+                printk(KERN_ERR XEN_GCOV_CORE "Invalid operation %d\n", op);
+                return -EINVAL;
+        }
+        switch (op) {
+        case GCOVPROF_seek_vars:
+                /*fun seeks vars to be used by proc code */
+                ret = var_info(arg, val);
+                break;
+        case GCOVPROF_start:
+                /*update linux kernel with profiling info */
+                ret = start_prof(arg,val);
+		break;
+        case GCOVPROF_reset:
+                /*reset the gcda or vmlinux*/
+                ret = reset_bb(arg,val);
+                break;
+        default:
+                ret = -ENOSYS;
+        }
+        return ret;
+}
+
+void do_global_ctors(ctor_t ctor[], unsigned long num)
+{
+	unsigned long i;
+
+	/* Call constructors and create gcov_info linked list*/
+	for (i = 0; i < num && ctor[i]; i++)
+		ctor[i] ();
+	num_xen_files = i-1;
+}
+
+ /*Register supplied struct BB. Called by each object code constructor. */
+void __gcov_init(struct gcov_info *bb)
+{
+	if (!bb->version)
+		return;
+	/*Check for compatible gcc version */
+	if (gcov_version == 0)
+		gcov_version = bb->version;
+	else if (bb->version != gcov_version) {
+		printk(KERN_WARNING XEN_GCOV_CORE "gcc version mismatch in "
+		       "file '%s'!\n", bb->filename);
+		return;
+	}
+	/*Set up linked list */
+	bb->version = 0;
+	bb->next = bb_head;
+	bb_head = bb;
+}
+
+static int __init gcov_init(void)
+{
+	do_global_ctors(__CTOR_LIST__.ctor, __CTOR_LIST__.num);
+	printk(KERN_INFO XEN_GCOV_CORE "init done\n");
+	return 0;
+}
+
+/* Unused functions needed to prevent linker errors. */
+void __gcov_flush(void) {}
+void __gcov_merge_add(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_single(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_delta(gcov_type * counters, unsigned int n_counters){}
+
+EXPORT_SYMBOL(__gcov_init);
+EXPORT_SYMBOL(do_global_ctors);
+EXPORT_SYMBOL(__gcov_flush);
+EXPORT_SYMBOL(__gcov_merge_add);
+EXPORT_SYMBOL(__gcov_merge_single);
+EXPORT_SYMBOL(__gcov_merge_delta);
+__initcall(gcov_init);
diff -r 5dbe4ef883fa xen/include/public/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/public/xen-gcov.h	Thu Feb 26 17:07:50 2009 +0530
@@ -0,0 +1,139 @@
+/******************************************************************************
+ * xen-gcov.h
+ * 
+ * Interface for enabling and populating hypervisor profiling info.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: tej parkash <tej.parkash@hcl.in>
+ * Written by tej parkash and team
+ *
+ */
+/********************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_seek_vars           	0
+#define GCOVPROF_start		      	1
+#define GCOVPROF_reset		      	2
+#define GCOVPROF_last_op          	3
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS       		5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*ctor_t)(void);
+
+struct gcov_fn_info{
+    gcov_unsigned_t ident;    /* unique ident of function */
+    gcov_unsigned_t checksum; /* function checksum */
+    unsigned n_ctrs[0];       /* instrumented counters */
+};
+
+struct gcov_ctr_info
+{
+    gcov_unsigned_t num;      /* number of counters.  */
+    gcov_type *values;        /* their values.  */
+    gcov_merge_fn merge;      /* The function used to merge them.  */
+};
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+    gcov_unsigned_t version;  /* expected version number */
+    struct gcov_info *next;   /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    /* uniquifying time stamp */
+    const char *filename;
+
+    unsigned n_functions;     /* number of functions */
+    struct gcov_fn_info *functions; /* table of functions */
+
+    unsigned ctr_mask;        /* mask of counters instrumented.  */
+    struct gcov_ctr_info counts[0]; /* count data. The number of bits
+                     set in the ctr_mask field
+                     determines how big this array
+                     is.  */
+};
+
+
+struct xen_gcov_fn_info{
+    gcov_unsigned_t ident;    		/* unique ident of function */
+    gcov_unsigned_t checksum; 		/* function checksum */
+    unsigned int n_ctrs[GCOV_COUNTERS];	/* instrumented counters */
+};
+typedef struct xen_gcov_fn_info xen_gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_fn_info_t);
+
+
+
+struct xen_gcov_ctr_info
+{
+    gcov_unsigned_t num;      /* number of counters.  */
+    gcov_type *values;        /* their values.  */
+    gcov_merge_fn merge;      /* The function used to merge them.  */
+};
+typedef struct  xen_gcov_ctr_info xen_gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct xen_gcov_info
+{
+    gcov_unsigned_t version;  		/* expected version number */
+    struct xen_gcov_info *next;   	/* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    		/* uniquifying time stamp */
+    char file[100];
+
+    unsigned int n_functions;     	/* number of functions */
+    struct xen_gcov_fn_info *functions; 	/* table of functions */
+
+    unsigned int ctr_mask;        	/* mask of counters instrumented.  */
+    struct xen_gcov_ctr_info counts[GCOV_COUNTERS]; /* count data.*/
+};
+typedef struct xen_gcov_info xen_gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_info_t);
+
+struct gcov_variables
+{
+    char src_path[100];
+    unsigned long g_version;
+    unsigned int n_files;
+    gcov_unsigned_t *ctr_num;
+    unsigned int *n_funcs;     
+};
+typedef struct gcov_variables gcov_variables_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_variables_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r 5dbe4ef883fa xen/include/xen/config.h
--- a/xen/include/xen/config.h	Wed Feb 25 16:04:11 2009 +0530
+++ b/xen/include/xen/config.h	Thu Feb 26 17:07:50 2009 +0530
@@ -96,4 +96,20 @@
 #define __cpuinitdata
 #define __cpuinit
 
+#define SORT(x)        x
+#define CONSTRUCTORS                           \
+       __CTOR_LIST__ = .;                  \
+       LONG((__CTOR_END__ - __CTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       __CTOR_LIST2__ = .;                 \
+       *(SORT(.ctors))                     \
+       LONG(0)                         \
+       __CTOR_END__ = .;                   \
+       __DTOR_LIST__ = .;                  \
+       LONG((__DTOR_END__ - __DTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       *(SORT(.dtors))                     \
+       LONG(0)                         \
+       __DTOR_END__ = .;
+
 #endif /* __XEN_CONFIG_H__ */

[-- Attachment #3: linux-2.6.18-gcov-v2.patch --]
[-- Type: application/octet-stream, Size: 36865 bytes --]

diff -r 51decc39e5e7 drivers/xen/Kconfig
--- a/drivers/xen/Kconfig	Wed Jan 28 13:42:09 2009 +0000
+++ b/drivers/xen/Kconfig	Thu Feb 26 17:04:07 2009 +0530
@@ -263,6 +263,11 @@
 	default y
 	help
 	  Xen hypervisor attributes will show up under /sys/hypervisor/.
+config XEN_GCOV_PROC
+        tristate "Code covarage for hypervisor"
+        help
+         Select to profile Xen Hypervisor only. Create /proc/xen/gcov entry
+         which provides access to the current code coverage state of xen hypervisor
 
 choice
 	prompt "Xen version compatibility"
diff -r 51decc39e5e7 drivers/xen/Makefile
--- a/drivers/xen/Makefile	Wed Jan 28 13:42:09 2009 +0000
+++ b/drivers/xen/Makefile	Thu Feb 26 17:04:07 2009 +0530
@@ -3,6 +3,7 @@
 obj-y	+= evtchn/
 obj-y	+= xenbus/
 obj-y	+= char/
+obj-y	+= gcov/
 
 obj-y	+= util.o
 obj-$(CONFIG_XEN_BALLOON)		+= balloon/
diff -r 51decc39e5e7 drivers/xen/core/xen_proc.c
--- a/drivers/xen/core/xen_proc.c	Wed Jan 28 13:42:09 2009 +0000
+++ b/drivers/xen/core/xen_proc.c	Thu Feb 26 17:04:07 2009 +0530
@@ -3,7 +3,7 @@
 #include <linux/proc_fs.h>
 #include <xen/xen_proc.h>
 
-static struct proc_dir_entry *xen_base;
+struct proc_dir_entry *xen_base;
 
 struct proc_dir_entry *create_xen_proc_entry(const char *name, mode_t mode)
 {
@@ -20,4 +20,5 @@
 	remove_proc_entry(name, xen_base);
 }
 
+EXPORT_SYMBOL(xen_base);
 EXPORT_SYMBOL_GPL(remove_xen_proc_entry); 
diff -r 51decc39e5e7 drivers/xen/gcov/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/Makefile	Thu Feb 26 17:04:07 2009 +0530
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_GCOV_PROC) := xen-gcov-proc.o
diff -r 51decc39e5e7 drivers/xen/gcov/xen-gcov-proc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/xen-gcov-proc.c	Thu Feb 26 17:04:07 2009 +0530
@@ -0,0 +1,1054 @@
+/**
+ *  @file xen-gcov-proc.c
+ *
+ *  @Author: Hubertus Franke <frankeh@us.ibm.com>
+ *           Rajan Ravindran <rajancr@us.ibm.com>
+ *           Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
+ *           Paul Larson
+ *
+ *  Modified by tej parkash and team for Xen (tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/xen-gcov.h>
+#include <xen/evtchn.h>
+#include <xen/xen_proc.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+
+MODULE_LICENSE("GPL");
+
+#define GCOV_PROC_HEADER	"xen-gcov-proc: "
+#define GCOV_PROC_ROOT		"gcov"
+#define GCOV_PROC_VMLINUX	"vmlinux"
+#define PAD8(x)			(((x) + 7) & ~7)
+
+/* If set to non-zero, create links to additional files in proc filesystem
+ * entries. */
+static int xen_gcov_link = 1;
+
+/*count update frequency*/
+static int xen_gcov_update = 5;
+
+#ifdef MODULE
+/* Parameter handling. */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+/* module_param is a 2.6 thing (though there are compat macros since 2.4.25) */
+MODULE_PARM(xen_gcov_link, "i");
+MODULE_PARM(xen_gcov_update, "i");
+#else
+module_param(xen_gcov_link, int, 0400);
+module_param(xen_gcov_update, int, 0400);
+#endif				/* LINUX_VERSION_CODE */
+
+MODULE_PARM_DESC(xen_gcov_link, "If set to non-zero, create links to additional "
+		 "files in proc filesystem entries");
+MODULE_PARM_DESC(xen_gcov_update, "decides the frequency of hypervisor count"
+		 "update and default value is 5sec.");
+#else				/* MODULE */
+/* Called when 'xen_gcov_link' is specified on the kernel command line. */
+static int __init xen_gcov_link_setup(char *str)
+{
+	xen_gcov_link = (int)simple_strtol(str, NULL, 10);
+	return 1;
+}
+
+__setup("xen_gcov_link=", xen_gcov_link_setup);
+
+static int __init xen_gcov_update_setup(char *str)
+{
+	xen_gcov_update = (int)simple_strtol(str, NULL, 10);
+	return 1;
+}
+
+__setup("xen_gcov_update=", xen_gcov_update_setup);
+
+#endif				/* MODULE */
+
+/* create sysclt entry @/proc/sys/xen
+ * accomplish the count update frequency
+ * perform: echo <num> >/proc/sys/xen/xen_gcov_update
+ */
+#ifdef CONFIG_SYSCTL
+static ctl_table gcov_table[] = {
+	{
+	 	.ctl_name 	= 4,
+	 	.procname 	= "xen_gcov_update",
+	 	.data 		= &xen_gcov_update,
+	 	.maxlen 	= sizeof(xen_gcov_update),
+	 	.mode 		= 0644,
+	 	.proc_handler 	= proc_dointvec,
+	 }
+	,
+	{.ctl_name = 0}
+};
+
+static ctl_table gcov_root_table[] = {
+	{
+	 	.ctl_name 	= 123,
+	 	.procname 	= "xen",
+	 	.mode 		= 0555,
+	 	.child 		= gcov_table},
+	{.ctl_name = 0}
+};
+
+/*sysclt table specific declaration*/
+static struct ctl_table_header *gcov_table_header;
+
+#endif /* CONFIG_SYSCTL */
+
+/* Protect global variables from concurrent access. */
+static DEFINE_MUTEX(gcov_mutex);
+static DEFINE_MUTEX(write_mutex);
+
+/*workqueue function*/
+static void xeno_prof(void *);
+
+/*workqueue for the calling hypervisor count head*/
+static DECLARE_WORK(work, xeno_prof, NULL);
+
+/* Filesystem entry for /proc/gcov/vmlinux */
+static struct proc_dir_entry *proc_vmlinux = NULL;
+
+/* First leaf node. */
+static struct gcov_ftree_node *leaf_nodes = NULL;
+
+/* Cached node used to speed up sequential reads in /proc/vmlinux. */
+static struct gcov_ftree_node *cached_node = NULL;
+
+/* Root node for internal data tree. */
+static struct gcov_ftree_node tree_root;
+
+struct gcov_variables *gcov_var = NULL;
+
+/* Filename extension for data files. */
+static const char *da_ending = "gcda";
+
+/* Array of filename endings to use when creating links. */
+static const char *endings[] = { "gcno", "c" };
+
+/*Info which keeps no if file in xen info to be profile*/
+unsigned int xen_file_nos;
+
+/*head of xen-gcov-info linked list*/
+struct xen_gcov_info *list_head = NULL;
+
+/* xen_sourcepath length */
+static int sourcepath_len;
+
+unsigned long xen_gcov_version;
+
+/* Retrieve proc_dir_entry associated with INODE. */
+/*static struct proc_dir_entry *PDE(const struct inode *inode)
+{
+	return ((struct proc_dir_entry *)inode->u.generic_ip);
+}*/
+
+
+/* Create nodes for all gcda files based on xen_file_nos value */
+static inline int create_bb_list(void)
+{
+	int i;
+	struct xen_gcov_info *node = NULL, *tmp_node = NULL;
+
+	for (i = 0; i < gcov_var->n_files; i++) {
+		node = kmalloc(sizeof(struct xen_gcov_info), GFP_KERNEL);
+		node->counts[0].values =
+		    kmalloc(sizeof(gcov_type) * gcov_var->ctr_num[i],
+			    GFP_KERNEL);
+		node->functions =
+		    kmalloc(sizeof(struct xen_gcov_fn_info) *
+			    gcov_var->n_funcs[i], GFP_KERNEL);
+		node->next = NULL;
+		if (list_head == NULL) {
+			list_head = node;
+			tmp_node = node;
+		} else {
+			tmp_node->next = node;
+			tmp_node = node;
+		}
+	}
+	
+	return 0;
+}
+
+/* free memory allocated for gcov_info list */
+static inline int free_bb_list(void)
+{
+	struct xen_gcov_info *node = NULL;
+	struct xen_gcov_info *temp = list_head;
+        for (;temp!=NULL;temp = temp->next) {
+			node = temp;
+			kfree(node->counts[0].values);
+			kfree(node->functions);
+			kfree(node);
+	}
+	return 0;
+}
+
+/* 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 (adapted from gcc/gcov-io.h). */
+static int store_gcov_type(gcov_type value, char *dest, size_t bytes)
+{
+	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;
+	return 0;
+}
+
+/* Determine whether counter TYPE is active in BB. */
+static int counter_active(struct xen_gcov_info *bb, unsigned int type)
+{
+	return ((1 << type) & bb->ctr_mask);
+}
+
+/* Return size of .gcda counter section. */
+static size_t
+sizeof_counter_data(struct xen_gcov_info *bb, unsigned int row,
+		    unsigned int col)
+{
+
+	struct xen_gcov_fn_info *func =
+	    (struct xen_gcov_fn_info *)&bb->functions[row];
+
+	if (counter_active(bb, col)) {
+		return /* tag */ 4 + /* length */ 4 +
+		    /* counters */ func->n_ctrs[col] * 8;
+	} else
+		return 0;
+}
+
+/* Return size of .gcda data section associated with FUNC.  */
+static size_t sizeof_func_data(struct xen_gcov_info *bb, unsigned int row)
+{
+	size_t result;
+	unsigned int type;
+
+	result =
+	    /* tag */ 4 + /* length */ 4 + /* ident */ 4 + /* checksum */ 4;
+	for (type = 0; type < GCOV_COUNTERS; type++)
+		result += sizeof_counter_data(bb, row, type);
+	return result;
+}
+
+/* Get size of .gcda file associated with BB. */
+static size_t sizeof_da_file(struct xen_gcov_info *bb)
+{
+	size_t result;
+	unsigned int i;
+
+	result = /* magic */ 4 + /* version */ 4 + /* stamp */ 4;
+	for (i = 0; i < bb->n_functions; i++)
+		result += sizeof_func_data(bb, i);
+	return result;
+}
+
+/* Store a 32 bit unsigned integer value in GCOV format to memory at address
+ *  * BUF. */
+static void store_int32(uint32_t i, char *buf)
+{
+	uint32_t *p;
+	p = (int *)buf;
+	*p = i;
+
+}
+
+/* Store a 64 bit unsigned integer value in GCOV format to memory at address
+ *  * BUF. */
+static void store_int64(uint64_t i, char *buf)
+{
+	store_int32((uint32_t) (i & 0xffff), buf);
+	store_int32((uint32_t) (i >> 32), buf + 4);
+}
+
+/* Store a gcov counter in GCOV format to memory at address BUF. The counter is
+ *  * identified by BB, FUNC, TYPE and COUNTER. */
+static void
+store_counter(struct xen_gcov_info *bb, unsigned int func, unsigned int type,
+	      unsigned int counter, char *buf)
+{
+	unsigned int counter_off;
+	unsigned int type_off;
+	unsigned int i;
+	struct xen_gcov_fn_info *func_ptr;
+	/* Get offset into counts array */
+	type_off = 0;
+	for (i = 0; i < type; i++)
+		if (counter_active(bb, i))
+			type_off++;
+	/* Get offset into values array. */
+	counter_off = counter;
+	for (i = 0; i < func; i++) {
+		func_ptr = (struct xen_gcov_fn_info *)&bb->functions[i];
+		counter_off += func_ptr->n_ctrs[type];
+	}
+	/* Create in temporary storage */
+	store_int64(bb->counts[type_off].values[counter_off], buf);
+}
+
+/* Store a counter section in userspace memory. The counter section is
+ *  * identified by BB, FUNC and TYPE. The destination address is BUF. Store at
+ *   * most COUNT bytes beginning at OFFSET. Return the number of bytes stored or a
+ *    * negative value on error. */
+static ssize_t
+store_counter_data(struct xen_gcov_info *bb, unsigned int func,
+		   unsigned int type, char *buf, size_t count, loff_t offset)
+{
+	struct xen_gcov_fn_info *func_ptr;
+	char data[8];
+	char *from;
+	size_t len;
+	ssize_t result;
+	unsigned int i;
+
+	func_ptr = (struct xen_gcov_fn_info *)&bb->functions[func];
+	result = 0;
+	while (count > 0) {
+		if (offset < 4) {
+			/* Tag ID */
+			store_int32((uint32_t) GCOV_TAG_FOR_COUNTER(type),
+				    data);
+			len = 4 - offset;
+			from = data + offset;
+		} else if (offset < 8) {
+			/* Tag length in groups of 4 bytes */
+			store_int32((uint32_t)
+				    func_ptr->n_ctrs[type] * 2, data);
+			len = 4 - (offset - 4);
+			from = data + (offset - 4);
+		} else {
+			/* Actual counter data */
+			i = (offset - 8) / 8;
+			/* Check for EOF */
+			if (i >= func_ptr->n_ctrs[type])
+				break;
+			store_counter(bb, func, type, i, data);
+			len = 8 - (offset - 8) % 8;
+			from = data + (offset - 8) % 8;
+		}
+		if (len > count)
+			len = count;
+		if (copy_to_user(buf, from, len))
+			return -EFAULT;
+		count -= len;
+		buf += len;
+		offset += len;
+		result += len;
+	}
+	return result;
+}
+
+/* Store a function section and associated counter sections in userspace memory.
+ *  * The function section is identified by BB and FUNC. The destination address is
+ *   * BUF. Store at most COUNT bytes beginning at OFFSET. Return the number of
+ *    * bytes stored or a negative value on error. */
+static ssize_t
+store_func_data(struct xen_gcov_info *bb, unsigned int func, char *buf,
+		size_t count, loff_t offset)
+{
+	char data[4];
+	char *from;
+	size_t len;
+	unsigned int i;
+	loff_t off;
+	size_t size;
+	ssize_t result;
+	ssize_t rc;
+	result = 0;
+	while (count > 0) {
+		if (offset < 16) {
+			if (offset < 4) {
+				/* Tag ID */
+				store_int32((uint32_t) GCOV_TAG_FUNCTION, data);
+				len = 4 - offset;
+				from = data + offset;
+			} else if (offset < 8) {
+				/* Tag length */
+				store_int32(2, data);
+				len = 4 - (offset - 4);
+				from = data + (offset - 4);
+			} else if (offset < 12) {
+				/* Function ident */
+				store_int32((uint32_t) bb->
+					    functions[func].ident, data);
+				len = 4 - (offset - 8);
+				from = data + (offset - 8);
+			} else {
+				/* Function checksum */
+				store_int32((uint32_t) bb->
+					    functions[func].checksum, data);
+				len = 4 - (offset - 12);
+				from = data + (offset - 12);
+			}
+			/* Do the actual store */
+			if (len > count)
+				len = count;
+			if (copy_to_user(buf, from, len))
+				return -EFAULT;
+		} else {
+			off = 16;
+			len = 0;
+			for (i = 0; i < GCOV_COUNTERS; i++) {
+				size = sizeof_counter_data(bb, func, i);
+				if (offset < off + size) {
+					rc = store_counter_data(bb, func, i,
+								buf, count,
+								offset - off);
+					if (rc < 0)
+						return rc;
+					len = rc;
+					break;
+				}
+				off += size;
+			}
+			/* Check for EOF */
+			if (i == GCOV_COUNTERS)
+				break;
+		}
+		count -= len;
+		buf += len;
+		offset += len;
+		result += len;
+	}
+	return result;
+}
+
+/* Store data of .gcda file associated with NODE to userspace memory at BUF.
+ *  * OFFSET specifies the offset inside the .da file. COUNT is the maximum
+ *   * number of bytes to store. Return the number of bytes stored, zero for
+ *    * EOF or a negative number in case of error. */
+static ssize_t
+store_da_file(struct gcov_ftree_node *node, char *buf, size_t count,
+	      loff_t offset)
+{
+
+	struct xen_gcov_info *bb;
+	char data[4];
+	char *from;
+	size_t len;
+	unsigned int i;
+	loff_t off;
+	size_t size;
+	ssize_t result;
+	ssize_t rc;
+
+	bb = node->bb;
+	result = 0;
+	while (count > 0) {
+		if (offset < 12) {
+			if (offset < 4) {
+				/* File magic */
+				store_int32((uint32_t) GCOV_DATA_MAGIC, data);
+				len = 4 - offset;
+				from = data + offset;
+			} else if (offset < 8) {
+				/* File format/GCC version */
+				store_int32(xen_gcov_version, data);
+				len = 4 - (offset - 4);
+				from = data + (offset - 4);
+			} else {
+				/* Time stamp */
+				store_int32((uint32_t) bb->stamp, data);
+				len = 4 - (offset - 8);
+				from = data + (offset - 8);
+			}
+			/* Do the actual store */
+			if (len > count)
+				len = count;
+			if (copy_to_user(buf, from, len))
+				return -EFAULT;
+		} else {
+			off = 12;
+			len = 0;
+			for (i = 0; i < bb->n_functions; i++) {
+
+				size = sizeof_func_data(bb, i);
+				if (offset < off + size) {
+					rc = store_func_data(bb, i, buf, count,
+							     offset - off);
+
+					if (rc < 0)
+						return rc;
+					len = rc;
+					break;
+				}
+				off += size;
+			}
+			/* Check for EOF */
+			if (i == bb->n_functions)
+				break;
+		}
+		count -= len;
+		buf += len;
+		offset += len;
+		result += len;
+	}
+	return result;
+}
+
+/* Return size of header which precedes .da file entry associated with BB
+ * in the vmlinux file. */
+static size_t sizeof_vmlinux_header(struct xen_gcov_info *bb)
+{
+	return 8 + PAD8(strlen(bb->file) + 1);
+}
+
+/* Store data of header which precedes .da file entry associated with NODE
+ * in the vmlinux file to userspace memory at BUF. OFFSET specifies the offset
+ * inside the header file. COUNT is the maximum number of bytes to store.
+ * Return the number of bytes stored, zero for EOF or a negative number in
+ * case of error. */
+static ssize_t
+store_vmlinux_header(struct gcov_ftree_node *node, char *buf, size_t count,
+		     loff_t offset)
+{
+	char data[8];
+	char *from;
+	size_t namelen;
+	ssize_t stored;
+	size_t len;
+
+	namelen = strlen(node->bb->file);
+	stored = 0;
+	while (count > 0) {
+		if (offset < 8) {
+			/* Filename length */
+			if (store_gcov_type(PAD8(namelen + 1), data, 8))
+				return -EINVAL;
+			from = data + offset;
+			len = 8 - offset;
+		} else if (offset < 8 + namelen) {
+			/* Filename */
+			from = (char *)node->bb->file + offset - 8;
+			len = namelen - (offset - 8);
+		} else if (offset < node->header_size) {
+			/* Nil byte padding */
+			memset(data, 0, 8);
+			from = data;
+			len = PAD8(namelen + 1) - (offset - 8);
+		} else
+			break;
+		if (len > count)
+			len = count;
+		if (copy_to_user(buf, from, len))
+			return -EFAULT;
+		stored += len;
+		count -= len;
+		offset += len;
+		buf += len;
+	}
+
+	return stored;
+}
+
+/* Update data related to vmlinux file. */
+static void update_vmlinux_data(void)
+{
+	struct gcov_ftree_node *node;
+	loff_t offset;
+
+	offset = 0;
+	for (node = leaf_nodes; node; node = node->next) {
+		node->offset = offset;
+		node->da_size = sizeof_da_file(node->bb);
+		node->header_size = sizeof_vmlinux_header(node->bb);
+		offset += node->header_size + node->da_size;
+	}
+	proc_vmlinux->size = offset;
+	cached_node = NULL;
+}
+
+/* Read .da or vmlinux file. */
+static ssize_t
+read_gcov(struct file *file, char *buf, size_t count, loff_t * pos)
+{
+	struct gcov_ftree_node *node;
+	struct proc_dir_entry *dir_entry;
+	ssize_t rc;
+
+	mutex_lock(&gcov_mutex);
+	dir_entry = PDE(file->f_dentry->d_inode);
+	rc = 0;
+	if (dir_entry == proc_vmlinux) {
+		/* Are we in a sequential read? */
+		if (cached_node && (*pos >= cached_node->offset))
+			node = cached_node;
+		else
+			node = leaf_nodes;
+		/* Find node corresponding to offset */
+		while (node && node->next && (*pos >= node->next->offset))
+			node = node->next;
+		cached_node = node;
+		if (node) {
+			if (*pos - node->offset < node->header_size)
+				rc = store_vmlinux_header(node, buf, count,
+							  *pos - node->offset);
+			else {
+				rc = store_da_file(node, buf, count,
+						   *pos - node->offset -
+						   node->header_size);
+			}
+		}
+	} else {
+		node = (struct gcov_ftree_node *)dir_entry->data;
+		if (node) {
+			rc = store_da_file(node, buf, count, *pos);
+		}
+	}
+	if (rc > 0)
+		*pos += rc;
+	mutex_unlock(&gcov_mutex);
+	return rc;
+}
+
+/* Reset counters on write request. */
+static ssize_t
+write_gcov(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+	struct gcov_ftree_node *node;
+	struct proc_dir_entry *dir_entry;
+
+	mutex_lock(&write_mutex);
+	dir_entry = PDE(file->f_dentry->d_inode);
+	if (dir_entry == proc_vmlinux) {
+		/* Reset all nodes */
+		node = leaf_nodes;
+		if (HYPERVISOR_gcovprof_op(GCOVPROF_reset, NULL, !RESET_GCDA)) {
+			printk(KERN_ERR GCOV_PROC_HEADER
+			       "Failed to reset the vmlinux\n");
+			mutex_unlock(&write_mutex);
+			return EIO;
+		}
+
+	} else {
+		node = (struct gcov_ftree_node *)dir_entry->data;
+		if (HYPERVISOR_gcovprof_op
+		    (GCOVPROF_reset, node->bb->file, RESET_GCDA)) {
+			printk(KERN_ERR GCOV_PROC_HEADER
+			       "Failed to reset file %s\n", node->bb->file);
+			mutex_unlock(&write_mutex);
+			return EIO;
+		}
+	}
+	mutex_unlock(&write_mutex);
+	return count;
+}
+
+/* Return a newly allocated copy of STRING. */
+static char *strdup(const char *string)
+{
+	char *result;
+
+	result = (char *)kmalloc(strlen(string) + 1, GFP_KERNEL);
+	if (result)
+		strcpy(result, string);
+	return result;
+}
+
+/* Allocate a new node and fill in NAME and BB. */
+static struct gcov_ftree_node *alloc_node(const char *name,
+					  struct xen_gcov_info *bb)
+{
+	struct gcov_ftree_node *node;
+
+	node = (struct gcov_ftree_node *)
+	    kmalloc(sizeof(struct gcov_ftree_node), GFP_KERNEL);
+	if (!node)
+		return NULL;
+	memset(node, 0, sizeof(struct gcov_ftree_node));
+	node->fname = strdup(name);
+	if (!node->fname) {
+		kfree(node);
+		return NULL;
+	}
+	node->bb = bb;
+	return node;
+}
+
+/* Free memory allocated for NODE. */
+static void free_node(struct gcov_ftree_node *node)
+{
+	if (node == &tree_root)
+		return;
+	if (node->fname)
+		kfree(node->fname);
+	kfree(node);
+}
+
+/* Remove proc filesystem entries associated with NODE. */
+static void delete_from_proc(struct gcov_ftree_node *node)
+{
+	struct proc_dir_entry *parent;
+	int i;
+
+	if (node->parent)
+		parent = node->parent->proc[0];
+	else
+		parent = xen_base;
+	for (i = 0; i < sizeof(node->proc) / sizeof(node->proc[0]); i++)
+		if (node->proc[i])
+			remove_proc_entry(node->proc[i]->name, parent);
+}
+
+/* Release all resources associated with NODE. If NODE is a directory node,
+ * also clean up all children. */
+static void cleanup_node(struct gcov_ftree_node *node)
+{
+	struct gcov_ftree_node *curr;
+	struct gcov_ftree_node *next;
+	struct gcov_ftree_node *prev;
+
+	next = node;
+	do {
+		/* Depth first traversal of all children */
+		curr = next;
+		while (curr->files)
+			curr = curr->files;
+		if (curr->sibling)
+			next = curr->sibling;
+		else
+			next = curr->parent;
+		/* Remove from tree */
+		if (curr->parent) {
+			if (curr->parent->files == curr)
+				curr->parent->files = curr->sibling;
+			else {
+				for (prev = curr->parent->files;
+				     prev->sibling != curr;
+				     prev = prev->sibling) ;
+				prev->sibling = curr->sibling;
+			}
+		}
+		/* Remove from leaf node list if necessary */
+		if (curr->bb) {
+			if (leaf_nodes == curr)
+				leaf_nodes = curr->next;
+			else {
+				for (prev = leaf_nodes;
+				     prev && (prev->next != curr);
+				     prev = prev->next) ;
+				if (prev)
+					prev->next = curr->next;
+			}
+		}
+		/* Delete node */
+		delete_from_proc(curr);
+		free_node(curr);
+	} while (node != curr);
+}
+
+/* Clean up NODE and containing path in case it would be left empty. */
+static void cleanup_node_and_path(struct gcov_ftree_node *node)
+{
+	while (node->parent &&
+	       node->parent != &tree_root && !node->parent->files->sibling)
+		node = node->parent;
+	cleanup_node(node);
+}
+
+/* Create a new directory node named NAME under PARENT. Upon success return
+ * zero and update RESULT to point to the newly created node. Return non-zero
+ * otherwise. */
+static int
+create_dir_node(struct gcov_ftree_node *parent, char *name,
+		struct gcov_ftree_node **result)
+{
+	struct gcov_ftree_node *node;
+
+	/* Initialize new node */
+	node = alloc_node(name, NULL);
+	if (!node)
+		return -ENOMEM;
+	/* Create proc filesystem entry */
+	node->proc[0] = proc_mkdir(name, parent->proc[0]);
+	if (!node->proc[0]) {
+		free_node(node);
+		return -EIO;
+	}
+	/* Insert node into tree */
+	node->parent = parent;
+	node->sibling = parent->files;
+	parent->files = node;
+	*result = node;
+	return 0;
+}
+
+static struct file_operations proc_gcov_operations = {
+	.owner		= THIS_MODULE,
+	.read 		= read_gcov,
+	.write		= write_gcov,
+};
+
+/* Create a new file node named NAME under PARENT. Associate node with BB.
+ * Return zero upon success, non-zero otherwise. */
+static int
+create_file_node(struct gcov_ftree_node *parent, char *name,
+		 struct xen_gcov_info *bb)
+{
+	struct gcov_ftree_node *node;
+	char *link_target;
+	char *link_name;
+	int i;
+	/* Initialize new node */
+	node = alloc_node(name, bb);
+	if (!node)
+		return -ENOMEM;
+	/* Create proc filesystem entry */
+	node->proc[0] = create_proc_entry(name, S_IWUSR | S_IRUGO,
+					  parent->proc[0]);
+	if (!node->proc[0]) {
+		free_node(node);
+		return -EIO;
+	}
+	node->proc[0]->data = node;
+	node->proc[0]->proc_fops = &proc_gcov_operations;
+	node->proc[0]->size = sizeof_da_file(bb);
+	/* Create symbolic links */
+	if (xen_gcov_link) {
+		/* Note: temp string length is calculated as upper limit */
+		link_target = (char *)kmalloc(strlen(bb->file) +
+					      strlen(da_ending) +
+					      strlen(gcov_var->src_path),
+					      GFP_KERNEL);
+		if (!link_target) {
+			delete_from_proc(node);
+			free_node(node);
+			return -ENOMEM;
+		}
+		for (i = 0; i < sizeof(endings) / sizeof(endings[0]); i++) {
+
+			strcpy(link_target, bb->file);
+			link_target[strlen(link_target) -
+				    strlen(da_ending)] = 0;
+			strcat(link_target, endings[i]);
+			link_name = strrchr(link_target, '/') + 1;
+			node->proc[i + 1] = proc_symlink(link_name,
+							 parent->proc[0],
+							 link_target);
+			if (!node->proc[i + 1]) {
+				kfree(link_target);
+				delete_from_proc(node);
+				free_node(node);
+				return -EIO;
+			}
+		}
+		kfree(link_target);
+	}
+	/* Insert node into tree */
+	node->parent = parent;
+	node->sibling = parent->files;
+	parent->files = node;
+	node->next = leaf_nodes;
+	leaf_nodes = node;
+	return 0;
+}
+
+/* Return proc filesystem entry name for FILENAME. */
+static inline char *get_proc_filename(const char *filename)
+{
+	char *result;
+
+	result = strdup(filename + sourcepath_len + 1);
+	return result;
+}
+
+/* Create tree node and proc filesystem entry for BB. Create subdirectories as
+ * necessary. Return zero upon success, non-zero otherwise. */
+static int create_node(struct xen_gcov_info *bb)
+{
+	struct gcov_ftree_node *parent;
+	struct gcov_ftree_node *node;
+	char *filename;
+	char *curr;
+	char *next;
+	int rc;
+
+	filename = get_proc_filename(bb->file);
+	if (!filename)
+		return -ENOMEM;
+	/* Recreate directory path in proc filesystem */
+	parent = &tree_root;
+	for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) {
+		/* Skip empty path components */
+		if (curr == next)
+			continue;
+		*next = 0;
+		/* Check whether directory already exists */
+		for (node = parent->files;
+		     node && (strcmp(node->fname, curr) != 0);
+		     node = node->sibling) ;
+		if (!node) {
+			/* Create directory node */
+			rc = create_dir_node(parent, curr, &node);
+			if (rc) {
+				if (parent != &tree_root)
+					cleanup_node_and_path(parent);
+				kfree(filename);
+				return rc;
+			}
+		}
+		parent = node;
+	}
+	rc = create_file_node(parent, curr, bb);
+	kfree(filename);
+	return rc;
+}
+
+static void xeno_prof(void *data)
+{
+	/*update linked list with profiling info and line counts */
+	WARN_ON(HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0));
+	
+	/*again schedule work */
+	schedule_delayed_work(&work, xen_gcov_update * HZ);
+
+}
+
+/* Invokes hypercall to get all the necessary gcov variables from core */
+static inline int gcov_core_vars(void)
+{
+
+	int ret;
+	gcov_var = kmalloc(sizeof(struct gcov_variables), GFP_KERNEL);
+	if ((ret =
+	     HYPERVISOR_gcovprof_op(GCOVPROF_seek_vars, gcov_var, 0)) != 0)
+		return ret;
+	
+	xen_gcov_version = gcov_var->g_version;
+
+	gcov_var->ctr_num =
+	    kmalloc(gcov_var->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+	gcov_var->n_funcs =
+	    kmalloc(gcov_var->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+	if ((ret =
+	     HYPERVISOR_gcovprof_op(GCOVPROF_seek_vars, gcov_var, 1)) != 0)
+		return ret;
+
+	sourcepath_len = strlen(gcov_var->src_path);
+	return ret;
+}
+
+static int __init xen_gcov_init(void)
+{
+	struct xen_gcov_info *bb;
+	int ret, rc = 0;
+	/* seek gcov core variable information & construct the gcov_info list */
+	if (gcov_core_vars()) {
+		printk(KERN_ERR GCOV_PROC_HEADER
+		       "Failed to get the varaibales from core\n");
+		return -EIO;
+	}
+
+	/*add sys entry in /proc/sys/xen for count update tuning*/	
+#ifdef CONFIG_SYSCTL
+	gcov_table_header = register_sysctl_table(gcov_root_table, 0);
+	if (!gcov_table_header)
+		printk(KERN_INFO GCOV_PROC_HEADER
+		       "no /proc/sys/xen added\n");
+#endif
+	printk(KERN_INFO GCOV_PROC_HEADER "xen_gcov_link=%d xen_gcov_update=%d\n", 
+							xen_gcov_link, xen_gcov_update);
+	
+	/* Create xen_gcov_info linked list */
+	create_bb_list();
+	
+	/*populate the linked list with profiling info and line counts */
+	ret = HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0);
+	if (ret) {
+		printk(KERN_ERR GCOV_PROC_HEADER
+		       "Failed to retrive hypervisor profiling info\n");
+		return ret;
+	}
+	/* Initializing root node and /proc/gcov entry */
+	tree_root.fname = GCOV_PROC_ROOT;
+	tree_root.proc[0] = proc_mkdir(tree_root.fname, xen_base);
+	if (!tree_root.proc[0]) {
+		printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+		       "create root proc filesystem entry\n");
+		return -EIO;
+	}
+	/* Create /proc/gcov/vmlinux entry */
+	proc_vmlinux = create_proc_entry(GCOV_PROC_VMLINUX, S_IWUSR | S_IRUGO,
+					 tree_root.proc[0]);
+	if (!proc_vmlinux) {
+		printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+		       "create proc filesystem entry %s\n", GCOV_PROC_VMLINUX);
+		cleanup_node(&tree_root);
+		return -EIO;
+	}
+	proc_vmlinux->proc_fops = &proc_gcov_operations;
+
+	/*populate /proc/xen/gcov entry with data files*/
+	for (bb = list_head; bb != NULL; bb = bb->next) {
+		rc = create_node(bb);
+		if (rc) {
+			printk(KERN_ERR GCOV_PROC_HEADER
+			       "init failed: could not create node for %s (err=%d)\n",
+			       bb->file, rc);
+			remove_proc_entry(proc_vmlinux->name,
+					  tree_root.proc[0]);
+			cleanup_node(&tree_root);
+			return rc;
+		}
+	} /*done*/
+	update_vmlinux_data();
+
+	/*schedule for profiling update after <xen_gcov_update> secs*/
+	schedule_delayed_work(&work, xen_gcov_update * HZ);
+	printk(KERN_INFO GCOV_PROC_HEADER "init done\n");
+	kfree(gcov_var);
+	return 0;
+}
+
+/* Clean up module data. */
+static void __exit xen_gcov_cleanup(void)
+{
+	cancel_delayed_work(&work);
+	flush_scheduled_work();
+#ifdef CONFIG_SYSCTL
+	unregister_sysctl_table(gcov_table_header);
+#endif
+	remove_proc_entry(proc_vmlinux->name, tree_root.proc[0]);
+	cleanup_node(&tree_root);
+	free_bb_list();
+	printk(KERN_INFO GCOV_PROC_HEADER "Module is now unloaded\n");
+}
+
+module_init(xen_gcov_init);
+module_exit(xen_gcov_cleanup);
diff -r 51decc39e5e7 include/asm-i386/mach-xen/asm/hypercall.h
--- a/include/asm-i386/mach-xen/asm/hypercall.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/asm-i386/mach-xen/asm/hypercall.h	Thu Feb 26 17:04:07 2009 +0530
@@ -404,6 +404,13 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 
 
 #endif /* __HYPERCALL_H__ */
diff -r 51decc39e5e7 include/asm-x86_64/mach-xen/asm/hypercall.h
--- a/include/asm-x86_64/mach-xen/asm/hypercall.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/asm-x86_64/mach-xen/asm/hypercall.h	Thu Feb 26 17:04:07 2009 +0530
@@ -405,4 +405,11 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 #endif /* __HYPERCALL_H__ */
diff -r 51decc39e5e7 include/xen/interface/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/xen/interface/xen-gcov.h	Thu Feb 26 17:04:07 2009 +0530
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *  xen-gcov.h
+ *   
+ *  Interface for populating hypervisor profiling info under /proc/xen
+ *  
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to
+ *  deal in the Software without restriction, including without limitation the
+ *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ *  sell copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *   
+ *  The above copyright notice and this permission notice shall be included in
+ *  all copies or substantial portions of the Software.
+ *   
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ *  DEALINGS IN THE SOFTWARE.
+ *   
+ *  Author: tej parkash <tej.parkash@hcl.in>
+ *  Written by tej parkash and team
+ *  
+ */
+/**********************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_seek_vars            0
+#define GCOVPROF_start		      1
+#define GCOVPROF_reset		      2
+#define GCOVPROF_last_op              3
+
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS                 5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+/*xen-gcov-proc specific macros*/
+#define RESET_GCDA      0
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+
+
+struct gcov_ftree_node
+{
+    char *fname;             		/* Hierarchy-relative name */
+    struct gcov_ftree_node *sibling; 	/* First sibling of this node */
+    struct gcov_ftree_node *files;   	/* First child of this node */
+    struct gcov_ftree_node *parent;  	/* Parent of this node */
+    struct proc_dir_entry *proc[4];  	/* Entries for .da, .bb, .bbg, .c */
+    struct xen_gcov_info *bb;           	/* Associated struct bb */
+    loff_t offset;           		/* Offset in vmlinux file */
+    size_t da_size;          		/* Size of associated .da file */
+    size_t header_size;      		/* Size of associated file header */
+    struct gcov_ftree_node *next;    	/* Next leaf node */
+};
+
+
+struct xen_gcov_fn_info
+{
+    gcov_unsigned_t ident;    		/* unique ident of function */
+    gcov_unsigned_t checksum; 		/* function checksum */
+    unsigned int n_ctrs[GCOV_COUNTERS]; /* instrumented counters */
+};
+typedef struct xen_gcov_fn_info xen_gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_fn_info_t);
+
+struct xen_gcov_ctr_info
+{
+    gcov_unsigned_t num;      		/* number of counters.  */
+    gcov_type *values;			/* thier values. */
+    gcov_merge_fn merge;      		/* merge function  */
+};
+typedef struct  xen_gcov_ctr_info xen_gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct xen_gcov_info
+{
+    gcov_unsigned_t version;  		/* expected version number */
+    struct xen_gcov_info *next;   	/* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    		/* uniquifying time stamp */
+    char file[100];
+
+    unsigned int n_functions;     	/* number of functions */
+    struct xen_gcov_fn_info *functions;	/* table of functions */
+
+    unsigned int ctr_mask;        	/* mask of counters instrumented.  */
+    struct xen_gcov_ctr_info counts[GCOV_COUNTERS]; /* count data */
+};
+typedef struct xen_gcov_info xen_gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_info_t);
+
+struct gcov_variables
+{
+    char src_path[100];			/* source path */
+    unsigned long g_version;		/* gcov version */
+    unsigned int n_files;		/* num of file in hypervisor code */
+    gcov_unsigned_t *ctr_num;		/* count variables */
+    unsigned int *n_funcs;     		/* num of funcs per bb struct */
+	
+};
+typedef struct gcov_variables gcov_variables_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_variables_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r 51decc39e5e7 include/xen/interface/xen.h
--- a/include/xen/interface/xen.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/xen/interface/xen.h	Thu Feb 26 17:04:07 2009 +0530
@@ -91,6 +91,7 @@
 #define __HYPERVISOR_sysctl               35
 #define __HYPERVISOR_domctl               36
 #define __HYPERVISOR_kexec_op             37
+#define __HYPERVISOR_gcovprof_op          38
 
 /* Architecture-specific hypercall definitions. */
 #define __HYPERVISOR_arch_0               48
diff -r 51decc39e5e7 include/xen/xen_proc.h
--- a/include/xen/xen_proc.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/xen/xen_proc.h	Thu Feb 26 17:04:07 2009 +0530
@@ -3,6 +3,8 @@
 #define __ASM_XEN_PROC_H__
 
 #include <linux/proc_fs.h>
+
+extern struct proc_dir_entry *xen_base;
 
 extern struct proc_dir_entry *create_xen_proc_entry(
 	const char *name, mode_t mode);

[-- Attachment #4: lcov --]
[-- Type: application/octet-stream, Size: 52551 bytes --]

#!/usr/bin/perl -w
#
#   Copyright (c) International Business Machines  Corp., 2002,2007
#
#   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
#
#
# lcov
#
#   This is a wrapper script which provides a single interface for accessing
#   LCOV coverage data.
#
#
# History:
#   2002-08-29 created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
#                         IBM Lab Boeblingen
#   2002-09-05 / Peter Oberparleiter: implemented --kernel-directory +
#                multiple directories
#   2002-10-16 / Peter Oberparleiter: implemented --add-tracefile option
#   2002-10-17 / Peter Oberparleiter: implemented --extract option
#   2002-11-04 / Peter Oberparleiter: implemented --list option
#   2003-03-07 / Paul Larson: Changed to make it work with the latest gcov 
#                kernel patch.  This will break it with older gcov-kernel
#                patches unless you change the value of $gcovmod in this script
#   2003-04-07 / Peter Oberparleiter: fixed bug which resulted in an error
#                when trying to combine .info files containing data without
#                a test name
#   2003-04-10 / Peter Oberparleiter: extended Paul's change so that LCOV
#                works both with the new and the old gcov-kernel patch
#   2003-04-10 / Peter Oberparleiter: added $gcov_dir constant in anticipation
#                of a possible move of the gcov kernel directory to another
#                file system in a future version of the gcov-kernel patch
#   2003-04-15 / Paul Larson: make info write to STDERR, not STDOUT
#   2003-04-15 / Paul Larson: added --remove option
#   2003-04-30 / Peter Oberparleiter: renamed --reset to --zerocounters
#                to remove naming ambiguity with --remove
#   2003-04-30 / Peter Oberparleiter: adjusted help text to include --remove
#   2003-06-27 / Peter Oberparleiter: implemented --diff
#   2003-07-03 / Peter Oberparleiter: added line checksum support, added
#                --no-checksum
#   2003-12-11 / Laurent Deniel: added --follow option
#   2004-03-29 / Peter Oberparleiter: modified --diff option to better cope with
#                ambiguous patch file entries, modified --capture option to use
#                modprobe before insmod (needed for 2.6)
#   2004-03-30 / Peter Oberparleiter: added --path option
#   2004-08-09 / Peter Oberparleiter: added configuration file support
#

use strict;
use File::Basename; 
use Getopt::Long;


# Global constants
our $lcov_version	= "LTP GCOV extension version 1.6";
our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";

# Hash Table which contains gcov module name as well as directory path
our %gcov_module_dir = ( "gcov-prof"     => "/proc/gcov",
                         "gcov-proc"     => "/proc/gcov",
                         "xen-gcov-proc" => "/proc/xen/gcov" );
                                                   
# Directory containing gcov kernel files
our $gcov_dir = "/proc/gcov";

# The location of the insmod tool
our $insmod_tool	= "/sbin/insmod";

# The location of the modprobe tool
our $modprobe_tool	= "/sbin/modprobe";

# The location of the rmmod tool
our $rmmod_tool		= "/sbin/rmmod";

# Where to create temporary directories
our $tmp_dir		= "/tmp";

# How to prefix a temporary directory name
our $tmp_prefix		= "tmpdir";


# Prototypes
sub print_usage(*);
sub check_options();
sub userspace_reset();
sub userspace_capture();
sub kernel_reset();
sub kernel_capture();
sub add_traces();
sub read_info_file($);
sub get_info_entry($);
sub set_info_entry($$$$$;$$);
sub add_counts($$);
sub merge_checksums($$$);
sub combine_info_entries($$$);
sub combine_info_files($$);
sub write_info_file(*$);
sub extract();
sub remove();
sub list();
sub get_common_filename($$);
sub read_diff($);
sub diff();
sub system_no_output($@);
sub read_config($);
sub apply_config($);

sub info(@);
sub unload_module($);
sub check_and_load_kernel_module();
sub create_temp_dir();
sub transform_pattern($);


# Global variables & initialization
our @directory;		# Specifies where to get coverage data from
our @kernel_directory;	# If set, captures only from specified kernel subdirs
our @add_tracefile;	# If set, reads in and combines all files in list
our $list;		# If set, list contents of tracefile
our $extract;		# If set, extracts parts of tracefile
our $remove;		# If set, removes parts of tracefile
our $diff;		# If set, modifies tracefile according to diff
our $reset;		# If set, reset all coverage data to zero
our $capture;		# If set, capture data
our $output_filename;	# Name for file to write coverage data to
our $test_name = "";	# Test case name
our $quiet = "";	# If set, suppress information messages
our $help;		# Help option flag
our $version;		# Version option flag
our $convert_filenames;	# If set, convert filenames when applying diff
our $strip;		# If set, strip leading directories when applying diff
our $need_unload;	# If set, unload gcov kernel module
our $temp_dir_name;	# Name of temporary directory
our $cwd = `pwd`;	# Current working directory
our $to_file;		# If set, indicates that output is written to a file
our $follow;		# If set, indicates that find shall follow links
our $diff_path = "";	# Path removed from tracefile when applying diff
our $base_directory;	# Base directory (cwd of gcc during compilation)
our $checksum;		# If set, calculate a checksum for each line
our $no_checksum;	# If set, don't calculate a checksum for each line
our $compat_libtool;	# If set, indicates that libtool mode is to be enabled
our $no_compat_libtool;	# If set, indicates that libtool mode is to be disabled
our $gcov_tool;
our $ignore_errors;
our $initial;
our $config;		# Configuration file contents
chomp($cwd);
our $tool_dir = dirname($0);	# Directory where genhtml tool is installed


#
# Code entry point
#

# Add current working directory if $tool_dir is not already an absolute path
if (! ($tool_dir =~ /^\/(.*)$/))
{
	$tool_dir = "$cwd/$tool_dir";
}

# Read configuration file if available
if (-r $ENV{"HOME"}."/.lcovrc")
{
	$config = read_config($ENV{"HOME"}."/.lcovrc");
}
elsif (-r "/etc/lcovrc")
{
	$config = read_config("/etc/lcovrc");
}

if ($config)
{
	# Copy configuration file values to variables
	apply_config({
		"lcov_gcov_dir"		=> \$gcov_dir,
		"lcov_insmod_tool"	=> \$insmod_tool,
		"lcov_modprobe_tool"	=> \$modprobe_tool,
		"lcov_rmmod_tool"	=> \$rmmod_tool,
		"lcov_tmp_dir"		=> \$tmp_dir});
}

# Parse command line options
if (!GetOptions("directory|d|di=s" => \@directory,
		"add-tracefile=s" => \@add_tracefile,
		"list=s" => \$list,
		"kernel-directory=s" => \@kernel_directory,
		"extract=s" => \$extract,
		"remove=s" => \$remove,
		"diff=s" => \$diff,
		"convert-filenames" => \$convert_filenames,
		"strip=i" => \$strip,
		"capture|c" => \$capture,
		"output-file=s" => \$output_filename,
		"test-name=s" => \$test_name,
		"zerocounters" => \$reset,
		"quiet" => \$quiet,
		"help" => \$help,
		"version" => \$version,
		"follow" => \$follow,
		"path=s" => \$diff_path,
		"base-directory=s" => \$base_directory,
		"checksum" => \$checksum,
		"no-checksum" => \$no_checksum,
		"compat-libtool" => \$compat_libtool,
		"no-compat-libtool" => \$no_compat_libtool,
		"gcov-tool=s" => \$gcov_tool,
		"ignore-errors=s" => \$ignore_errors,
		"initial|i" => \$initial
		))
{
	print_usage(*STDERR);
	exit(1);
}
else
{
	# Merge options
	if (defined($no_checksum))
	{
		$checksum = ($no_checksum ? 0 : 1);
		$no_checksum = undef;
	}

	if (defined($no_compat_libtool))
	{
		$compat_libtool = ($no_compat_libtool ? 0 : 1);
		$no_compat_libtool = undef;
	}
}

# Check for help option
if ($help)
{
	print_usage(*STDOUT);
	exit(0);
}

# Check for version option
if ($version)
{
	print("$lcov_version\n");
	exit(0);
}

# Normalize --path text
$diff_path =~ s/\/$//;

# Check for valid options
check_options();

# Only --extract, --remove and --diff allow unnamed parameters
if (@ARGV && !($extract || $remove || $diff))
{
	print_usage(*STDERR);
	exit(1);
}

# Check for output filename
$to_file = ($output_filename && ($output_filename ne "-"));

if ($capture)
{
	if (!$to_file)
	{
		# Option that tells geninfo to write to stdout
		$output_filename = "-";
	}
}
else
{
	if ($initial)
	{
		print(STDERR "Option --initial is only valid when capturing ".
		      "data (-c)\n");
		print_usage(*STDERR);
		exit(2);
	}
}

# Check for requested functionality
if ($reset)
{
	# Differentiate between user space and kernel reset
	if (@directory)
	{
		userspace_reset();
	}
	else
	{
		kernel_reset();
	}
}
elsif ($capture)
{
	# Differentiate between user space and kernel 
	if (@directory)
	{
		userspace_capture();
	}
	else
	{
		kernel_capture();
	}
}
elsif (@add_tracefile)
{
	add_traces();
}
elsif ($remove)
{
	remove();
}
elsif ($extract)
{
	extract();
}
elsif ($list)
{
	list();
}
elsif ($diff)
{
	if (scalar(@ARGV) != 1)
	{
		die("ERROR: option --diff requires one additional argument!\n");
	}
	diff();
}

info("Done.\n");
exit(0);

# Check for follow option
if ($follow)
{
	$follow = "--follow";
}

#
# print_usage(handle)
#
# Print usage information.
#

sub print_usage(*)
{
	local *HANDLE = $_[0];
	my $tool_name = basename($0);

	print(HANDLE <<END_OF_USAGE);
Usage: $tool_name [OPTIONS]

Use lcov to collect coverage data from either the currently running Linux
kernel or from a user space application. Specify the --directory option to
get coverage data for a user space program.

Misc:
  -h, --help                      Print this help, then exit
  -v, --version                   Print version number, then exit
  -q, --quiet                     Do not print progress messages

Operation:
  -z, --zerocounters              Reset all execution counts to zero
  -c, --capture                   Capture coverage data
  -a, --add-tracefile FILE        Add contents of tracefiles
  -e, --extract FILE PATTERN      Extract files matching PATTERN from FILE
  -r, --remove FILE PATTERN       Remove files matching PATTERN from FILE
  -l, --list FILE                 List contents of tracefile FILE
      --diff FILE DIFF            Transform tracefile FILE according to DIFF

Options:
  -i, --initial                   Capture initial zero coverage data
  -t, --test-name NAME            Specify test name to be stored with data
  -o, --output-file FILENAME      Write data to FILENAME instead of stdout
  -d, --directory DIR             Use .da files in DIR instead of kernel
  -f, --follow                    Follow links when searching .da files
  -k, --kernel-directory KDIR     Capture kernel coverage data only from KDIR
  -b, --base-directory DIR        Use DIR as base directory for relative paths
      --convert-filenames         Convert filenames when applying diff
      --strip DEPTH               Strip initial DEPTH directory levels in diff
      --path PATH                 Strip PATH from tracefile when applying diff
      --(no-)checksum             Enable (disable) line checksumming
      --(no-)compat-libtool       Enable (disable) libtool compatibility mode
      --gcov-tool TOOL            Specify gcov tool location
      --ignore-errors ERRORS      Continue after ERRORS (gcov, source)

See $lcov_url for more information about this tool.
END_OF_USAGE
	;
}


#
# check_options()
#
# Check for valid combination of command line options. Die on error.
#

sub check_options()
{
	my $i = 0;

	# Count occurrence of mutually exclusive options
	$reset && $i++;
	$capture && $i++;
	@add_tracefile && $i++;
	$extract && $i++;
	$remove && $i++;
	$list && $i++;
	$diff && $i++;
	
	if ($i == 0)
	{
		print(STDERR "Need one of the options -z, -c, -a, -e, -r, ".
			     "-l or --diff\n");
		print_usage(*STDERR);
		exit(2);
	}
	elsif ($i > 1)
	{
		die("ERROR: only one of -z, -c, -a, -e, -r, -l or ".
		    "--diff allowed!\n");
	}
}


#
# userspace_reset()
#
# Reset coverage data found in DIRECTORY by deleting all contained .da files.
#
# Die on error.
#

sub userspace_reset()
{
	my $current_dir;
	my @file_list;

	foreach $current_dir (@directory)
	{
		info("Deleting all .da files in $current_dir and ".
		     "subdirectories\n");
		if ($follow)
		{
			@file_list =
				`find $current_dir -follow -name \\*\\.da -o -name \\*\\.gcda -type f 2>/dev/null`;
		}
		else
		{
			@file_list =
				`find $current_dir -name \\*\\.da -o -name \\*\\.gcda -type f 2>/dev/null`;
		}
		chomp(@file_list);
		foreach (@file_list)
		{
			unlink($_) or die("ERROR: cannot remove file $_!\n");
		}
	}
}


#
# userspace_capture()
#
# Capture coverage data found in DIRECTORY and write it to OUTPUT_FILENAME
# if specified, otherwise to STDOUT.
#
# Die on error.
#

sub userspace_capture()
{
	my @param;
	my $file_list = join(" ", @directory);

	info("Capturing coverage data from $file_list\n");
	@param = ("$tool_dir/geninfo", @directory);
	if ($output_filename)
	{
		@param = (@param, "--output-filename", $output_filename);
	}
	if ($test_name)
	{
		@param = (@param, "--test-name", $test_name);
	}
	if ($follow)
	{
		@param = (@param, "--follow");
	}
	if ($quiet)
	{
		@param = (@param, "--quiet");
	}
	if (defined($checksum))
	{
		if ($checksum)
		{
			@param = (@param, "--checksum");
		}
		else
		{
			@param = (@param, "--no-checksum");
		}
	}
	if ($base_directory)
	{
		@param = (@param, "--base-directory", $base_directory);
	}
	if ($no_compat_libtool)
	{
		@param = (@param, "--no-compat-libtool");
	}
	elsif ($compat_libtool)
	{
		@param = (@param, "--compat-libtool");
	}
	if ($gcov_tool)
	{
		@param = (@param, "--gcov-tool", $gcov_tool);
	}
	if ($ignore_errors)
	{
		@param = (@param, "--ignore-errors", $ignore_errors);
	}
	if ($initial)
	{
		@param = (@param, "--initial");
	}

	system(@param);
	exit($? >> 8);
}


#
# kernel_reset()
#
# Reset kernel coverage.
#
# Die on error.
#

sub kernel_reset()
{
	local *HANDLE;
	check_and_load_kernel_module();

	info("Resetting kernel execution counters\n");
	open(HANDLE, ">$gcov_dir/vmlinux") or
		die("ERROR: cannot write to $gcov_dir/vmlinux!\n");
	print(HANDLE "0");
	close(HANDLE);

	# Unload module if we loaded it in the first place
	if ($need_unload)
	{
		unload_module($need_unload);
	}
}


#
# kernel_capture()
#
# Capture kernel coverage data and write it to OUTPUT_FILENAME if specified,
# otherwise stdout.
#

sub kernel_capture()
{
	my @param;

	check_and_load_kernel_module();

	# Make sure the temporary directory is removed upon script termination
	END
	{
		if ($temp_dir_name)
		{
			stat($temp_dir_name);
			if (-r _)
			{
				info("Removing temporary directory ".
				     "$temp_dir_name\n");

				# Remove temporary directory
				system("rm", "-rf", $temp_dir_name)
					and warn("WARNING: cannot remove ".
						 "temporary directory ".
						 "$temp_dir_name!\n");
			}
		}
	}

	# Get temporary directory
	$temp_dir_name = create_temp_dir();

	info("Copying kernel data to temporary directory $temp_dir_name\n");

	if (!@kernel_directory)
	{
		# Copy files from gcov kernel directory
		system("cp", "-dr", $gcov_dir, $temp_dir_name)
			and die("ERROR: cannot copy files from $gcov_dir!\n");
	}
	else
	{
		# Prefix list of kernel sub-directories with the gcov kernel
		# directory
		@kernel_directory = map("$gcov_dir/$_", @kernel_directory);

		# Copy files from gcov kernel directory
		system("cp", "-dr", @kernel_directory, $temp_dir_name)
			and die("ERROR: cannot copy files from ".
				join(" ", @kernel_directory)."!\n");
	}

	# Make directories writable
	system("find", $temp_dir_name, "-type", "d", "-exec", "chmod", "u+w",
	       "{}", ";")
		and die("ERROR: cannot modify access rights for ".
			"$temp_dir_name!\n");

	# Make files writable
	system("find", $temp_dir_name, "-type", "f", "-exec", "chmod", "u+w",
	       "{}", ";")
		and die("ERROR: cannot modify access rights for ".
			"$temp_dir_name!\n");

	# Capture data
	info("Capturing coverage data from $temp_dir_name\n");
	@param = ("$tool_dir/geninfo", $temp_dir_name);
	if ($output_filename)
	{
		@param = (@param, "--output-filename", $output_filename);
	}
	if ($test_name)
	{
		@param = (@param, "--test-name", $test_name);
	}
	if ($follow)
	{
		@param = (@param, "--follow");
	}
	if ($quiet)
	{
		@param = (@param, "--quiet");
	}
	if (defined($checksum))
	{
		if ($checksum)
		{
			@param = (@param, "--checksum");
		}
		else
		{
			@param = (@param, "--no-checksum");
		}
	}
	if ($base_directory)
	{
		@param = (@param, "--base-directory", $base_directory);
	}
	if ($no_compat_libtool)
	{
		@param = (@param, "--no-compat-libtool");
	}
	elsif ($compat_libtool)
	{
		@param = (@param, "--compat-libtool");
	}
	if ($gcov_tool)
	{
		@param = (@param, "--gcov-tool", $gcov_tool);
	}
	if ($ignore_errors)
	{
		@param = (@param, "--ignore-errors", $ignore_errors);
	}
	if ($initial)
	{
		@param = (@param, "--initial");
	}
	system(@param) and exit($? >> 8);


	# Unload module if we loaded it in the first place
	if ($need_unload)
	{
		unload_module($need_unload);
	}
}


#
# info(printf_parameter)
#
# Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag
# is not set.
#

sub info(@)
{
	if (!$quiet)
	{
		# Print info string
		if ($to_file)
		{
			print(@_)
		}
		else
		{
			# Don't interfer with the .info output to STDOUT
			printf(STDERR @_);
		}
	}
}


#
# Check if the gcov kernel module is loaded. If it is, exit, if not, try
# to load it.
#
# Die on error.
#

sub check_and_load_kernel_module()
{
	my $module_name="";

	# Is it loaded already?
	stat("$gcov_dir");
	if (-r _) { return(); }

	info("Loading required gcov kernel module.\n");

	# Do we have access to the insmod tool?
	stat($insmod_tool);
	if (!-x _)
	{
		die("ERROR: need insmod tool ($insmod_tool) to access kernel ".
		    "coverage data!\n");
	}
	# Do we have access to the modprobe tool?
	stat($modprobe_tool);
	if (!-x _)
	{
		die("ERROR: need modprobe tool ($modprobe_tool) to access ".
		    "kernel coverage data!\n");
	}

	# Try some possibilities of where the gcov module may be found.
	# And there is a hash table called %gcov_module_dir which has the module name
	# associated with directory path. Based on the $gcov_dir select the 
	# particular module to load.
	while ((my $mod_name, my $gcov_path) = each (%gcov_module_dir))
	{
		$module_name = $mod_name if($gcov_path eq $gcov_dir);

		# Try to load module from system wide module directory
		# /lib/modules
		if (system_no_output(3, $modprobe_tool, $module_name) == 0)
		{
			# Succeeded
			$need_unload = $module_name;
			return();
		}

		# Try to load linux 2.5/2.6 module from tool directory
		if (system_no_output(3, $insmod_tool,
				      "$tool_dir/$module_name.ko") == 0)
		{
			# Succeeded
			$need_unload = $module_name;
			return();
		}

		# Try to load linux 2.4 module from tool directory
		if (system_no_output(3, $insmod_tool,
				     "$tool_dir/$module_name.o") == 0)
		{
			# Succeeded
			$need_unload = $module_name;
			return();
		}
	}

	# Hm, loading failed - maybe we aren't root?
	if ($> != 0)
	{
		die("ERROR: need root access to load kernel module!\n");
	}

	die("ERROR: cannot load required gcov kernel module!\n");
}


#
# unload_module()
#
# Unload the gcov kernel module.
#

sub unload_module($)
{
	my $module = $_[0];

	info("Unloading kernel module $module\n");

	# Do we have access to the rmmod tool?
	stat($rmmod_tool);
	if (!-x _)
	{
		warn("WARNING: cannot execute rmmod tool at $rmmod_tool - ".
		     "gcov module still loaded!\n");
	}

	# Unload gcov kernel module
	system_no_output(1, $rmmod_tool, $module)
		and warn("WARNING: cannot unload gcov kernel module ".
		         "$module!\n");
}


#
# create_temp_dir()
#
# Create a temporary directory and return its path.
#
# Die on error.
#

sub create_temp_dir()
{
	my $dirname;
	my $number = sprintf("%d", rand(1000));

	# Endless loops are evil
	while ($number++ < 1000)
	{
		$dirname = "$tmp_dir/$tmp_prefix$number";
		stat($dirname);
		if (-e _) { next; }

		mkdir($dirname)
			or die("ERROR: cannot create temporary directory ".
			       "$dirname!\n");

		return($dirname);
	}

	die("ERROR: cannot create temporary directory in $tmp_dir!\n");
}


#
# read_info_file(info_filename)
#
# Read in the contents of the .info file specified by INFO_FILENAME. Data will
# be returned as a reference to a hash containing the following mappings:
#
# %result: for each filename found in file -> \%data
#
# %data: "test"  -> \%testdata
#        "sum"   -> \%sumcount
#        "func"  -> \%funcdata
#        "found" -> $lines_found (number of instrumented lines found in file)
#	 "hit"   -> $lines_hit (number of executed lines in file)
#        "check" -> \%checkdata
#
# %testdata: name of test affecting this file -> \%testcount
#
# %testcount: line number -> execution count for a single test
# %sumcount : line number -> execution count for all tests
# %funcdata : line number -> name of function beginning at that line
# %checkdata: line number -> checksum of source code line
# 
# Note that .info file sections referring to the same file and test name
# will automatically be combined by adding all execution counts.
#
# Note that if INFO_FILENAME ends with ".gz", it is assumed that the file
# is compressed using GZIP. If available, GUNZIP will be used to decompress
# this file.
#
# Die on error.
#

sub read_info_file($)
{
	my $tracefile = $_[0];		# Name of tracefile
	my %result;			# Resulting hash: file -> data
	my $data;			# Data handle for current entry
	my $testdata;			#       "             "
	my $testcount;			#       "             "
	my $sumcount;			#       "             "
	my $funcdata;			#       "             "
	my $checkdata;			#       "             "
	my $line;			# Current line read from .info file
	my $testname;			# Current test name
	my $filename;			# Current filename
	my $hitcount;			# Count for lines hit
	my $count;			# Execution count of current line
	my $negative;			# If set, warn about negative counts
	my $changed_testname;		# If set, warn about changed testname
	my $line_checksum;		# Checksum of current line
	local *INFO_HANDLE;		# Filehandle for .info file

	info("Reading tracefile $tracefile\n");

	# Check if file exists and is readable
	stat($_[0]);
	if (!(-r _))
	{
		die("ERROR: cannot read file $_[0]!\n");
	}

	# Check if this is really a plain file
	if (!(-f _))
	{
		die("ERROR: not a plain file: $_[0]!\n");
	}

	# Check for .gz extension
	if ($_[0] =~ /\.gz$/)
	{
		# Check for availability of GZIP tool
		system_no_output(1, "gunzip" ,"-h")
			and die("ERROR: gunzip command not available!\n");

		# Check integrity of compressed file
		system_no_output(1, "gunzip", "-t", $_[0])
			and die("ERROR: integrity check failed for ".
				"compressed file $_[0]!\n");

		# Open compressed file
		open(INFO_HANDLE, "gunzip -c $_[0]|")
			or die("ERROR: cannot start gunzip to decompress ".
			       "file $_[0]!\n");
	}
	else
	{
		# Open decompressed file
		open(INFO_HANDLE, $_[0])
			or die("ERROR: cannot read file $_[0]!\n");
	}

	$testname = "";
	while (<INFO_HANDLE>)
	{
		chomp($_);
		$line = $_;

		# Switch statement
		foreach ($line)
		{
			/^TN:([^,]*)/ && do
			{
				# Test name information found
				$testname = defined($1) ? $1 : "";
				if ($testname =~ s/\W/_/g)
				{
					$changed_testname = 1;
				}
				last;
			};

			/^[SK]F:(.*)/ && do
			{
				# Filename information found
				# Retrieve data for new entry
				$filename = $1;

				$data = $result{$filename};
				($testdata, $sumcount, $funcdata, $checkdata) =
					get_info_entry($data);

				if (defined($testname))
				{
					$testcount = $testdata->{$testname};
				}
				else
				{
					my %new_hash;
					$testcount = \%new_hash;
				}
				last;
			};

			/^DA:(\d+),(-?\d+)(,[^,\s]+)?/ && do
			{
				# Fix negative counts
				$count = $2 < 0 ? 0 : $2;
				if ($2 < 0)
				{
					$negative = 1;
				}
				# Execution count found, add to structure
				# Add summary counts
				$sumcount->{$1} += $count;

				# Add test-specific counts
				if (defined($testname))
				{
					$testcount->{$1} += $count;
				}

				# Store line checksum if available
				if (defined($3))
				{
					$line_checksum = substr($3, 1);

					# Does it match a previous definition
					if (defined($checkdata->{$1}) &&
					    ($checkdata->{$1} ne
					     $line_checksum))
					{
						die("ERROR: checksum mismatch ".
						    "at $filename:$1\n");
					}

					$checkdata->{$1} = $line_checksum;
				}
				last;
			};

			/^FN:(\d+),([^,]+)/ && do
			{
				# Function data found, add to structure
				$funcdata->{$1} = $2;
				last;
			};

			/^end_of_record/ && do
			{
				# Found end of section marker
				if ($filename)
				{
					# Store current section data
					if (defined($testname))
					{
						$testdata->{$testname} =
							$testcount;
					}	
					set_info_entry($data, $testdata,
						       $sumcount, $funcdata,
						       $checkdata);
					$result{$filename} = $data;
					last;
				}
			};

			# default
			last;
		}
	}
	close(INFO_HANDLE);

	# Calculate lines_found and lines_hit for each file
	foreach $filename (keys(%result))
	{
		$data = $result{$filename};

		($testdata, $sumcount, $funcdata) = get_info_entry($data);

		# Filter out empty test cases
		if (scalar(keys(%{$sumcount})) == 0)
		{
			delete($result{$filename});
			next;
		}
		foreach $testname (keys(%{$testdata}))
		{
			if (!defined($testdata->{$testname}) ||
			    scalar(keys(%{$testdata->{$testname}})) == 0)
			{
				delete($testdata->{$testname});
			}
		}

		$data->{"found"} = scalar(keys(%{$sumcount}));
		$hitcount = 0;

		foreach (keys(%{$sumcount}))
		{
			if ($sumcount->{$_} > 0) { $hitcount++; }
		}

		$data->{"hit"} = $hitcount;

		$result{$filename} = $data;
	}

	if (scalar(keys(%result)) == 0)
	{
		die("ERROR: no valid records found in tracefile $tracefile\n");
	}
	if ($negative)
	{
		warn("WARNING: negative counts found in tracefile ".
		     "$tracefile\n");
	}
	if ($changed_testname)
	{
		warn("WARNING: invalid characters removed from testname in ".
		     "tracefile $tracefile\n");
	}

	return(\%result);
}


#
# get_info_entry(hash_ref)
#
# Retrieve data from an entry of the structure generated by read_info_file().
# Return a list of references to hashes:
# (test data hash ref, sum count hash ref, funcdata hash ref, checkdata hash
#  ref, lines found, lines hit)
#

sub get_info_entry($)
{
	my $testdata_ref = $_[0]->{"test"};
	my $sumcount_ref = $_[0]->{"sum"};
	my $funcdata_ref = $_[0]->{"func"};
	my $checkdata_ref = $_[0]->{"check"};
	my $lines_found = $_[0]->{"found"};
	my $lines_hit = $_[0]->{"hit"};

	return ($testdata_ref, $sumcount_ref, $funcdata_ref, $checkdata_ref,
		$lines_found, $lines_hit);
}


#
# set_info_entry(hash_ref, testdata_ref, sumcount_ref, funcdata_ref,
#                checkdata_ref[,lines_found, lines_hit])
#
# Update the hash referenced by HASH_REF with the provided data references.
#

sub set_info_entry($$$$$;$$)
{
	my $data_ref = $_[0];

	$data_ref->{"test"} = $_[1];
	$data_ref->{"sum"} = $_[2];
	$data_ref->{"func"} = $_[3];
	$data_ref->{"check"} = $_[4];

	if (defined($_[5])) { $data_ref->{"found"} = $_[5]; }
	if (defined($_[6])) { $data_ref->{"hit"} = $_[6]; }
}


#
# add_counts(data1_ref, data2_ref)
#
# DATA1_REF and DATA2_REF are references to hashes containing a mapping
#
#   line number -> execution count
#
# Return a list (RESULT_REF, LINES_FOUND, LINES_HIT) where RESULT_REF
# is a reference to a hash containing the combined mapping in which
# execution counts are added.
#

sub add_counts($$)
{
	my %data1 = %{$_[0]};	# Hash 1
	my %data2 = %{$_[1]};	# Hash 2
	my %result;		# Resulting hash
	my $line;		# Current line iteration scalar
	my $data1_count;	# Count of line in hash1
	my $data2_count;	# Count of line in hash2
	my $found = 0;		# Total number of lines found
	my $hit = 0;		# Number of lines with a count > 0

	foreach $line (keys(%data1))
	{
		$data1_count = $data1{$line};
		$data2_count = $data2{$line};

		# Add counts if present in both hashes
		if (defined($data2_count)) { $data1_count += $data2_count; }

		# Store sum in %result
		$result{$line} = $data1_count;

		$found++;
		if ($data1_count > 0) { $hit++; }
	}

	# Add lines unique to data2
	foreach $line (keys(%data2))
	{
		# Skip lines already in data1
		if (defined($data1{$line})) { next; }

		# Copy count from data2
		$result{$line} = $data2{$line};

		$found++;
		if ($result{$line} > 0) { $hit++; }
	}

	return (\%result, $found, $hit);
}


#
# merge_checksums(ref1, ref2, filename)
#
# REF1 and REF2 are references to hashes containing a mapping
#
#   line number -> checksum
#
# Merge checksum lists defined in REF1 and REF2 and return reference to
# resulting hash. Die if a checksum for a line is defined in both hashes
# but does not match.
#

sub merge_checksums($$$)
{
	my $ref1 = $_[0];
	my $ref2 = $_[1];
	my $filename = $_[2];
	my %result;
	my $line;

	foreach $line (keys(%{$ref1}))
	{
		if (defined($ref2->{$line}) &&
		    ($ref1->{$line} ne $ref2->{$line}))
		{
			die("ERROR: checksum mismatch at $filename:$line\n");
		}
		$result{$line} = $ref1->{$line};
	}

	foreach $line (keys(%{$ref2}))
	{
		$result{$line} = $ref2->{$line};
	}

	return \%result;
}


#
# merge_func_data(ref1, ref2, filename)
#

sub merge_func_data($$$)
{
	my $ref1 = $_[0];
	my $ref2 = $_[1];
	my $filename = $_[2];
	my %result;
	my %ignore;
	my $line1;
	my $line2;

	# Check for mismatch
	foreach $line1 (keys(%{$ref1}))
	{
		foreach $line2 (keys(%{$ref2}))
		{
			if (($ref1->{$line1} eq $ref2->{$line2}) &&
			    ($line1 != $line2))
			{
				warn("WARNING: function data mismatch at ".
				     "$filename:$ref1->{$line1}\n");
				$ignore{$line2} = 1;
			}
		}
	}

	# Merge
	foreach (keys(%{$ref1}))
	{
		$result{$_} = $ref1->{$_};
	}

	foreach (keys(%{$ref2}))
	{
		if (!$ignore{$_})
		{
			$result{$_} = $ref2->{$_};
		}
	}

	return \%result;
}


#
# combine_info_entries(entry_ref1, entry_ref2, filename)
#
# Combine .info data entry hashes referenced by ENTRY_REF1 and ENTRY_REF2.
# Return reference to resulting hash.
#

sub combine_info_entries($$$)
{
	my $entry1 = $_[0];	# Reference to hash containing first entry
	my $testdata1;
	my $sumcount1;
	my $funcdata1;
	my $checkdata1;

	my $entry2 = $_[1];	# Reference to hash containing second entry
	my $testdata2;
	my $sumcount2;
	my $funcdata2;
	my $checkdata2;

	my %result;		# Hash containing combined entry
	my %result_testdata;
	my $result_sumcount = {};
	my $result_funcdata;
	my $lines_found;
	my $lines_hit;

	my $testname;
	my $filename = $_[2];

	# Retrieve data
	($testdata1, $sumcount1, $funcdata1, $checkdata1) =
		get_info_entry($entry1);
	($testdata2, $sumcount2, $funcdata2, $checkdata2) =
		get_info_entry($entry2);

	# Merge checksums
	$checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename);

	# Combine funcdata
	$result_funcdata = merge_func_data($funcdata1, $funcdata2, $filename);
	
	# Combine testdata
	foreach $testname (keys(%{$testdata1}))
	{
		if (defined($testdata2->{$testname}))
		{
			# testname is present in both entries, requires
			# combination
			($result_testdata{$testname}) =
				add_counts($testdata1->{$testname},
					   $testdata2->{$testname});
		}
		else
		{
			# testname only present in entry1, add to result
			$result_testdata{$testname} = $testdata1->{$testname};
		}

		# update sum count hash
		($result_sumcount, $lines_found, $lines_hit) =
			add_counts($result_sumcount,
				   $result_testdata{$testname});
	}

	foreach $testname (keys(%{$testdata2}))
	{
		# Skip testnames already covered by previous iteration
		if (defined($testdata1->{$testname})) { next; }

		# testname only present in entry2, add to result hash
		$result_testdata{$testname} = $testdata2->{$testname};

		# update sum count hash
		($result_sumcount, $lines_found, $lines_hit) =
			add_counts($result_sumcount,
				   $result_testdata{$testname});
	}
	
	# Calculate resulting sumcount

	# Store result
	set_info_entry(\%result, \%result_testdata, $result_sumcount,
		       $result_funcdata, $checkdata1, $lines_found,
		       $lines_hit);

	return(\%result);
}


#
# combine_info_files(info_ref1, info_ref2)
#
# Combine .info data in hashes referenced by INFO_REF1 and INFO_REF2. Return
# reference to resulting hash.
#

sub combine_info_files($$)
{
	my %hash1 = %{$_[0]};
	my %hash2 = %{$_[1]};
	my $filename;

	foreach $filename (keys(%hash2))
	{
		if ($hash1{$filename})
		{
			# Entry already exists in hash1, combine them
			$hash1{$filename} =
				combine_info_entries($hash1{$filename},
						     $hash2{$filename},
						     $filename);
		}
		else
		{
			# Entry is unique in both hashes, simply add to
			# resulting hash
			$hash1{$filename} = $hash2{$filename};
		}
	}

	return(\%hash1);
}


#
# add_traces()
#

sub add_traces()
{
	my $total_trace;
	my $current_trace;
	my $tracefile;
	local *INFO_HANDLE;

	info("Combining tracefiles.\n");

	foreach $tracefile (@add_tracefile)
	{
		$current_trace = read_info_file($tracefile);
		if ($total_trace)
		{
			$total_trace = combine_info_files($total_trace,
							  $current_trace);
		}
		else
		{
			$total_trace = $current_trace;
		}
	}

	# Write combined data
	if ($to_file)
	{
		info("Writing data to $output_filename\n");
		open(INFO_HANDLE, ">$output_filename")
			or die("ERROR: cannot write to $output_filename!\n");
		write_info_file(*INFO_HANDLE, $total_trace);
		close(*INFO_HANDLE);
	}
	else
	{
		write_info_file(*STDOUT, $total_trace);
	}
}


#
# write_info_file(filehandle, data)
#

sub write_info_file(*$)
{
	local *INFO_HANDLE = $_[0];
	my %data = %{$_[1]};
	my $source_file;
	my $entry;
	my $testdata;
	my $sumcount;
	my $funcdata;
	my $checkdata;
	my $testname;
	my $line;
	my $testcount;
	my $found;
	my $hit;

	foreach $source_file (keys(%data))
	{
		$entry = $data{$source_file};
		($testdata, $sumcount, $funcdata, $checkdata) =
			get_info_entry($entry);
		foreach $testname (keys(%{$testdata}))
		{
			$testcount = $testdata->{$testname};
			$found = 0;
			$hit   = 0;

			print(INFO_HANDLE "TN:$testname\n");
			print(INFO_HANDLE "SF:$source_file\n");

			foreach $line (sort({$a <=> $b} keys(%{$funcdata})))
			{
				print(INFO_HANDLE "FN:$line,".
				      $funcdata->{$line}."\n");
			}

			foreach $line (sort({$a <=> $b} keys(%{$testcount})))
			{
				print(INFO_HANDLE "DA:$line,".
				      $testcount->{$line}.
				      (defined($checkdata->{$line}) &&
				       $checksum ?
				       ",".$checkdata->{$line} : "")."\n");
				$found++;
				if ($testcount->{$line} > 0)
				{
					$hit++;
				}

			}
			print(INFO_HANDLE "LF:$found\n");
			print(INFO_HANDLE "LH:$hit\n");
			print(INFO_HANDLE "end_of_record\n");
		}
	}
}


#
# transform_pattern(pattern)
#
# Transform shell wildcard expression to equivalent PERL regular expression.
# Return transformed pattern.
#

sub transform_pattern($)
{
	my $pattern = $_[0];

	# Escape special chars

	$pattern =~ s/\\/\\\\/g;
	$pattern =~ s/\//\\\//g;
	$pattern =~ s/\^/\\\^/g;
	$pattern =~ s/\$/\\\$/g;
	$pattern =~ s/\(/\\\(/g;
	$pattern =~ s/\)/\\\)/g;
	$pattern =~ s/\[/\\\[/g;
	$pattern =~ s/\]/\\\]/g;
	$pattern =~ s/\{/\\\{/g;
	$pattern =~ s/\}/\\\}/g;
	$pattern =~ s/\./\\\./g;
	$pattern =~ s/\,/\\\,/g;
	$pattern =~ s/\|/\\\|/g;
	$pattern =~ s/\+/\\\+/g;
	$pattern =~ s/\!/\\\!/g;

	# Transform ? => (.) and * => (.*)

	$pattern =~ s/\*/\(\.\*\)/g;
	$pattern =~ s/\?/\(\.\)/g;

	return $pattern;
}


#
# extract()
#

sub extract()
{
	my $data = read_info_file($extract);
	my $filename;
	my $keep;
	my $pattern;
	my @pattern_list;
	my $extracted = 0;
	local *INFO_HANDLE;

	# Need perlreg expressions instead of shell pattern
	@pattern_list = map({ transform_pattern($_); } @ARGV);

	# Filter out files which do not match any pattern
	foreach $filename (sort(keys(%{$data})))
	{
		$keep = 0;

		foreach $pattern (@pattern_list)
		{
			$keep ||= ($filename =~ (/^$pattern$/));
		}


		if (!$keep)
		{
			delete($data->{$filename});
		}
		else
		{
			info("Extracting $filename\n"),
			$extracted++;
		}
	}

	# Write extracted data
	if ($to_file)
	{
		info("Extracted $extracted files\n");
		info("Writing data to $output_filename\n");
		open(INFO_HANDLE, ">$output_filename")
			or die("ERROR: cannot write to $output_filename!\n");
		write_info_file(*INFO_HANDLE, $data);
		close(*INFO_HANDLE);
	}
	else
	{
		write_info_file(*STDOUT, $data);
	}
}


#
# remove()
#

sub remove()
{
	my $data = read_info_file($remove);
	my $filename;
	my $match_found;
	my $pattern;
	my @pattern_list;
	my $removed = 0;
	local *INFO_HANDLE;

	# Need perlreg expressions instead of shell pattern
	@pattern_list = map({ transform_pattern($_); } @ARGV);

	# Filter out files that match the pattern
	foreach $filename (sort(keys(%{$data})))
	{
		$match_found = 0;

		foreach $pattern (@pattern_list)
		{
			$match_found ||= ($filename =~ (/$pattern$/));
		}


		if ($match_found)
		{
			delete($data->{$filename});
			info("Removing $filename\n"),
			$removed++;
		}
	}

	# Write data
	if ($to_file)
	{
		info("Deleted $removed files\n");
		info("Writing data to $output_filename\n");
		open(INFO_HANDLE, ">$output_filename")
			or die("ERROR: cannot write to $output_filename!\n");
		write_info_file(*INFO_HANDLE, $data);
		close(*INFO_HANDLE);
	}
	else
	{
		write_info_file(*STDOUT, $data);
	}
}


#
# list()
#

sub list()
{
	my $data = read_info_file($list);
	my $filename;
	my $found;
	my $hit;
	my $entry;

	info("Listing contents of $list:\n");

	# List all files
	foreach $filename (sort(keys(%{$data})))
	{
		$entry = $data->{$filename};
		(undef, undef, undef, undef, $found, $hit) =
			get_info_entry($entry);
		printf("$filename: $hit of $found lines hit\n");
	}
}


#
# get_common_filename(filename1, filename2)
#
# Check for filename components which are common to FILENAME1 and FILENAME2.
# Upon success, return
#
#   (common, path1, path2)
#
#  or 'undef' in case there are no such parts.
#

sub get_common_filename($$)
{
        my @list1 = split("/", $_[0]);
        my @list2 = split("/", $_[1]);
	my @result;

	# Work in reverse order, i.e. beginning with the filename itself
	while (@list1 && @list2 && ($list1[$#list1] eq $list2[$#list2]))
	{
		unshift(@result, pop(@list1));
		pop(@list2);
	}

	# Did we find any similarities?
	if (scalar(@result) > 0)
	{
	        return (join("/", @result), join("/", @list1),
			join("/", @list2));
	}
	else
	{
		return undef;
	}
}


#
# strip_directories($path, $depth)
#
# Remove DEPTH leading directory levels from PATH.
#

sub strip_directories($$)
{
	my $filename = $_[0];
	my $depth = $_[1];
	my $i;

	if (!defined($depth) || ($depth < 1))
	{
		return $filename;
	}
	for ($i = 0; $i < $depth; $i++)
	{
		$filename =~ s/^[^\/]*\/+(.*)$/$1/;
	}
	return $filename;
}


#
# read_diff(filename)
#
# Read diff output from FILENAME to memory. The diff file has to follow the
# format generated by 'diff -u'. Returns a list of hash references:
#
#   (mapping, path mapping)
#
#   mapping:   filename -> reference to line hash
#   line hash: line number in new file -> corresponding line number in old file
#
#   path mapping:  filename -> old filename
#
# Die in case of error.
#

sub read_diff($)
{
	my $diff_file = $_[0];	# Name of diff file
	my %diff;		# Resulting mapping filename -> line hash
	my %paths;		# Resulting mapping old path  -> new path
	my $mapping;		# Reference to current line hash
	my $line;		# Contents of current line
	my $num_old;		# Current line number in old file
	my $num_new;		# Current line number in new file
	my $file_old;		# Name of old file in diff section
	my $file_new;		# Name of new file in diff section
	my $filename;		# Name of common filename of diff section
	my $in_block = 0;	# Non-zero while we are inside a diff block
	local *HANDLE;		# File handle for reading the diff file

	info("Reading diff $diff_file\n");

	# Check if file exists and is readable
	stat($diff_file);
	if (!(-r _))
	{
		die("ERROR: cannot read file $diff_file!\n");
	}

	# Check if this is really a plain file
	if (!(-f _))
	{
		die("ERROR: not a plain file: $diff_file!\n");
	}

	# Check for .gz extension
	if ($diff_file =~ /\.gz$/)
	{
		# Check for availability of GZIP tool
		system_no_output(1, "gunzip", "-h")
			and die("ERROR: gunzip command not available!\n");

		# Check integrity of compressed file
		system_no_output(1, "gunzip", "-t", $diff_file)
			and die("ERROR: integrity check failed for ".
				"compressed file $diff_file!\n");

		# Open compressed file
		open(HANDLE, "gunzip -c $diff_file|")
			or die("ERROR: cannot start gunzip to decompress ".
			       "file $_[0]!\n");
	}
	else
	{
		# Open decompressed file
		open(HANDLE, $diff_file)
			or die("ERROR: cannot read file $_[0]!\n");
	}

	# Parse diff file line by line
	while (<HANDLE>)
	{
		chomp($_);
		$line = $_;

		foreach ($line)
		{
			# Filename of old file:
			# --- <filename> <date>
			/^--- (\S+)/ && do
			{
				$file_old = strip_directories($1, $strip);
				last;
			};
			# Filename of new file:
			# +++ <filename> <date>
			/^\+\+\+ (\S+)/ && do
			{
				# Add last file to resulting hash
				if ($filename)
				{
					my %new_hash;
					$diff{$filename} = $mapping;
					$mapping = \%new_hash;
				}
				$file_new = strip_directories($1, $strip);
				$filename = $file_old;
				$paths{$filename} = $file_new;
				$num_old = 1;
				$num_new = 1;
				last;
			};
			# Start of diff block:
			# @@ -old_start,old_num, +new_start,new_num @@
			/^\@\@\s+-(\d+),(\d+)\s+\+(\d+),(\d+)\s+\@\@$/ && do
			{
			$in_block = 1;
			while ($num_old < $1)
			{
				$mapping->{$num_new} = $num_old;
				$num_old++;
				$num_new++;
			}
			last;
			};
			# Unchanged line
			# <line starts with blank>
			/^ / && do
			{
				if ($in_block == 0)
				{
					last;
				}
				$mapping->{$num_new} = $num_old;
				$num_old++;
				$num_new++;
				last;
			};
			# Line as seen in old file
			# <line starts with '-'>
			/^-/ && do
			{
				if ($in_block == 0)
				{
					last;
				}
				$num_old++;
				last;
			};
			# Line as seen in new file
			# <line starts with '+'>
			/^\+/ && do
			{
				if ($in_block == 0)
				{
					last;
				}
				$num_new++;
				last;
			};
			# Empty line
			/^$/ && do
			{
				if ($in_block == 0)
				{
					last;
				}
				$mapping->{$num_new} = $num_old;
				$num_old++;
				$num_new++;
				last;
			};
		}
	}

	close(HANDLE);

	# Add final diff file section to resulting hash
	if ($filename)
	{
		$diff{$filename} = $mapping;
	}

	if (!%diff)
	{
		die("ERROR: no valid diff data found in $diff_file!\n".
		    "Make sure to use 'diff -u' when generating the diff ".
		    "file.\n");
	}
	return (\%diff, \%paths);
}


#
# apply_diff($count_data, $line_hash)
#
# Transform count data using a mapping of lines:
#
#   $count_data: reference to hash: line number -> data
#   $line_hash:  reference to hash: line number new -> line number old
#
# Return a reference to transformed count data.
#

sub apply_diff($$)
{
	my $count_data = $_[0];	# Reference to data hash: line -> hash
	my $line_hash = $_[1];	# Reference to line hash: new line -> old line
	my %result;		# Resulting hash
	my $last_new = 0;	# Last new line number found in line hash
	my $last_old = 0;	# Last old line number found in line hash

	# Iterate all new line numbers found in the diff
	foreach (sort({$a <=> $b} keys(%{$line_hash})))
	{
		$last_new = $_;
		$last_old = $line_hash->{$last_new};

		# Is there data associated with the corresponding old line?
		if (defined($count_data->{$line_hash->{$_}}))
		{
			# Copy data to new hash with a new line number
			$result{$_} = $count_data->{$line_hash->{$_}};
		}
	}
	# Transform all other lines which come after the last diff entry
	foreach (sort({$a <=> $b} keys(%{$count_data})))
	{
		if ($_ < $last_old)
		{
			# Skip lines which were covered by line hash
			next;
		}
		# Copy data to new hash with an offset
		$result{$_ + ($last_new - $last_old)} = $count_data->{$_};
	}

	return \%result;
}


#
# get_line_hash($filename, $diff_data, $path_data)
#
# Find line hash in DIFF_DATA which matches FILENAME. On succes, return list
# line hash. or undef in case of no match. Die if more than one line hashes in
# DIFF_DATA match.
#

sub get_line_hash($$$)
{
	my $filename = $_[0];
	my $diff_data = $_[1];
	my $path_data = $_[2];
	my $conversion;
	my $old_path;
	my $new_path;
	my $diff_name;
	my $common;
	my $old_depth;
	my $new_depth;

	foreach (keys(%{$diff_data}))
	{
		# Try to match diff filename with filename
		if ($filename =~ /^\Q$diff_path\E\/$_$/)
		{
			if ($diff_name)
			{
				# Two files match, choose the more specific one
				# (the one with more path components)
				$old_depth = ($diff_name =~ tr/\///);
				$new_depth = (tr/\///);
				if ($old_depth == $new_depth)
				{
					die("ERROR: diff file contains ".
					    "ambiguous entries for ".
					    "$filename\n");
				}
				elsif ($new_depth > $old_depth)
				{
					$diff_name = $_;
				}
			}
			else
			{
				$diff_name = $_;
			}
		};
	}
	if ($diff_name)
	{
		# Get converted path
		if ($filename =~ /^(.*)$diff_name$/)
		{
			($common, $old_path, $new_path) =
				get_common_filename($filename,
					$1.$path_data->{$diff_name});
		}
		return ($diff_data->{$diff_name}, $old_path, $new_path);
	}
	else
	{
		return undef;
	}
}


#
# convert_paths(trace_data, path_conversion_data)
#
# Rename all paths in TRACE_DATA which show up in PATH_CONVERSION_DATA.
#

sub convert_paths($$)
{
	my $trace_data = $_[0];
	my $path_conversion_data = $_[1];
	my $filename;
	my $new_path;

	if (scalar(keys(%{$path_conversion_data})) == 0)
	{
		info("No path conversion data available.\n");
		return;
	}

	# Expand path conversion list
	foreach $filename (keys(%{$path_conversion_data}))
	{
		$new_path = $path_conversion_data->{$filename};
		while (($filename =~ s/^(.*)\/[^\/]+$/$1/) &&
		       ($new_path =~ s/^(.*)\/[^\/]+$/$1/) &&
		       ($filename ne $new_path))
		{
			$path_conversion_data->{$filename} = $new_path;
		}
	}

	# Adjust paths
	FILENAME: foreach $filename (keys(%{$trace_data}))
	{
		# Find a path in our conversion table that matches, starting
		# with the longest path
		foreach (sort({length($b) <=> length($a)}
			      keys(%{$path_conversion_data})))
		{
			# Is this path a prefix of our filename?
			if (!($filename =~ /^$_(.*)$/))
			{
				next;
			}
			$new_path = $path_conversion_data->{$_}.$1;

			# Make sure not to overwrite an existing entry under
			# that path name
			if ($trace_data->{$new_path})
			{
				# Need to combine entries
				$trace_data->{$new_path} =
					combine_info_entries(
						$trace_data->{$filename},
						$trace_data->{$new_path},
						$filename);
			}
			else
			{
				# Simply rename entry
				$trace_data->{$new_path} =
					$trace_data->{$filename};
			}
			delete($trace_data->{$filename});
			next FILENAME;
		}
		info("No conversion available for filename $filename\n");
	}
}


#
# diff()
#

sub diff()
{
	my $trace_data = read_info_file($diff);
	my $diff_data;
	my $path_data;
	my $old_path;
	my $new_path;
	my %path_conversion_data;
	my $filename;
	my $line_hash;
	my $new_name;
	my $entry;
	my $testdata;
	my $testname;
	my $sumcount;
	my $funcdata;
	my $checkdata;
	my $found;
	my $hit;
	my $converted = 0;
	my $unchanged = 0;
	local *INFO_HANDLE;

	($diff_data, $path_data) = read_diff($ARGV[0]);

        foreach $filename (sort(keys(%{$trace_data})))
        {
		# Find a diff section corresponding to this file
		($line_hash, $old_path, $new_path) =
			get_line_hash($filename, $diff_data, $path_data);
		if (!$line_hash)
		{
			# There's no diff section for this file
			$unchanged++;
			next;
		}
		$converted++;
		if ($old_path && $new_path && ($old_path ne $new_path))
		{
			$path_conversion_data{$old_path} = $new_path;
		}
		# Check for deleted files
		if (scalar(keys(%{$line_hash})) == 0)
		{
			info("Removing $filename\n");
			delete($trace_data->{$filename});
			next;
		}
		info("Converting $filename\n");
		$entry = $trace_data->{$filename};
		($testdata, $sumcount, $funcdata, $checkdata) =
			get_info_entry($entry);
		# Convert test data
		foreach $testname (keys(%{$testdata}))
		{
			$testdata->{$testname} =
				apply_diff($testdata->{$testname}, $line_hash);
			# Remove empty sets of test data
			if (scalar(keys(%{$testdata->{$testname}})) == 0)
			{
				delete($testdata->{$testname});
			}
		}
		# Rename test data to indicate conversion
		foreach $testname (keys(%{$testdata}))
		{
			# Skip testnames which already contain an extension
			if ($testname =~ /,[^,]+$/)
			{
				next;
			}
			# Check for name conflict
			if (defined($testdata->{$testname.",diff"}))
			{
				# Add counts
				($testdata->{$testname}) = add_counts(
					$testdata->{$testname},
					$testdata->{$testname.",diff"});
				delete($testdata->{$testname.",diff"});
			}
			$testdata->{$testname.",diff"} = $testdata->{$testname};
			delete($testdata->{$testname});
		}
		# Convert summary of test data
		$sumcount = apply_diff($sumcount, $line_hash);
		# Convert function data
		$funcdata = apply_diff($funcdata, $line_hash);
		# Convert checksum data
		$checkdata = apply_diff($checkdata, $line_hash);
		# Update found/hit numbers
		$found = 0;
		$hit = 0;
		foreach (keys(%{$sumcount}))
		{
			$found++;
			if ($sumcount->{$_} > 0)
			{
				$hit++;
			}
		}
		if ($found > 0)
		{
			# Store converted entry
			set_info_entry($entry, $testdata, $sumcount, $funcdata,
				       $checkdata, $found, $hit);
		}
		else
		{
			# Remove empty data set
			delete($trace_data->{$filename});
		}
        }

	# Convert filenames as well if requested
	if ($convert_filenames)
	{
		convert_paths($trace_data, \%path_conversion_data);
	}

	info("$converted entr".($converted != 1 ? "ies" : "y")." converted, ".
	     "$unchanged entr".($unchanged != 1 ? "ies" : "y")." left ".
	     "unchanged.\n");

	# Write data
	if ($to_file)
	{
		info("Writing data to $output_filename\n");
		open(INFO_HANDLE, ">$output_filename")
			or die("ERROR: cannot write to $output_filename!\n");
		write_info_file(*INFO_HANDLE, $trace_data);
		close(*INFO_HANDLE);
	}
	else
	{
		write_info_file(*STDOUT, $trace_data);
	}
}


#
# system_no_output(mode, parameters)
#
# Call an external program using PARAMETERS while suppressing depending on
# the value of MODE:
#
#   MODE & 1: suppress STDOUT
#   MODE & 2: suppress STDERR
#
# Return 0 on success, non-zero otherwise.
#

sub system_no_output($@)
{
	my $mode = shift;
	my $result;
	local *OLD_STDERR;
	local *OLD_STDOUT;

	# Save old stdout and stderr handles
	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
	($mode & 2) && open(OLD_STDERR, ">>&STDERR");

	# Redirect to /dev/null
	($mode & 1) && open(STDOUT, ">/dev/null");
	($mode & 2) && open(STDERR, ">/dev/null");
 
	system(@_);
	$result = $?;

	# Close redirected handles
	($mode & 1) && close(STDOUT);
	($mode & 2) && close(STDERR);

	# Restore old handles
	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
 
	return $result;
}


#
# read_config(filename)
#
# Read configuration file FILENAME and return a reference to a hash containing
# all valid key=value pairs found.
#

sub read_config($)
{
	my $filename = $_[0];
	my %result;
	my $key;
	my $value;
	local *HANDLE;

	if (!open(HANDLE, "<$filename"))
	{
		warn("WARNING: cannot read configuration file $filename\n");
		return undef;
	}
	while (<HANDLE>)
	{
		chomp;
		# Skip comments
		s/#.*//;
		# Remove leading blanks
		s/^\s+//;
		# Remove trailing blanks
		s/\s+$//;
		next unless length;
		($key, $value) = split(/\s*=\s*/, $_, 2);
		if (defined($key) && defined($value))
		{
			$result{$key} = $value;
		}
		else
		{
			warn("WARNING: malformed statement in line $. ".
			     "of configuration file $filename\n");
		}
	}
	close(HANDLE);
	return \%result;
}


#
# apply_config(REF)
#
# REF is a reference to a hash containing the following mapping:
#
#   key_string => var_ref
#
# where KEY_STRING is a keyword and VAR_REF is a reference to an associated
# variable. If the global configuration hash CONFIG contains a value for
# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
#

sub apply_config($)
{
	my $ref = $_[0];

	foreach (keys(%{$ref}))
	{
		if (defined($config->{$_}))
		{
			${$ref->{$_}} = $config->{$_};
		}
	}
}

[-- Attachment #5: lcov-diff.patch --]
[-- Type: application/octet-stream, Size: 1535 bytes --]

--- lcov	2009-02-25 18:56:39.000000000 +0530
+++ lcov-mod	2009-02-25 18:57:23.000000000 +0530
@@ -67,9 +67,11 @@
 our $lcov_version	= "LTP GCOV extension version 1.6";
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 
-# Names of the GCOV kernel module
-our @gcovmod = ("gcov-prof", "gcov-proc");
-
+# Hash Table which contains gcov module name as well as directory path
+our %gcov_module_dir = ( "gcov-prof"     => "/proc/gcov",
+                         "gcov-proc"     => "/proc/gcov",
+                         "xen-gcov-proc" => "/proc/xen/gcov" );
+                                                   
 # Directory containing gcov kernel files
 our $gcov_dir = "/proc/gcov";
 
@@ -736,7 +738,7 @@
 
 sub check_and_load_kernel_module()
 {
-	my $module_name;
+	my $module_name="";
 
 	# Is it loaded already?
 	stat("$gcov_dir");
@@ -759,9 +761,14 @@
 		    "kernel coverage data!\n");
 	}
 
-	# Try some possibilities of where the gcov kernel module may be found
-	foreach $module_name (@gcovmod)
+	# Try some possibilities of where the gcov module may be found.
+	# And there is a hash table called %gcov_module_dir which has the module name
+	# associated with directory path. Based on the $gcov_dir select the 
+	# particular module to load.
+	while ((my $mod_name, my $gcov_path) = each (%gcov_module_dir))
 	{
+		$module_name = $mod_name if($gcov_path eq $gcov_dir);
+
 		# Try to load module from system wide module directory
 		# /lib/modules
 		if (system_no_output(3, $modprobe_tool, $module_name) == 0)

[-- Attachment #6: README --]
[-- Type: application/octet-stream, Size: 3390 bytes --]


---------------------------------------------------------------
- README file for the GCOV extention (LCOV)
- Includes access of Kernel/HYPERVISOR/Userspace Program
---------------------------------------------------------------


Description
-----------

  LCOV is an extension of GCOV, a GNU tool which provides information about
  what parts of a program are actually executed (i.e. "covered") while running
  a particular test case. The functionality is HTML based output. Where coverage 
  rates are additionaly indicated using bar graphs and specific colors.

Contents
---------
1. About lcov config file (/etc/lcovrc)
2. An example of how to access kernel coverage data 
3. An example of how to access HYPERVISOR coverage data
4. An example of how to access coverage data for a user space program


1. About lcov config file (/etc/lcovrc)
----------------------------------------

 The LCOV package is available as either RPM or tarball from:
     
    http://ltp.sourceforge.net/coverage/lcov.php

 LCOV has been modified to support both kernel and xen hypervisor by editing lcov config file.
 LCOV config file will be located in /etc/lcovrc, there is variable called lcov_gcov_dir which
 has the directory path containing gcov kernel files. 

	* Kernel directory path lcov_gcov_dir = /proc/gcov
	* Xen hypervisor directory path lcov_gcov_dir = /proc/xen/gcov

 By defualt config file has kernel directory path (i.e /proc/gcov). User has to specify either 
 kernel path or xen hypervisor path. 	

2. An example of how to access kernel coverage data
----------------------------------------------------
Requirements: get and install gcov-kernel package

 User has to make sure that lcov_gcov_dir should hold the directory path "/proc/gcov" in LCOV 
 config file (i.e /etc/lcovrc).

  a) Capturing the current coverage state to a file

     lcov --capture --output-file kernel.info

  b) Resetting counters

     lcov --zerocounters

  c) Getting HTML output

     genhtml kernel.info

Point the web browser of your choice to the resulting index.html file.

3. An example of how to access HYPERVISOR coverage data
----------------------------------------------------
Requirements: get and install GCOV-HYPERVISOR package

 User has to make sure that lcov_gcov_dir should hold the directory path "/proc/xen/gcov" in LCOV 
 config file (i.e /etc/lcovrc). 

  a) Capturing the current coverage state to a file

     lcov --capture --output-file kernel.info

  b) Resetting counters

     lcov --zerocounters

  c) Getting HTML output

     genhtml kernel.info

Point the web browser of your choice to the resulting index.html file.

4. An example of how to access coverage data for a user space program
---------------------------------------------------------------------
Requirements: compile the program in question using GCC with the options
-fprofile-arcs and -ftest-coverage. Assuming the compile directory is called
"appdir", do the following:

  a) Resetting counters

     lcov --directory appdir --zerocounters

  b) Capturing the current coverage state to a file (works only after the
     application has been started and stopped at least once)

     lcov --directory appdir --capture --output-file app.info

  c) Getting HTML output

     genhtml app.info

Point the web browser of your choice to the resulting index.html file.





































 







[-- Attachment #7: lcov-screenshot --]
[-- Type: application/octet-stream, Size: 126300 bytes --]

[-- Attachment #8: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
  2009-02-26 15:09 [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor) Tej
@ 2009-02-27 19:02 ` Gianluca Guida
  2009-02-28  7:23   ` Tej
  2009-03-02 16:06   ` Tej
  0 siblings, 2 replies; 8+ messages in thread
From: Gianluca Guida @ 2009-02-27 19:02 UTC (permalink / raw)
  To: Tej; +Cc: George Dunlap, xen-devel, Keir Fraser

Hi,

Tej wrote:
> Sending Patches for the 64 bit Hypervisor, We have tested patches on
> AMD-64 (Athlon(tm) 64 X2 Dual Core Processor 4600+) machine with
> gcc-4.2 & gcc-3.4. Please refer the previous GCOV RFC in same mail for
> more info on Hypervisor profiling.
> We have added support to 32bit and 64bit Kernel.
> 
> In addition to patches for hypervisor profiling, we did a little work
> on *lcov* to work with hypervisor.
> README could be useful, for naive lcov user. locv-diff.patch show our
> change in lcov scripts.
> 
> any comments, feedback and suggestion are more than welcome

While I still need to test the patch (building 3.3 right now) and to 
understand gcov internals, I think that a few comments can be done, 
mostly aestethicals.

- xen coding style: Using four-spaces tabs is generally the tradition. 
Also I do prefer to have brackets that start code blocks on a new line 
aligned to the previous line, but that's not followed everywhere in the 
code.

- Makefiles: while the num=$*.c is still a mystery to me, my question 
is: do you really need to make links with different names to files 
compiled multiple times? If so, it would be useful to remove them on 
'make clean'.
Also, it would be useful to make this feature enabled with a 
compile-time option.

More comments to follow as I test it.

Thanks,
Gianluca

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
  2009-02-27 19:02 ` Gianluca Guida
@ 2009-02-28  7:23   ` Tej
  2009-03-02 16:06   ` Tej
  1 sibling, 0 replies; 8+ messages in thread
From: Tej @ 2009-02-28  7:23 UTC (permalink / raw)
  To: Gianluca Guida; +Cc: George Dunlap, xen-devel, Keir Fraser

On Sat, Feb 28, 2009 at 12:32 AM, Gianluca Guida
<gianluca.guida@eu.citrix.com> wrote:
> Hi,
>
> Tej wrote:
>>
>> Sending Patches for the 64 bit Hypervisor, We have tested patches on
>> AMD-64 (Athlon(tm) 64 X2 Dual Core Processor 4600+) machine with
>> gcc-4.2 & gcc-3.4. Please refer the previous GCOV RFC in same mail for
>> more info on Hypervisor profiling.
>> We have added support to 32bit and 64bit Kernel.
>>
>> In addition to patches for hypervisor profiling, we did a little work
>> on *lcov* to work with hypervisor.
>> README could be useful, for naive lcov user. locv-diff.patch show our
>> change in lcov scripts.
>>
>> any comments, feedback and suggestion are more than welcome
>
> While I still need to test the patch (building 3.3 right now) and to
> understand gcov internals, I think that a few comments can be done, mostly
> aestethicals.
>
> - xen coding style: Using four-spaces tabs is generally the tradition. Also
> I do prefer to have brackets that start code blocks on a new line aligned to
> the previous line, but that's not followed everywhere in the code.

thanks for your comments and general feedback
Generally we uses Lindent script for tab/alignment problem...  i will
incorporate above said coding styles and resubmit
>
> - Makefiles: while the num=$*.c is still a mystery to me, my question is: do
> you really need to make links with different names to files compiled

num=$*.c changes mainly added to run lcov smoothly... Let me take an example

in hap directory Makefile generate guest_walk_%level.o and gcov option
generates guest_walk_%level.gcno... but there is no
guest_walk_%level.c files , so lcov script give me warning/error
saying guest_walk_%level.gcno file does not have correspoding *.c.. so
we have added some Makefile trick to generate *.c link at compile
time.

> multiple times? If so, it would be useful to remove them on 'make clean'.
nice point
thats absolutely needed,  so i will incorporated the changes

> Also, it would be useful to make this feature enabled with a compile-time
> option.

i will look into this also and resubmit the patches on Monday...
>
> More comments to follow as I test it.

thanks for your review and comments

>
> Thanks,
> Gianluca
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
  2009-02-27 19:02 ` Gianluca Guida
  2009-02-28  7:23   ` Tej
@ 2009-03-02 16:06   ` Tej
       [not found]     ` <f1c9d250903252359p13f2fe47r84674d183fe8629f@mail.gmail.com>
  1 sibling, 1 reply; 8+ messages in thread
From: Tej @ 2009-03-02 16:06 UTC (permalink / raw)
  To: Gianluca Guida; +Cc: George Dunlap, xen-devel, Keir Fraser

[-- Attachment #1: Type: text/plain, Size: 1582 bytes --]

On Sat, Feb 28, 2009 at 12:32 AM, Gianluca Guida
<gianluca.guida@eu.citrix.com> wrote:
> Hi,
>
> Tej wrote:
>>
>> Sending Patches for the 64 bit Hypervisor, We have tested patches on
>> AMD-64 (Athlon(tm) 64 X2 Dual Core Processor 4600+) machine with
>> gcc-4.2 & gcc-3.4. Please refer the previous GCOV RFC in same mail for
>> more info on Hypervisor profiling.
>> We have added support to 32bit and 64bit Kernel.
>>
>> In addition to patches for hypervisor profiling, we did a little work
>> on *lcov* to work with hypervisor.
>> README could be useful, for naive lcov user. locv-diff.patch show our
>> change in lcov scripts.
>>
>> any comments, feedback and suggestion are more than welcome
>
> While I still need to test the patch (building 3.3 right now) and to
> understand gcov internals, I think that a few comments can be done, mostly
> aestethicals.
>
> - xen coding style: Using four-spaces tabs is generally the tradition. Also
> I do prefer to have brackets that start code blocks on a new line aligned to
> the previous line, but that's not followed everywhere in the code.
>
> - Makefiles: while the num=$*.c is still a mystery to me, my question is: do
> you really need to make links with different names to files compiled
> multiple times? If so, it would be useful to remove them on 'make clean'.
> Also, it would be useful to make this feature enabled with a compile-time
> option.

Attached patches address the four-spaces tabs and make clean issues.
Hope the code is more clean and readable

>
> More comments to follow as I test it.
>
> Thanks,
> Gianluca
>

[-- Attachment #2: linux-2.6.18-gcov-v3.patch --]
[-- Type: application/octet-stream, Size: 41869 bytes --]

diff -r 51decc39e5e7 drivers/xen/Kconfig
--- a/drivers/xen/Kconfig	Wed Jan 28 13:42:09 2009 +0000
+++ b/drivers/xen/Kconfig	Mon Mar 02 17:57:59 2009 +0530
@@ -263,6 +263,11 @@
 	default y
 	help
 	  Xen hypervisor attributes will show up under /sys/hypervisor/.
+config XEN_GCOV_PROC
+        tristate "Code covarage for hypervisor"
+        help
+         Select to profile Xen Hypervisor only. Create /proc/xen/gcov entry
+         which provides access to the current code coverage state of xen hypervisor
 
 choice
 	prompt "Xen version compatibility"
diff -r 51decc39e5e7 drivers/xen/Makefile
--- a/drivers/xen/Makefile	Wed Jan 28 13:42:09 2009 +0000
+++ b/drivers/xen/Makefile	Mon Mar 02 17:57:59 2009 +0530
@@ -3,6 +3,7 @@
 obj-y	+= evtchn/
 obj-y	+= xenbus/
 obj-y	+= char/
+obj-y	+= gcov/
 
 obj-y	+= util.o
 obj-$(CONFIG_XEN_BALLOON)		+= balloon/
diff -r 51decc39e5e7 drivers/xen/core/xen_proc.c
--- a/drivers/xen/core/xen_proc.c	Wed Jan 28 13:42:09 2009 +0000
+++ b/drivers/xen/core/xen_proc.c	Mon Mar 02 17:57:59 2009 +0530
@@ -3,7 +3,7 @@
 #include <linux/proc_fs.h>
 #include <xen/xen_proc.h>
 
-static struct proc_dir_entry *xen_base;
+struct proc_dir_entry *xen_base;
 
 struct proc_dir_entry *create_xen_proc_entry(const char *name, mode_t mode)
 {
@@ -20,4 +20,5 @@
 	remove_proc_entry(name, xen_base);
 }
 
+EXPORT_SYMBOL(xen_base);
 EXPORT_SYMBOL_GPL(remove_xen_proc_entry); 
diff -r 51decc39e5e7 drivers/xen/gcov/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/Makefile	Mon Mar 02 17:57:59 2009 +0530
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_GCOV_PROC) := xen-gcov-proc.o
diff -r 51decc39e5e7 drivers/xen/gcov/xen-gcov-proc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/xen-gcov-proc.c	Mon Mar 02 17:57:59 2009 +0530
@@ -0,0 +1,1134 @@
+/**
+ *  @file xen-gcov-proc.c
+ *
+ *  @Author: Hubertus Franke <frankeh@us.ibm.com>
+ *           Rajan Ravindran <rajancr@us.ibm.com>
+ *           Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
+ *           Paul Larson
+ *
+ *  Modified by tej parkash and team for Xen (tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/xen-gcov.h>
+#include <xen/evtchn.h>
+#include <xen/xen_proc.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+
+MODULE_LICENSE("GPL");
+
+#define GCOV_PROC_HEADER    "xen-gcov-proc: "
+#define GCOV_PROC_ROOT        "gcov"
+#define GCOV_PROC_VMLINUX    "vmlinux"
+#define PAD8(x)            (((x) + 7) & ~7)
+
+/* If set to non-zero, create links to additional files in proc filesystem
+ * entries. */
+static int xen_gcov_link = 1;
+
+/*count update frequency*/
+static int xen_gcov_update = 5;
+
+#ifdef MODULE
+/* Parameter handling. */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+/* module_param is a 2.6 thing (though there are compat macros since 2.4.25) */
+MODULE_PARM(xen_gcov_link, "i");
+MODULE_PARM(xen_gcov_update, "i");
+#else
+module_param(xen_gcov_link, int, 0400);
+module_param(xen_gcov_update, int, 0400);
+#endif                /* LINUX_VERSION_CODE */
+
+MODULE_PARM_DESC(xen_gcov_link, "If set to non-zero, create links to additional "
+         "files in proc filesystem entries");
+MODULE_PARM_DESC(xen_gcov_update, "decides the frequency of hypervisor count"
+         "update and default value is 5sec.");
+#else                /* MODULE */
+/* Called when 'xen_gcov_link' is specified on the kernel command line. */
+static int __init xen_gcov_link_setup(char *str)
+{
+    xen_gcov_link = (int)simple_strtol(str, NULL, 10);
+    return 1;
+}
+
+__setup("xen_gcov_link=", xen_gcov_link_setup);
+
+static int __init xen_gcov_update_setup(char *str)
+{
+    xen_gcov_update = (int)simple_strtol(str, NULL, 10);
+    return 1;
+}
+
+__setup("xen_gcov_update=", xen_gcov_update_setup);
+
+#endif                /* MODULE */
+
+/* create sysclt entry @/proc/sys/xen
+ * accomplish the count update frequency
+ * perform: echo <num> >/proc/sys/xen/xen_gcov_update
+ */
+#ifdef CONFIG_SYSCTL
+static ctl_table gcov_table[] = {
+    {
+        .ctl_name     = 4,
+    .procname     = "xen_gcov_update",
+    .data         = &xen_gcov_update,
+    .maxlen     = sizeof(xen_gcov_update),
+    .mode         = 0644,
+    .proc_handler     = proc_dointvec,
+    }
+    ,
+    {.ctl_name = 0}
+};
+
+static ctl_table gcov_root_table[] = {
+    {
+        .ctl_name     = 123,
+        .procname     = "xen",
+        .mode         = 0555,
+        .child        = gcov_table},
+    {.ctl_name = 0}
+};
+
+/*sysclt table specific declaration*/
+static struct ctl_table_header *gcov_table_header;
+
+#endif /* CONFIG_SYSCTL */
+
+/* Protect global variables from concurrent access. */
+static DEFINE_MUTEX(gcov_mutex);
+static DEFINE_MUTEX(write_mutex);
+
+/*workqueue function*/
+static void xeno_prof(void *);
+
+/*workqueue for the calling hypervisor count head*/
+static DECLARE_WORK(work, xeno_prof, NULL);
+
+/* Filesystem entry for /proc/gcov/vmlinux */
+static struct proc_dir_entry *proc_vmlinux = NULL;
+
+/* First leaf node. */
+static struct gcov_ftree_node *leaf_nodes = NULL;
+
+/* Cached node used to speed up sequential reads in /proc/vmlinux. */
+static struct gcov_ftree_node *cached_node = NULL;
+
+/* Root node for internal data tree. */
+static struct gcov_ftree_node tree_root;
+
+struct gcov_variables *gcov_var = NULL;
+
+/* Filename extension for data files. */
+static const char *da_ending = "gcda";
+
+/* Array of filename endings to use when creating links. */
+static const char *endings[] = { "gcno", "c" };
+
+/*Info which keeps no if file in xen info to be profile*/
+unsigned int xen_file_nos;
+
+/*head of xen-gcov-info linked list*/
+struct xen_gcov_info *list_head = NULL;
+
+/* xen_sourcepath length */
+static int sourcepath_len;
+
+unsigned long xen_gcov_version;
+
+/* Create nodes for all gcda files based on xen_file_nos value */
+static inline int create_bb_list(void)
+{
+    int i;
+    struct xen_gcov_info *node = NULL, *tmp_node = NULL;
+
+    for (i = 0; i < gcov_var->n_files; i++) 
+    {
+        node = kmalloc(sizeof(struct xen_gcov_info), GFP_KERNEL);
+        node->counts[0].values =
+            kmalloc(sizeof(gcov_type) * gcov_var->ctr_num[i],
+                GFP_KERNEL);
+        node->functions =
+            kmalloc(sizeof(struct xen_gcov_fn_info) *
+                gcov_var->n_funcs[i], GFP_KERNEL);
+        node->next = NULL;
+        if (list_head == NULL) 
+        {
+            list_head = node;
+            tmp_node = node;
+        } 
+        else 
+        {
+            tmp_node->next = node;
+            tmp_node = node;
+        }
+    }
+    
+    return 0;
+}
+
+/* free memory allocated for gcov_info list */
+static inline int free_bb_list(void)
+{
+    struct xen_gcov_info *node = NULL;
+    struct xen_gcov_info *temp = list_head;
+    for (;temp!=NULL;temp = temp->next) 
+    {
+        node = temp;
+        kfree(node->counts[0].values);
+        kfree(node->functions);
+        kfree(node);
+    }
+
+    return 0;
+}
+
+/* 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 (adapted from gcc/gcov-io.h). */
+static int store_gcov_type(gcov_type value, char *dest, size_t bytes)
+{
+    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;
+
+    return 0;
+}
+
+/* Determine whether counter TYPE is active in BB. */
+static int counter_active(struct xen_gcov_info *bb, unsigned int type)
+{
+    return ((1 << type) & bb->ctr_mask);
+}
+
+/* Return size of .gcda counter section. */
+static size_t
+sizeof_counter_data(struct xen_gcov_info *bb, unsigned int row,
+            unsigned int col)
+{
+
+    struct xen_gcov_fn_info *func =
+        (struct xen_gcov_fn_info *)&bb->functions[row];
+
+    if (counter_active(bb, col)) 
+    {
+        return /* tag */ 4 + /* length */ 4 +
+            /* counters */ func->n_ctrs[col] * 8;
+    } 
+    else
+        return 0;
+}
+
+/* Return size of .gcda data section associated with FUNC.  */
+static size_t sizeof_func_data(struct xen_gcov_info *bb, unsigned int row)
+{
+    size_t result;
+    unsigned int type;
+
+    result =
+        /* tag */ 4 + /* length */ 4 + /* ident */ 4 + /* checksum */ 4;
+    for (type = 0; type < GCOV_COUNTERS; type++)
+        result += sizeof_counter_data(bb, row, type);
+    return result;
+}
+
+/* Get size of .gcda file associated with BB. */
+static size_t sizeof_da_file(struct xen_gcov_info *bb)
+{
+    size_t result;
+    unsigned int i;
+
+    result = /* magic */ 4 + /* version */ 4 + /* stamp */ 4;
+    for (i = 0; i < bb->n_functions; i++)
+        result += sizeof_func_data(bb, i);
+    return result;
+}
+
+/* Store a 32 bit unsigned integer value in GCOV format to memory at address
+ *  * BUF. */
+static void store_int32(uint32_t i, char *buf)
+{
+    uint32_t *p;
+    p = (int *)buf;
+    *p = i;
+
+}
+
+/* Store a 64 bit unsigned integer value in GCOV format to memory at address
+ *  * BUF. */
+static void store_int64(uint64_t i, char *buf)
+{
+    store_int32((uint32_t) (i & 0xffff), buf);
+    store_int32((uint32_t) (i >> 32), buf + 4);
+}
+
+/* Store a gcov counter in GCOV format to memory at address BUF. The counter is
+ *  * identified by BB, FUNC, TYPE and COUNTER. */
+static void
+store_counter(struct xen_gcov_info *bb, unsigned int func, unsigned int type,
+          unsigned int counter, char *buf)
+{
+    unsigned int counter_off;
+    unsigned int type_off;
+    unsigned int i;
+    struct xen_gcov_fn_info *func_ptr;
+    /* Get offset into counts array */
+    type_off = 0;
+    for (i = 0; i < type; i++)
+        if (counter_active(bb, i))
+        type_off++;
+    /* Get offset into values array. */
+    counter_off = counter;
+    for (i = 0; i < func; i++) 
+    {
+        func_ptr = (struct xen_gcov_fn_info *)&bb->functions[i];
+        counter_off += func_ptr->n_ctrs[type];
+    }
+    /* Create in temporary storage */
+    store_int64(bb->counts[type_off].values[counter_off], buf);
+}
+
+/* Store a counter section in userspace memory. The counter section is
+ *  * identified by BB, FUNC and TYPE. The destination address is BUF. Store at
+ *   * most COUNT bytes beginning at OFFSET. Return the number of bytes stored or a
+ *    * negative value on error. */
+static ssize_t
+store_counter_data(struct xen_gcov_info *bb, unsigned int func,
+           unsigned int type, char *buf, size_t count, loff_t offset)
+{
+    struct xen_gcov_fn_info *func_ptr;
+    char data[8]; 
+    char *from;
+    size_t len;
+    ssize_t result;
+    unsigned int i;
+
+    func_ptr = (struct xen_gcov_fn_info *)&bb->functions[func];
+    result = 0;
+    while (count > 0) 
+    {
+        if (offset < 4) 
+        {
+            /* Tag ID */
+            store_int32((uint32_t) GCOV_TAG_FOR_COUNTER(type),
+               data);
+            len = 4 - offset;
+            from = data + offset;
+        } 
+        else if (offset < 8) 
+        {
+            /* Tag length in groups of 4 bytes */
+            store_int32((uint32_t)
+            func_ptr->n_ctrs[type] * 2, data);
+            len = 4 - (offset - 4);
+            from = data + (offset - 4);
+        } 
+        else 
+        {
+            /* Actual counter data */
+            i = (offset - 8) / 8;
+            /* Check for EOF */
+            if (i >= func_ptr->n_ctrs[type])
+                break;
+            store_counter(bb, func, type, i, data);
+            len = 8 - (offset - 8) % 8;
+            from = data + (offset - 8) % 8;
+        }
+        if (len > count)
+        len = count;
+        if (copy_to_user(buf, from, len))
+        return -EFAULT;
+        count -= len;
+        buf += len;
+        offset += len;
+        result += len;
+    }
+
+    return result;
+}
+
+/* Store a function section and associated counter sections in userspace memory.
+ *  * The function section is identified by BB and FUNC. The destination address is
+ *   * BUF. Store at most COUNT bytes beginning at OFFSET. Return the number of
+ *    * bytes stored or a negative value on error. */
+static ssize_t
+store_func_data(struct xen_gcov_info *bb, unsigned int func, char *buf,
+        size_t count, loff_t offset)
+{
+    char data[4];
+    char *from;
+    size_t len;
+    unsigned int i;
+    loff_t off;
+    size_t size;
+    ssize_t result;
+    ssize_t rc;
+    result = 0;
+    while (count > 0) 
+    {
+        if (offset < 16) 
+        {
+            if (offset < 4) 
+            {
+                /* Tag ID */
+                store_int32((uint32_t) GCOV_TAG_FUNCTION, data);
+                len = 4 - offset;
+                from = data + offset;
+            } 
+            else if (offset < 8) 
+            {
+                /* Tag length */
+                store_int32(2, data);
+                len = 4 - (offset - 4);
+                from = data + (offset - 4);
+            } 
+            else if (offset < 12) 
+            {
+                /* Function ident */
+                store_int32((uint32_t) bb->
+                functions[func].ident, data);
+                len = 4 - (offset - 8);
+                from = data + (offset - 8);
+            } 
+            else 
+            {
+                /* Function checksum */
+                store_int32((uint32_t) bb->
+                functions[func].checksum, data);
+                len = 4 - (offset - 12);
+                from = data + (offset - 12);
+            }
+            /* Do the actual store */
+            if (len > count)
+                len = count;
+            if (copy_to_user(buf, from, len))
+                return -EFAULT;
+        }  
+        else 
+        {
+            off = 16;
+            len = 0;
+            for (i = 0; i < GCOV_COUNTERS; i++) 
+            {
+                size = sizeof_counter_data(bb, func, i);
+                if (offset < off + size) 
+                {
+                    rc = store_counter_data(bb, func, i,
+                       buf, count,
+                       offset - off);
+                       if (rc < 0)
+                           return rc;
+                       len = rc;
+                       break;
+                }
+                off += size;
+            }
+            /* Check for EOF */
+            if (i == GCOV_COUNTERS)
+                break;
+         } 
+    count -= len;
+    buf += len;
+    offset += len;
+    result += len;
+    }
+
+    return result;
+}
+
+/* Store data of .gcda file associated with NODE to userspace memory at BUF.
+ *  * OFFSET specifies the offset inside the .da file. COUNT is the maximum
+ *   * number of bytes to store. Return the number of bytes stored, zero for
+ *    * EOF or a negative number in case of error. */
+static ssize_t
+store_da_file(struct gcov_ftree_node *node, char *buf, size_t count,
+          loff_t offset)
+{
+
+    struct xen_gcov_info *bb;
+    char data[4];
+    char *from;
+    size_t len;
+    unsigned int i;
+    loff_t off;
+    size_t size;
+    ssize_t result;
+    ssize_t rc;
+
+    bb = node->bb;
+    result = 0;
+    while (count > 0) 
+        {
+            if (offset < 12) 
+            {
+                if (offset < 4) 
+                {
+                    /* File magic */
+                    store_int32((uint32_t) GCOV_DATA_MAGIC, data);
+                    len = 4 - offset;
+                    from = data + offset;
+                } 
+                 else if (offset < 8) 
+                {
+                    /* File format/GCC version */
+                    store_int32(xen_gcov_version, data);
+                    len = 4 - (offset - 4);
+                    from = data + (offset - 4);
+                } 
+                else 
+                {
+                    /* Time stamp */
+                    store_int32((uint32_t) bb->stamp, data);
+                    len = 4 - (offset - 8);
+                    from = data + (offset - 8);
+                }
+            /* Do the actual store */
+            if (len > count)
+                len = count;
+            if (copy_to_user(buf, from, len))
+                return -EFAULT;
+            } 
+            else 
+            {
+                off = 12;
+                len = 0;
+                for (i = 0; i < bb->n_functions; i++) 
+                {
+
+                    size = sizeof_func_data(bb, i);
+                    if (offset < off + size) 
+                    {
+                        rc = store_func_data(bb, i, buf, count,
+                            offset - off);
+
+                        if (rc < 0)
+                            return rc;
+                        len = rc;
+                         break;
+                    }
+                    off += size;
+
+                }
+                /* Check for EOF */
+                if (i == bb->n_functions)
+                    break;
+            }
+            count -= len;
+            buf += len;
+            offset += len;
+            result += len;
+       }
+
+    return result;
+}
+
+/* Return size of header which precedes .da file entry associated with BB
+ * in the vmlinux file. */
+static size_t sizeof_vmlinux_header(struct xen_gcov_info *bb)
+{
+    return 8 + PAD8(strlen(bb->file) + 1);
+}
+
+/* Store data of header which precedes .da file entry associated with NODE
+ * in the vmlinux file to userspace memory at BUF. OFFSET specifies the offset
+ * inside the header file. COUNT is the maximum number of bytes to store.
+ * Return the number of bytes stored, zero for EOF or a negative number in
+ * case of error. */
+static ssize_t
+store_vmlinux_header(struct gcov_ftree_node *node, char *buf, size_t count,
+             loff_t offset)
+{
+    char data[8];
+    char *from;
+    size_t namelen;
+    ssize_t stored;
+    size_t len;
+
+    namelen = strlen(node->bb->file);
+    stored = 0;
+    while (count > 0) 
+    {
+        if (offset < 8) 
+        {
+            /* Filename length */
+            if (store_gcov_type(PAD8(namelen + 1), data, 8))
+                return -EINVAL;
+            from = data + offset;
+            len = 8 - offset;
+        } 
+        else if (offset < 8 + namelen) 
+        {
+            /* Filename */
+            from = (char *)node->bb->file + offset - 8;
+            len = namelen - (offset - 8);
+        } 
+        else if (offset < node->header_size) 
+        {
+            /* Nil byte padding */
+            memset(data, 0, 8);
+            from = data;
+            len = PAD8(namelen + 1) - (offset - 8);
+        }   
+        else
+            break;
+        if (len > count)
+            len = count;
+        if (copy_to_user(buf, from, len))
+            return -EFAULT;
+
+        stored += len;
+        count -= len;
+        offset += len;
+        buf += len;
+    }
+
+    return stored;
+}
+
+/* Update data related to vmlinux file. */
+static void update_vmlinux_data(void)
+{
+    struct gcov_ftree_node *node;
+    loff_t offset;
+
+    offset = 0;
+    for (node = leaf_nodes; node; node = node->next) 
+    {
+        node->offset = offset;
+        node->da_size = sizeof_da_file(node->bb);
+        node->header_size = sizeof_vmlinux_header(node->bb);
+        offset += node->header_size + node->da_size;
+    }
+    proc_vmlinux->size = offset;
+    cached_node = NULL;
+}
+
+/* Read .da or vmlinux file. */
+static ssize_t
+read_gcov(struct file *file, char *buf, size_t count, loff_t * pos)
+{
+    struct gcov_ftree_node *node;
+    struct proc_dir_entry *dir_entry;
+    ssize_t rc;
+
+    mutex_lock(&gcov_mutex);
+    dir_entry = PDE(file->f_dentry->d_inode);
+    rc = 0;
+    if (dir_entry == proc_vmlinux) 
+    {
+        /* Are we in a sequential read? */
+        if (cached_node && (*pos >= cached_node->offset))
+            node = cached_node;
+        else
+            node = leaf_nodes;
+            /* Find node corresponding to offset */
+        while (node && node->next && (*pos >= node->next->offset))
+            node = node->next;
+            cached_node = node;
+            if (node) 
+            {
+                if (*pos - node->offset < node->header_size)
+                    rc = store_vmlinux_header(node, buf, count,
+                    *pos - node->offset);
+                else
+                {
+                    rc = store_da_file(node, buf, count,
+                    *pos - node->offset -
+                    node->header_size);
+                }
+            }
+    } 
+    else 
+    {
+        node = (struct gcov_ftree_node *)dir_entry->data;
+        if (node) 
+        {
+            rc = store_da_file(node, buf, count, *pos);
+        }
+    }
+    if (rc > 0)
+        *pos += rc;
+    mutex_unlock(&gcov_mutex);
+    return rc;
+}
+
+/* Reset counters on write request. */
+static ssize_t
+write_gcov(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+    struct gcov_ftree_node *node;
+    struct proc_dir_entry *dir_entry;
+
+    mutex_lock(&write_mutex);
+    dir_entry = PDE(file->f_dentry->d_inode);
+    if (dir_entry == proc_vmlinux) 
+    {
+        /* Reset all nodes */
+        node = leaf_nodes;
+        if (HYPERVISOR_gcovprof_op(GCOVPROF_reset, NULL, !RESET_GCDA)) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                   "Failed to reset the vmlinux\n");
+            mutex_unlock(&write_mutex);
+            return EIO;
+        }
+
+    } 
+    else 
+    {
+        node = (struct gcov_ftree_node *)dir_entry->data;
+        if (HYPERVISOR_gcovprof_op
+            (GCOVPROF_reset, node->bb->file, RESET_GCDA)) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                   "Failed to reset file %s\n", node->bb->file);
+            mutex_unlock(&write_mutex);
+            return EIO;
+        }
+    }
+    mutex_unlock(&write_mutex);
+    return count;
+}
+
+/* Return a newly allocated copy of STRING. */
+static char *strdup(const char *string)
+{
+    char *result;
+
+    result = (char *)kmalloc(strlen(string) + 1, GFP_KERNEL);
+    if (result)
+        strcpy(result, string);
+    return result;
+}
+
+/* Allocate a new node and fill in NAME and BB. */
+static struct gcov_ftree_node *alloc_node(const char *name,
+                      struct xen_gcov_info *bb)
+{
+    struct gcov_ftree_node *node;
+
+    node = (struct gcov_ftree_node *)
+        kmalloc(sizeof(struct gcov_ftree_node), GFP_KERNEL);
+    if (!node)
+        return NULL;
+    memset(node, 0, sizeof(struct gcov_ftree_node));
+    node->fname = strdup(name);
+    if (!node->fname) 
+    {
+        kfree(node);
+        return NULL;
+    }
+    node->bb = bb;
+    return node;
+}
+
+/* Free memory allocated for NODE. */
+static void free_node(struct gcov_ftree_node *node)
+{
+    if (node == &tree_root)
+        return;
+    if (node->fname)
+    kfree(node->fname);
+    kfree(node);
+}
+
+/* Remove proc filesystem entries associated with NODE. */
+static void delete_from_proc(struct gcov_ftree_node *node)
+{
+    struct proc_dir_entry *parent;
+    int i;
+
+    if (node->parent)
+        parent = node->parent->proc[0];
+    else
+        parent = xen_base;
+    for (i = 0; i < sizeof(node->proc) / sizeof(node->proc[0]); i++)
+        if (node->proc[i])
+            remove_proc_entry(node->proc[i]->name, parent);
+}
+
+/* Release all resources associated with NODE. If NODE is a directory node,
+ * also clean up all children. */
+static void cleanup_node(struct gcov_ftree_node *node)
+{
+    struct gcov_ftree_node *curr;
+    struct gcov_ftree_node *next;
+    struct gcov_ftree_node *prev;
+
+    next = node;
+    do 
+    {
+        /* Depth first traversal of all children */
+        curr = next;
+        while (curr->files)
+            curr = curr->files;
+        if (curr->sibling)
+            next = curr->sibling;
+        else
+            next = curr->parent;
+        /* Remove from tree */
+        if (curr->parent) 
+        {
+            if (curr->parent->files == curr)
+                curr->parent->files = curr->sibling;
+            else 
+            {
+                for (prev = curr->parent->files;
+                    prev->sibling != curr;
+                    prev = prev->sibling) ;
+                    prev->sibling = curr->sibling;
+            }
+        }
+        /* Remove from leaf node list if necessary */
+        if (curr->bb) 
+        {
+            if (leaf_nodes == curr)
+                leaf_nodes = curr->next;
+            else 
+            {
+                for (prev = leaf_nodes;
+                     prev && (prev->next != curr);
+                     prev = prev->next) ;
+                if (prev)
+                    prev->next = curr->next;
+            }
+        }
+        /* Delete node */
+        delete_from_proc(curr);
+        free_node(curr);
+    } while (node != curr);
+}
+
+/* Clean up NODE and containing path in case it would be left empty. */
+static void cleanup_node_and_path(struct gcov_ftree_node *node)
+{
+    while (node->parent &&
+        node->parent != &tree_root && !node->parent->files->sibling)
+    node = node->parent;
+    cleanup_node(node);
+}
+
+/* Create a new directory node named NAME under PARENT. Upon success return
+ * zero and update RESULT to point to the newly created node. Return non-zero
+ * otherwise. */
+static int
+create_dir_node(struct gcov_ftree_node *parent, char *name,
+        struct gcov_ftree_node **result)
+{
+    struct gcov_ftree_node *node;
+
+    /* Initialize new node */
+    node = alloc_node(name, NULL);
+    if (!node)
+        return -ENOMEM;
+    /* Create proc filesystem entry */
+    node->proc[0] = proc_mkdir(name, parent->proc[0]);
+    if (!node->proc[0]) 
+    {
+        free_node(node);
+        return -EIO;
+    }
+    /* Insert node into tree */
+    node->parent = parent;
+    node->sibling = parent->files;
+    parent->files = node;
+    *result = node;
+    return 0;
+}
+
+static struct file_operations proc_gcov_operations = {
+    .owner        = THIS_MODULE,
+    .read         = read_gcov,
+    .write        = write_gcov,
+};
+
+/* Create a new file node named NAME under PARENT. Associate node with BB.
+ * Return zero upon success, non-zero otherwise. */
+static int
+create_file_node(struct gcov_ftree_node *parent, char *name,
+         struct xen_gcov_info *bb)
+{
+    struct gcov_ftree_node *node;
+    char *link_target;
+    char *link_name;
+    int i;
+    /* Initialize new node */
+    node = alloc_node(name, bb);
+    if (!node)
+        return -ENOMEM;
+    /* Create proc filesystem entry */
+    node->proc[0] = create_proc_entry(name, S_IWUSR | S_IRUGO,
+                      parent->proc[0]);
+    if (!node->proc[0]) 
+    {
+        free_node(node);
+        return -EIO;
+    }
+    node->proc[0]->data = node;
+    node->proc[0]->proc_fops = &proc_gcov_operations;
+    node->proc[0]->size = sizeof_da_file(bb);
+    /* Create symbolic links */
+    if (xen_gcov_link) 
+    {
+        /* Note: temp string length is calculated as upper limit */
+        link_target = (char *)kmalloc(strlen(bb->file) +
+                          strlen(da_ending) +
+                          strlen(gcov_var->src_path),
+                          GFP_KERNEL);
+        if (!link_target) 
+        {
+            delete_from_proc(node);
+            free_node(node);
+            return -ENOMEM;
+        }
+        for (i = 0; i < sizeof(endings) / sizeof(endings[0]); i++) 
+        {
+
+            strcpy(link_target, bb->file);
+            link_target[strlen(link_target) -
+                    strlen(da_ending)] = 0;
+            strcat(link_target, endings[i]);
+            link_name = strrchr(link_target, '/') + 1;
+            node->proc[i + 1] = proc_symlink(link_name,
+                             parent->proc[0],
+                             link_target);
+            if (!node->proc[i + 1]) 
+            {
+                kfree(link_target);
+                delete_from_proc(node);
+                free_node(node);
+                return -EIO;
+             }
+        }
+        kfree(link_target);
+    }
+    /* Insert node into tree */
+    node->parent = parent;
+    node->sibling = parent->files;
+    parent->files = node;
+    node->next = leaf_nodes;
+    leaf_nodes = node;
+    return 0;
+}
+
+/* Return proc filesystem entry name for FILENAME. */
+static inline char *get_proc_filename(const char *filename)
+{
+    char *result;
+
+    result = strdup(filename + sourcepath_len + 1);
+    return result;
+}
+
+/* Create tree node and proc filesystem entry for BB. Create subdirectories as
+ * necessary. Return zero upon success, non-zero otherwise. */
+static int create_node(struct xen_gcov_info *bb)
+{
+    struct gcov_ftree_node *parent;
+    struct gcov_ftree_node *node;
+    char *filename;
+    char *curr;
+    char *next;
+    int rc;
+
+    filename = get_proc_filename(bb->file);
+    if (!filename)
+        return -ENOMEM;
+    /* Recreate directory path in proc filesystem */
+    parent = &tree_root;
+    for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) 
+    {
+        /* Skip empty path components */
+        if (curr == next)
+            continue;
+        *next = 0;
+        /* Check whether directory already exists */
+        for (node = parent->files;
+             node && (strcmp(node->fname, curr) != 0);
+             node = node->sibling) ;
+        if (!node) 
+        {
+            /* Create directory node */
+            rc = create_dir_node(parent, curr, &node);
+            if (rc) 
+            {
+                if (parent != &tree_root)
+                    cleanup_node_and_path(parent);
+                kfree(filename);
+                return rc;
+            }
+        }
+        parent = node;
+    }
+    rc = create_file_node(parent, curr, bb);
+    kfree(filename);
+    return rc;
+}
+
+static void xeno_prof(void *data)
+{
+   /*update linked list with profiling info and line counts */
+    WARN_ON(HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0));
+    
+   /*again schedule work */
+   schedule_delayed_work(&work, xen_gcov_update * HZ);
+
+}
+
+/* Invokes hypercall to get all the necessary gcov variables from core */
+static inline int gcov_core_vars(void)
+{
+
+    int ret;
+    gcov_var = kmalloc(sizeof(struct gcov_variables), GFP_KERNEL);
+    if ((ret =
+        HYPERVISOR_gcovprof_op(GCOVPROF_seek_vars, gcov_var, 0)) != 0)
+    return ret;
+    
+    xen_gcov_version = gcov_var->g_version;
+
+    gcov_var->ctr_num =
+        kmalloc(gcov_var->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+    gcov_var->n_funcs =
+        kmalloc(gcov_var->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+    if ((ret =
+        HYPERVISOR_gcovprof_op(GCOVPROF_seek_vars, gcov_var, 1)) != 0)
+    return ret;
+
+   sourcepath_len = strlen(gcov_var->src_path);
+   return ret;
+}
+
+static int __init xen_gcov_init(void)
+{
+    struct xen_gcov_info *bb;
+    int ret, rc = 0;
+    /* seek gcov core variable information & construct the gcov_info list */
+    if (gcov_core_vars()) 
+    {
+        printk(KERN_ERR GCOV_PROC_HEADER
+           "Failed to get the varaibales from core\n");
+        return -EIO;
+    }
+
+    /*add sys entry in /proc/sys/xen for count update tuning*/    
+#ifdef CONFIG_SYSCTL
+    gcov_table_header = register_sysctl_table(gcov_root_table, 0);
+    if (!gcov_table_header)
+        printk(KERN_INFO GCOV_PROC_HEADER
+              "no /proc/sys/xen added\n");
+#endif
+    printk(KERN_INFO GCOV_PROC_HEADER "xen_gcov_link=%d xen_gcov_update=%d\n", 
+                        xen_gcov_link, xen_gcov_update);
+    
+    /* Create xen_gcov_info linked list */
+    create_bb_list();
+
+    /*populate the linked list with profiling info and line counts */
+    ret = HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0);
+    if (ret) 
+    {
+        printk(KERN_ERR GCOV_PROC_HEADER
+                "Failed to retrive hypervisor profiling info\n");
+        return ret;
+    }
+    /* Initializing root node and /proc/gcov entry */
+    tree_root.fname = GCOV_PROC_ROOT;
+    tree_root.proc[0] = proc_mkdir(tree_root.fname, xen_base);
+    if (!tree_root.proc[0]) 
+    {
+        printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+           "create root proc filesystem entry\n");
+        return -EIO;
+    }
+    /* Create /proc/gcov/vmlinux entry */
+    proc_vmlinux = create_proc_entry(GCOV_PROC_VMLINUX, S_IWUSR | S_IRUGO,
+                 tree_root.proc[0]);
+    if (!proc_vmlinux) 
+    {
+        printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+            "create proc filesystem entry %s\n", GCOV_PROC_VMLINUX);
+        cleanup_node(&tree_root);
+        return -EIO;
+    }
+    proc_vmlinux->proc_fops = &proc_gcov_operations;
+
+    /*populate /proc/xen/gcov entry with data files*/
+    for (bb = list_head; bb != NULL; bb = bb->next) 
+    {
+        rc = create_node(bb);
+        if (rc) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                "init failed: could not create node for %s (err=%d)\n",
+            bb->file, rc);
+            remove_proc_entry(proc_vmlinux->name,
+                   tree_root.proc[0]);
+            cleanup_node(&tree_root);
+            return rc;
+        }
+    } /*done*/
+   update_vmlinux_data();
+
+   /*schedule for profiling update after <xen_gcov_update> secs*/
+   schedule_delayed_work(&work, xen_gcov_update * HZ);
+   printk(KERN_INFO GCOV_PROC_HEADER "init done\n");
+   kfree(gcov_var);
+   return 0;
+}
+
+/* Clean up module data. */
+static void __exit xen_gcov_cleanup(void)
+{
+    cancel_delayed_work(&work);
+    flush_scheduled_work();
+#ifdef CONFIG_SYSCTL
+    unregister_sysctl_table(gcov_table_header);
+#endif
+    remove_proc_entry(proc_vmlinux->name, tree_root.proc[0]);
+    cleanup_node(&tree_root);
+    free_bb_list();
+    printk(KERN_INFO GCOV_PROC_HEADER "Module is now unloaded\n");
+}
+
+module_init(xen_gcov_init);
+module_exit(xen_gcov_cleanup);
diff -r 51decc39e5e7 include/asm-i386/mach-xen/asm/hypercall.h
--- a/include/asm-i386/mach-xen/asm/hypercall.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/asm-i386/mach-xen/asm/hypercall.h	Mon Mar 02 17:57:59 2009 +0530
@@ -404,6 +404,13 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 
 
 #endif /* __HYPERCALL_H__ */
diff -r 51decc39e5e7 include/asm-x86_64/mach-xen/asm/hypercall.h
--- a/include/asm-x86_64/mach-xen/asm/hypercall.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/asm-x86_64/mach-xen/asm/hypercall.h	Mon Mar 02 17:57:59 2009 +0530
@@ -405,4 +405,11 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 #endif /* __HYPERCALL_H__ */
diff -r 51decc39e5e7 include/xen/interface/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/xen/interface/xen-gcov.h	Mon Mar 02 17:57:59 2009 +0530
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *  xen-gcov.h
+ *   
+ *  Interface for populating hypervisor profiling info under /proc/xen
+ *  
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to
+ *  deal in the Software without restriction, including without limitation the
+ *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ *  sell copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *   
+ *  The above copyright notice and this permission notice shall be included in
+ *  all copies or substantial portions of the Software.
+ *   
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ *  DEALINGS IN THE SOFTWARE.
+ *   
+ *  Author: tej parkash <tej.parkash@hcl.in>
+ *  Written by tej parkash and team
+ *  
+ */
+/**********************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_seek_vars            0
+#define GCOVPROF_start		      1
+#define GCOVPROF_reset		      2
+#define GCOVPROF_last_op              3
+
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS                 5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+/*xen-gcov-proc specific macros*/
+#define RESET_GCDA      0
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+
+
+struct gcov_ftree_node
+{
+    char *fname;             		/* Hierarchy-relative name */
+    struct gcov_ftree_node *sibling; 	/* First sibling of this node */
+    struct gcov_ftree_node *files;   	/* First child of this node */
+    struct gcov_ftree_node *parent;  	/* Parent of this node */
+    struct proc_dir_entry *proc[4];  	/* Entries for .da, .bb, .bbg, .c */
+    struct xen_gcov_info *bb;           	/* Associated struct bb */
+    loff_t offset;           		/* Offset in vmlinux file */
+    size_t da_size;          		/* Size of associated .da file */
+    size_t header_size;      		/* Size of associated file header */
+    struct gcov_ftree_node *next;    	/* Next leaf node */
+};
+
+
+struct xen_gcov_fn_info
+{
+    gcov_unsigned_t ident;    		/* unique ident of function */
+    gcov_unsigned_t checksum; 		/* function checksum */
+    unsigned int n_ctrs[GCOV_COUNTERS]; /* instrumented counters */
+};
+typedef struct xen_gcov_fn_info xen_gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_fn_info_t);
+
+struct xen_gcov_ctr_info
+{
+    gcov_unsigned_t num;      		/* number of counters.  */
+    gcov_type *values;			/* thier values. */
+    gcov_merge_fn merge;      		/* merge function  */
+};
+typedef struct  xen_gcov_ctr_info xen_gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct xen_gcov_info
+{
+    gcov_unsigned_t version;  		/* expected version number */
+    struct xen_gcov_info *next;   	/* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    		/* uniquifying time stamp */
+    char file[100];
+
+    unsigned int n_functions;     	/* number of functions */
+    struct xen_gcov_fn_info *functions;	/* table of functions */
+
+    unsigned int ctr_mask;        	/* mask of counters instrumented.  */
+    struct xen_gcov_ctr_info counts[GCOV_COUNTERS]; /* count data */
+};
+typedef struct xen_gcov_info xen_gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_info_t);
+
+struct gcov_variables
+{
+    char src_path[100];			/* source path */
+    unsigned long g_version;		/* gcov version */
+    unsigned int n_files;		/* num of file in hypervisor code */
+    gcov_unsigned_t *ctr_num;		/* count variables */
+    unsigned int *n_funcs;     		/* num of funcs per bb struct */
+	
+};
+typedef struct gcov_variables gcov_variables_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_variables_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r 51decc39e5e7 include/xen/interface/xen.h
--- a/include/xen/interface/xen.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/xen/interface/xen.h	Mon Mar 02 17:58:00 2009 +0530
@@ -91,6 +91,7 @@
 #define __HYPERVISOR_sysctl               35
 #define __HYPERVISOR_domctl               36
 #define __HYPERVISOR_kexec_op             37
+#define __HYPERVISOR_gcovprof_op          38
 
 /* Architecture-specific hypercall definitions. */
 #define __HYPERVISOR_arch_0               48
diff -r 51decc39e5e7 include/xen/xen_proc.h
--- a/include/xen/xen_proc.h	Wed Jan 28 13:42:09 2009 +0000
+++ b/include/xen/xen_proc.h	Mon Mar 02 17:58:00 2009 +0530
@@ -3,6 +3,8 @@
 #define __ASM_XEN_PROC_H__
 
 #include <linux/proc_fs.h>
+
+extern struct proc_dir_entry *xen_base;
 
 extern struct proc_dir_entry *create_xen_proc_entry(
 	const char *name, mode_t mode);

[-- Attachment #3: xen-3.3-gcov-v3.patch --]
[-- Type: application/octet-stream, Size: 17907 bytes --]

diff -r a3675bed9c21 xen/Rules.mk
--- a/xen/Rules.mk	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/Rules.mk	Tue Mar 03 02:49:49 2009 +0530
@@ -9,6 +9,10 @@
 crash_debug   ?= n
 frame_pointer ?= n
 
+# xen source path to get update in .gcno files
+xen_src := $(CURDIR)
+xentree := $(if $(xen_src),$(xen_src),$(CURDIR))
+
 XEN_ROOT=$(BASEDIR)/..
 include $(XEN_ROOT)/Config.mk
 
@@ -56,6 +60,7 @@
 ALL_OBJS-y               += $(BASEDIR)/arch/$(TARGET_ARCH)/built_in.o
 
 CFLAGS-y                += -g -D__XEN__
+CFLAGS-y                += -fprofile-arcs -ftest-coverage
 CFLAGS-$(XSM_ENABLE)    += -DXSM_ENABLE
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_ENABLE -DXSM_MAGIC=0xf97cff8c
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_DEVELOP -DFLASK_BOOTPARAM -DFLASK_AVC_STATS
@@ -112,12 +117,14 @@
 
 .PHONY: clean
 clean:: $(addprefix _clean_, $(subdir-all))
-	rm -f *.o *~ core
+	rm -f *.o *.gcno *~ core
 _clean_%/: FORCE
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C $* clean
 
 %.o: %.c $(HDRS) Makefile
-	$(CC) $(CFLAGS) -c $< -o $@
+	$(CC) $(CFLAGS) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+
 
 %.o: %.S $(AHDRS) Makefile
 	$(CC) $(AFLAGS) -c $< -o $@
diff -r a3675bed9c21 xen/arch/x86/mm/hap/Makefile
--- a/xen/arch/x86/mm/hap/Makefile	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/arch/x86/mm/hap/Makefile	Tue Mar 03 02:49:49 2009 +0530
@@ -4,8 +4,18 @@
 obj-y += guest_walk_4level.o
 obj-y += p2m-ept.o
 
+LN = /bin/ln
+
 guest_levels  = $(subst level,,$(filter %level,$(subst ., ,$(subst _, ,$(1)))))
 guest_walk_defns = -DGUEST_PAGING_LEVELS=$(call guest_levels,$(1))
 
+num = $(call guest_levels,$(@F))level.c
+
 guest_walk_%level.o: guest_walk.c $(HDRS) Makefile
-	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c $< -o $@
+	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s guest_walk.c guest_walk_$(num)
+	
+.PHONY: clean
+clean::
+	rm -f guest_walk_*.c
diff -r a3675bed9c21 xen/arch/x86/mm/shadow/Makefile
--- a/xen/arch/x86/mm/shadow/Makefile	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/arch/x86/mm/shadow/Makefile	Tue Mar 03 02:49:49 2009 +0530
@@ -1,5 +1,14 @@
 obj-$(x86_32) += common.o guest_2.o guest_3.o
 obj-$(x86_64) += common.o guest_2.o guest_3.o guest_4.o
 
+LN = /bin/ln
+num=$*.c
+
 guest_%.o: multi.c $(HDRS) Makefile
-	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
+	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s multi.c guest_$(num)
+
+.PHONY: clean
+clean::
+	rm -f guest_*.c
diff -r a3675bed9c21 xen/arch/x86/x86_32/entry.S
--- a/xen/arch/x86/x86_32/entry.S	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/arch/x86/x86_32/entry.S	Tue Mar 03 02:49:49 2009 +0530
@@ -703,6 +703,7 @@
         .long do_sysctl             /* 35 */
         .long do_domctl
         .long do_kexec_op
+        .long do_gcovprof_op
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/4)
         .long do_ni_hypercall
         .endr
@@ -750,6 +751,7 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec_op          */
+        .byte 3 /* do_gcovprof_op	*/
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
         .endr
diff -r a3675bed9c21 xen/arch/x86/x86_64/entry.S
--- a/xen/arch/x86/x86_64/entry.S	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/arch/x86/x86_64/entry.S	Tue Mar 03 02:49:49 2009 +0530
@@ -692,6 +692,7 @@
         .quad do_sysctl             /* 35 */
         .quad do_domctl
         .quad do_kexec_op
+        .quad do_gcovprof_op
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
         .quad do_ni_hypercall
         .endr
@@ -739,6 +740,7 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec             */
+        .byte 3 /* do_gcovprof_op       */
         .byte 1 /* do_xsm_op            */
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
diff -r a3675bed9c21 xen/common/Makefile
--- a/xen/common/Makefile	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/common/Makefile	Tue Mar 03 02:49:49 2009 +0530
@@ -27,6 +27,7 @@
 obj-y += vsprintf.o
 obj-y += xmalloc.o
 obj-y += rcupdate.o
+obj-y += xen-gcov-core.o
 
 obj-$(perfc)       += perfc.o
 obj-$(crash_debug) += gdbstub.o
@@ -42,6 +43,8 @@
 
 subdir-y += libelf
 
+CFLAGS += -DSRC_PATH='"$(BASEDIR)"'
+
 # Object file contains changeset and compiler information.
 version.o: $(BASEDIR)/include/xen/compile.h
 
diff -r a3675bed9c21 xen/common/xen-gcov-core.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/common/xen-gcov-core.c	Tue Mar 03 02:49:49 2009 +0530
@@ -0,0 +1,255 @@
+/**
+ *  xen-gcov-core.c: arch dependent code for hypervisor profiling
+ *
+ *  Written by tej parkash and team(tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <xen/init.h>
+#include <xen/kernel.h>
+#include <xen/guest_access.h>
+#include <xen/sched.h>
+#include <xen/event.h>
+#include <xen/string.h>
+#include <public/xen-gcov.h>
+#include <xen/paging.h>
+#include <xsm/xsm.h>
+#include <xen/config.h>
+#include <xen/lib.h>
+
+
+#define XEN_GCOV_CORE    "xen-gcov-core:"
+
+static int num_xen_files;
+gcov_unsigned_t gcov_version = 0;
+struct gcov_info *bb_head;
+
+struct ctor_list 
+{
+    unsigned long num;
+    ctor_t ctor[];
+};
+extern struct ctor_list __CTOR_LIST__;
+
+static inline int
+counter_active(struct gcov_info *bb, unsigned int type)
+{
+    return (1 << type) & bb->ctr_mask;
+}
+
+static inline unsigned int
+get_fn_stride(struct gcov_info *bb)
+{
+    unsigned int stride;
+
+    stride = sizeof(struct gcov_fn_info) + sizeof(unsigned int);
+    if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int)) 
+    {
+        stride += __alignof__(struct gcov_fn_info) - 1;
+        stride &= ~(__alignof__(struct gcov_fn_info) - 1);
+    }
+    return stride;
+}
+
+static inline struct gcov_fn_info *
+get_fn_info(struct gcov_info *bb, unsigned int func)
+{
+    return (struct gcov_fn_info *)
+        ((char *)bb->functions + func * get_fn_stride(bb));
+}
+
+static void reset_gcda(struct gcov_info *bb)
+{
+
+    unsigned int i;
+    struct gcov_ctr_info *ctr;
+    ctr = bb->counts;
+    for (i=0; i < GCOV_COUNTERS; i++)
+        if (counter_active(bb, i)) 
+        {
+            memset(ctr->values, 0, ctr->num * sizeof(gcov_type));
+            ctr++;
+        }
+}
+
+/* Function to reset the bb counter. */
+static int reset_bb(XEN_GUEST_HANDLE(void) arg, int val)
+{
+    struct gcov_info *tmp=bb_head;
+    char bb_file[100];
+    if ( val == 0)
+    {
+        /* reset single data file */
+        if ( copy_from_guest(&bb_file, arg, 1) )
+            return -EFAULT;
+        for(;tmp!=NULL;tmp=tmp->next)
+        {
+            if(!strcmp(tmp->filename,bb_file))
+            {
+                reset_gcda(tmp);
+                break;
+            }
+            else
+                continue;
+        }
+    }
+    else 
+    {
+        /* reset all data files */
+        for(;tmp!=NULL;tmp=tmp->next)
+            reset_gcda(tmp);
+    }
+    return 0;
+}
+
+/*copy bb_head info */
+static int start_prof(XEN_GUEST_HANDLE(void) arg, int val)
+{
+    /*Add the code to copy the no of files to be add to xen */
+    struct xen_gcov_info bb_tmp;
+    struct gcov_info *tmp = bb_head;
+    struct gcov_fn_info *func;
+    struct gcov_ctr_info *ctr;
+    struct xen_gcov_ctr_info  *xctr;
+    struct xen_gcov_info *bb = &bb_tmp;
+    int i,j;
+
+    if ( copy_from_guest(&bb_tmp, arg, 1) )
+        return -EFAULT;
+    for(;tmp!=NULL&&bb!=NULL;tmp=tmp->next)
+    {
+        ctr = tmp->counts;
+        xctr = bb->counts;	
+        memcpy(bb->counts[0].values, ctr->values, ctr->num*sizeof(gcov_type));
+        xctr->num = ctr->num;
+        xctr->merge = ctr->merge;
+        bb->version  = tmp->version;
+        bb->stamp    = tmp->stamp;
+        safe_strcpy(bb->file, tmp->filename);
+        bb->ctr_mask    = tmp->ctr_mask;
+        bb->n_functions = tmp->n_functions;
+        for(i=0;i<tmp->n_functions;i++) 
+        {
+            func = get_fn_info(tmp,i);
+            bb->functions[i].ident=func->ident;
+            bb->functions[i].checksum=func->checksum;
+            for (j=0;j<GCOV_COUNTERS;j++) 
+            {
+                bb->functions[i].n_ctrs[j]=func->n_ctrs[j];
+            }
+        }
+        /*increment the bb_head pointers*/
+        bb=bb->next;
+    }
+    if ( copy_to_guest(arg, &bb_tmp, 1) )
+        return -EFAULT;
+    return 0;
+}
+
+static int var_info(XEN_GUEST_HANDLE(void) arg,int val)
+{
+    struct gcov_variables gcov_var;
+    struct gcov_variables *tmp=&gcov_var;
+    struct gcov_info *bb;
+    int i;
+    if( copy_from_guest(&gcov_var, arg, 1) )
+        return -EFAULT;
+    if ( val == 0 ) 
+    {
+        safe_strcpy(tmp->src_path,SRC_PATH);	
+        tmp->g_version=gcov_version;
+        tmp->n_files=num_xen_files;
+    } 
+    else 
+    {
+        for(i=0,bb=bb_head;bb!=NULL;bb=bb->next,i++)
+        {
+            tmp->ctr_num[i]=bb->counts->num;
+            tmp->n_funcs[i]=bb->n_functions;
+       }
+    }
+
+    if( copy_to_guest(arg, &gcov_var, 1) )
+        return -EFAULT;
+    return 0;
+}
+
+/*internal hyercall functions*/
+int do_gcovprof_op(int op, XEN_GUEST_HANDLE(void) arg, int val)
+{
+    int ret = 0;
+    if ((op < 0) || (op > GCOVPROF_last_op)) 
+    {
+        printk(KERN_ERR XEN_GCOV_CORE "Invalid operation %d\n", op);
+        return -EINVAL;
+    }
+    switch (op) 
+    {
+        case GCOVPROF_seek_vars:
+            /*fun seeks vars to be used by proc code */
+            ret = var_info(arg, val);
+            break;
+        case GCOVPROF_start:
+            /*update linux kernel with profiling info */
+            ret = start_prof(arg,val);
+            break;
+        case GCOVPROF_reset:
+            /*reset the gcda or vmlinux*/
+            ret = reset_bb(arg,val);
+            break;
+        default:
+            ret = -ENOSYS;
+    }
+    return ret;
+}
+
+void do_global_ctors(ctor_t ctor[], unsigned long num)
+{
+    unsigned long i;
+
+    /* Call constructors and create gcov_info linked list*/
+    for (i = 0; i < num && ctor[i]; i++)
+        ctor[i] ();
+    num_xen_files = i-1;
+}
+
+/*Register supplied struct BB. Called by each object code constructor. */
+void __gcov_init(struct gcov_info *bb)
+{
+    if (!bb->version)
+        return;
+    /*Check for compatible gcc version */
+    if (gcov_version == 0)
+        gcov_version = bb->version;
+    else if (bb->version != gcov_version) 
+    {
+        printk(KERN_WARNING XEN_GCOV_CORE "gcc version mismatch in "
+                  "file '%s'!\n", bb->filename);
+        return;
+    }
+    /*Set up linked list */
+    bb->version = 0;
+    bb->next = bb_head;
+    bb_head = bb;
+}
+
+static int __init gcov_init(void)
+{
+    do_global_ctors(__CTOR_LIST__.ctor, __CTOR_LIST__.num);
+    printk(KERN_INFO XEN_GCOV_CORE "init done\n");
+    return 0;
+}
+
+/* Unused functions needed to prevent linker errors. */
+void __gcov_flush(void) {}
+void __gcov_merge_add(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_single(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_delta(gcov_type * counters, unsigned int n_counters){}
+
+EXPORT_SYMBOL(__gcov_init);
+EXPORT_SYMBOL(do_global_ctors);
+EXPORT_SYMBOL(__gcov_flush);
+EXPORT_SYMBOL(__gcov_merge_add);
+EXPORT_SYMBOL(__gcov_merge_single);
+EXPORT_SYMBOL(__gcov_merge_delta);
+__initcall(gcov_init);
diff -r a3675bed9c21 xen/include/public/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/public/xen-gcov.h	Tue Mar 03 02:49:49 2009 +0530
@@ -0,0 +1,139 @@
+/******************************************************************************
+ * xen-gcov.h
+ * 
+ * Interface for enabling and populating hypervisor profiling info.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: tej parkash <tej.parkash@hcl.in>
+ * Written by tej parkash and team
+ *
+ */
+/********************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_seek_vars          0
+#define GCOVPROF_start              1
+#define GCOVPROF_reset              2
+#define GCOVPROF_last_op            3
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS               5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*ctor_t)(void);
+
+struct gcov_fn_info{
+    gcov_unsigned_t ident;    /* unique ident of function */
+    gcov_unsigned_t checksum; /* function checksum */
+    unsigned n_ctrs[0];       /* instrumented counters */
+};
+
+struct gcov_ctr_info
+{
+    gcov_unsigned_t num;      /* number of counters.  */
+    gcov_type *values;        /* their values.  */
+    gcov_merge_fn merge;      /* The function used to merge them.  */
+};
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+    gcov_unsigned_t version;  /* expected version number */
+    struct gcov_info *next;   /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    /* uniquifying time stamp */
+    const char *filename;
+
+    unsigned n_functions;     /* number of functions */
+    struct gcov_fn_info *functions; /* table of functions */
+
+    unsigned ctr_mask;        /* mask of counters instrumented.  */
+    struct gcov_ctr_info counts[0]; /* count data. The number of bits
+                     set in the ctr_mask field
+                     determines how big this array
+                     is.  */
+};
+
+
+struct xen_gcov_fn_info{
+    gcov_unsigned_t ident;              /* unique ident of function */
+    gcov_unsigned_t checksum;           /* function checksum */
+    unsigned int n_ctrs[GCOV_COUNTERS]; /* instrumented counters */
+};
+typedef struct xen_gcov_fn_info xen_gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_fn_info_t);
+
+
+
+struct xen_gcov_ctr_info
+{
+    gcov_unsigned_t num;      /* number of counters.  */
+    gcov_type *values;        /* their values.  */
+    gcov_merge_fn merge;      /* The function used to merge them.  */
+};
+typedef struct  xen_gcov_ctr_info xen_gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct xen_gcov_info
+{
+    gcov_unsigned_t version;            /* expected version number */
+    struct xen_gcov_info *next;         /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;              /* uniquifying time stamp */
+    char file[100];
+
+    unsigned int n_functions;           /* number of functions */
+    struct xen_gcov_fn_info *functions; /* table of functions */
+
+    unsigned int ctr_mask;              /* mask of counters instrumented.  */
+    struct xen_gcov_ctr_info counts[GCOV_COUNTERS]; /* count data.*/
+};
+typedef struct xen_gcov_info xen_gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_info_t);
+
+struct gcov_variables
+{
+    char src_path[100];
+    unsigned long g_version;
+    unsigned int n_files;
+    gcov_unsigned_t *ctr_num;
+    unsigned int *n_funcs;     
+};
+typedef struct gcov_variables gcov_variables_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_variables_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r a3675bed9c21 xen/include/xen/config.h
--- a/xen/include/xen/config.h	Tue Mar 03 02:35:13 2009 +0530
+++ b/xen/include/xen/config.h	Tue Mar 03 02:49:49 2009 +0530
@@ -96,4 +96,20 @@
 #define __cpuinitdata
 #define __cpuinit
 
+#define SORT(x)        x
+#define CONSTRUCTORS                           \
+       __CTOR_LIST__ = .;                  \
+       LONG((__CTOR_END__ - __CTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       __CTOR_LIST2__ = .;                 \
+       *(SORT(.ctors))                     \
+       LONG(0)                         \
+       __CTOR_END__ = .;                   \
+       __DTOR_LIST__ = .;                  \
+       LONG((__DTOR_END__ - __DTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       *(SORT(.dtors))                     \
+       LONG(0)                         \
+       __DTOR_END__ = .;
+
 #endif /* __XEN_CONFIG_H__ */

[-- Attachment #4: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
       [not found]     ` <f1c9d250903252359p13f2fe47r84674d183fe8629f@mail.gmail.com>
@ 2009-03-26 10:57       ` Gianluca Guida
  2009-03-26 15:11         ` Tej
                           ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Gianluca Guida @ 2009-03-26 10:57 UTC (permalink / raw)
  To: Tej; +Cc: George Dunlap, xen-devel, Keir Fraser

Hi,

Sorry for the late reply.

On Mar 26, 2009, at 7:59 AM, Tej wrote:

> On Mon, Mar 2, 2009 at 9:36 PM, Tej <bewith.tej@gmail.com> wrote:
>> On Sat, Feb 28, 2009 at 12:32 AM, Gianluca Guida
>> <gianluca.guida@eu.citrix.com> wrote:
>>> While I still need to test the patch (building 3.3 right now) and to
>>> understand gcov internals, I think that a few comments can be  
>>> done, mostly
>>> aestethicals.
>>>
>>> - xen coding style: Using four-spaces tabs is generally the  
>>> tradition. Also
>>> I do prefer to have brackets that start code blocks on a new line  
>>> aligned to
>>> the previous line, but that's not followed everywhere in the code.
>>>
>>> - Makefiles: while the num=$*.c is still a mystery to me, my  
>>> question is: do
>>> you really need to make links with different names to files compiled
>>> multiple times? If so, it would be useful to remove them on 'make  
>>> clean'.
>>> Also, it would be useful to make this feature enabled with a  
>>> compile-time
>>> option.
>>
>> Attached patches address the four-spaces tabs and make clean issues.
>> Hope the code is more clean and readable
>
> Hi
>
> Sorry for interrupting you guys from your busy schedule, as i just
> want to know the current status for GCOV patches i submitted early
> this month.
> any feedback on "testing/coding/design/feature" will be appreciated.

I've been testing it for a while, and it seems an useful and  
interesting feature.

A few comments, though:

- As I said, while the patch is minimal enough, the feature requires  
some intrusive changes in Xen, like changing the linker script (via  
the definition of CONSTRUCTORS) and in general it changes the compiler  
behavior, so I think it should be mandatory to make this feature  
explicitely enabled at compile time, e.g.  gcov=y.
- I am talking about Xen code only, but I think a few part of the  
codes should be more clear. I find very confusing the difference  
between xen_gcov_info and gcov_info, and all the instructions that  
uses them. A better naming scheme for the variables and more comments  
would be appreciated.

Thanks!
Gianluca

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
  2009-03-26 10:57       ` Gianluca Guida
@ 2009-03-26 15:11         ` Tej
  2009-04-06 15:04         ` Tej
  2009-04-29 14:56         ` Tej
  2 siblings, 0 replies; 8+ messages in thread
From: Tej @ 2009-03-26 15:11 UTC (permalink / raw)
  To: Gianluca Guida; +Cc: George Dunlap, xen-devel, Keir Fraser

On Thu, Mar 26, 2009 at 4:27 PM, Gianluca Guida
<Gianluca.Guida@eu.citrix.com> wrote:
> Hi,
>
> Sorry for the late reply.
>
> On Mar 26, 2009, at 7:59 AM, Tej wrote:
>
>> On Mon, Mar 2, 2009 at 9:36 PM, Tej <bewith.tej@gmail.com> wrote:
>>>
>>> On Sat, Feb 28, 2009 at 12:32 AM, Gianluca Guida
>>> <gianluca.guida@eu.citrix.com> wrote:
>>>>
>>>> While I still need to test the patch (building 3.3 right now) and to
>>>> understand gcov internals, I think that a few comments can be done,
>>>> mostly
>>>> aestethicals.
>>>>
>>>> - xen coding style: Using four-spaces tabs is generally the tradition.
>>>> Also
>>>> I do prefer to have brackets that start code blocks on a new line
>>>> aligned to
>>>> the previous line, but that's not followed everywhere in the code.
>>>>
>>>> - Makefiles: while the num=$*.c is still a mystery to me, my question
>>>> is: do
>>>> you really need to make links with different names to files compiled
>>>> multiple times? If so, it would be useful to remove them on 'make
>>>> clean'.
>>>> Also, it would be useful to make this feature enabled with a
>>>> compile-time
>>>> option.
>>>
>>> Attached patches address the four-spaces tabs and make clean issues.
>>> Hope the code is more clean and readable
>>
>> Hi
>>
>> Sorry for interrupting you guys from your busy schedule, as i just
>> want to know the current status for GCOV patches i submitted early
>> this month.
>> any feedback on "testing/coding/design/feature" will be appreciated.
>
> I've been testing it for a while, and it seems an useful and interesting
> feature.

glad to hear this

>
> A few comments, though:
>
> - As I said, while the patch is minimal enough, the feature requires some
> intrusive changes in Xen, like changing the linker script (via the
> definition of CONSTRUCTORS) and in general it changes the compiler behavior,
> so I think it should be mandatory to make this feature explicitely enabled
> at compile time, e.g.  gcov=y.

> - I am talking about Xen code only, but I think a few part of the codes
> should be more clear. I find very confusing the difference between
> xen_gcov_info and gcov_info, and all the instructions that uses them. A
> better naming scheme for the variables and more comments would be
> appreciated.

basically there should not be no difference between xen_gcov_info and
gcov_info, but due to some earlier implementation difficulties we kept
 two different structure. In final implementation those difference
narrow down drastically but we kept the both structures. I will look
back into this.

and All comments will be addressed asap.

thanks
-tej

>
> Thanks!
> Gianluca
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
  2009-03-26 10:57       ` Gianluca Guida
  2009-03-26 15:11         ` Tej
@ 2009-04-06 15:04         ` Tej
  2009-04-29 14:56         ` Tej
  2 siblings, 0 replies; 8+ messages in thread
From: Tej @ 2009-04-06 15:04 UTC (permalink / raw)
  To: Gianluca Guida; +Cc: George Dunlap, xen-devel, Keir Fraser

[-- Attachment #1: Type: text/plain, Size: 3199 bytes --]

On Thu, Mar 26, 2009 at 4:27 PM, Gianluca Guida
<Gianluca.Guida@eu.citrix.com> wrote:
> Hi,
>
> Sorry for the late reply.
>
> On Mar 26, 2009, at 7:59 AM, Tej wrote:
>
>> On Mon, Mar 2, 2009 at 9:36 PM, Tej <bewith.tej@gmail.com> wrote:
>>>
>>> On Sat, Feb 28, 2009 at 12:32 AM, Gianluca Guida
>>> <gianluca.guida@eu.citrix.com> wrote:
>>>>
>>>> While I still need to test the patch (building 3.3 right now) and to
>>>> understand gcov internals, I think that a few comments can be done,
>>>> mostly
>>>> aestethicals.
>>>>
>>>> - xen coding style: Using four-spaces tabs is generally the tradition.
>>>> Also
>>>> I do prefer to have brackets that start code blocks on a new line
>>>> aligned to
>>>> the previous line, but that's not followed everywhere in the code.
>>>>
>>>> - Makefiles: while the num=$*.c is still a mystery to me, my question
>>>> is: do
>>>> you really need to make links with different names to files compiled
>>>> multiple times? If so, it would be useful to remove them on 'make
>>>> clean'.
>>>> Also, it would be useful to make this feature enabled with a
>>>> compile-time
>>>> option.
>>>
>>> Attached patches address the four-spaces tabs and make clean issues.
>>> Hope the code is more clean and readable
>>
>> Hi
>>
>> Sorry for interrupting you guys from your busy schedule, as i just
>> want to know the current status for GCOV patches i submitted early
>> this month.
>> any feedback on "testing/coding/design/feature" will be appreciated.
>
> I've been testing it for a while, and it seems an useful and interesting
> feature.
>
> A few comments, though:
>
> - As I said, while the patch is minimal enough, the feature requires some
> intrusive changes in Xen, like changing the linker script (via the
> definition of CONSTRUCTORS) and in general it changes the compiler behavior,
> so I think it should be mandatory to make this feature explicitely enabled
> at compile time, e.g.  gcov=y.

patches attached added the compile time options gcov=y
a small readme is attached, which can be probably help in compiling
correctly at first time

gcov=n (default) disable every code part added by xen-gcov-core patch
(entry.S, core module, gcov constructor etc.)

Now in case, we have configured proc module (y || m) but did not use
gcov=y  option during compilation then proc module refuses to insert
as there is no hypercall added for gcov and we return -ENOENT (insmod
error type). Better suggestion are more than welcome

(i really don't know if we can make Kernel configuration and xen
configuration in sync e.g gcov=y depends on Kconfig "xen-gcov-proc" or
vice versa)

> - I am talking about Xen code only, but I think a few part of the codes
> should be more clear. I find very confusing the difference between
> xen_gcov_info and gcov_info, and all the instructions that uses them. A
> better naming scheme for the variables and more comments would be
> appreciated.

this issue still we are working on it. But if we keep two structure as
it is then we will have less complex core and proc implementation.

comments??????


Sorry for some delay.

>
> Thanks!
> Gianluca
>

[-- Attachment #2: linux-2.6.18-gcov-v4.patch --]
[-- Type: application/octet-stream, Size: 41893 bytes --]

diff -r 67a7ffcc5067 drivers/xen/Kconfig
--- a/drivers/xen/Kconfig	Wed Apr 01 11:43:01 2009 +0100
+++ b/drivers/xen/Kconfig	Mon Apr 06 18:45:41 2009 +0530
@@ -287,6 +287,12 @@
 	help
 	  Xen hypervisor attributes will show up under /sys/hypervisor/.
 
+config XEN_GCOV_PROC
+	tristate "Code covarage for hypervisor"
+	help
+	  Select to profile Xen Hypervisor only. Create /proc/xen/gcov entry
+	  which provides access to the current code coverage state of xen hypervisor
+
 choice
 	prompt "Xen version compatibility"
 	default XEN_COMPAT_030002_AND_LATER
diff -r 67a7ffcc5067 drivers/xen/Makefile
--- a/drivers/xen/Makefile	Wed Apr 01 11:43:01 2009 +0100
+++ b/drivers/xen/Makefile	Mon Apr 06 18:45:41 2009 +0530
@@ -3,6 +3,7 @@
 obj-y	+= evtchn/
 obj-y	+= xenbus/
 obj-y	+= char/
+obj-y	+= gcov/
 
 obj-y	+= util.o
 obj-$(CONFIG_XEN_BALLOON)		+= balloon/
diff -r 67a7ffcc5067 drivers/xen/core/xen_proc.c
--- a/drivers/xen/core/xen_proc.c	Wed Apr 01 11:43:01 2009 +0100
+++ b/drivers/xen/core/xen_proc.c	Mon Apr 06 18:45:41 2009 +0530
@@ -3,7 +3,7 @@
 #include <linux/proc_fs.h>
 #include <xen/xen_proc.h>
 
-static struct proc_dir_entry *xen_base;
+struct proc_dir_entry *xen_base;
 
 struct proc_dir_entry *create_xen_proc_entry(const char *name, mode_t mode)
 {
@@ -20,4 +20,5 @@
 	remove_proc_entry(name, xen_base);
 }
 
+EXPORT_SYMBOL(xen_base);
 EXPORT_SYMBOL_GPL(remove_xen_proc_entry); 
diff -r 67a7ffcc5067 drivers/xen/gcov/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/Makefile	Mon Apr 06 18:45:41 2009 +0530
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_GCOV_PROC) := xen-gcov-proc.o
diff -r 67a7ffcc5067 drivers/xen/gcov/xen-gcov-proc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/xen-gcov-proc.c	Mon Apr 06 18:45:41 2009 +0530
@@ -0,0 +1,1132 @@
+/**
+ *  @file xen-gcov-proc.c
+ *
+ *  @Author: Hubertus Franke <frankeh@us.ibm.com>
+ *           Rajan Ravindran <rajancr@us.ibm.com>
+ *           Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
+ *           Paul Larson
+ *
+ *  Modified by tej parkash and team for Xen (tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/xen-gcov.h>
+#include <xen/evtchn.h>
+#include <xen/xen_proc.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+
+MODULE_LICENSE("GPL");
+
+#define GCOV_PROC_HEADER    "xen-gcov-proc: "
+#define GCOV_PROC_ROOT        "gcov"
+#define GCOV_PROC_VMLINUX    "vmlinux"
+#define PAD8(x)            (((x) + 7) & ~7)
+
+/* If set to non-zero, create links to additional files in proc filesystem
+ * entries. */
+static int xen_gcov_link = 1;
+
+/*count update frequency*/
+static int xen_gcov_update = 5;
+
+#ifdef MODULE
+/* Parameter handling. */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+/* module_param is a 2.6 thing (though there are compat macros since 2.4.25) */
+MODULE_PARM(xen_gcov_link, "i");
+MODULE_PARM(xen_gcov_update, "i");
+#else
+module_param(xen_gcov_link, int, 0400);
+module_param(xen_gcov_update, int, 0400);
+#endif                /* LINUX_VERSION_CODE */
+
+MODULE_PARM_DESC(xen_gcov_link, "If set to non-zero, create links to additional "
+         "files in proc filesystem entries");
+MODULE_PARM_DESC(xen_gcov_update, "decides the frequency of hypervisor count"
+         "update and default value is 5sec.");
+#else                /* MODULE */
+/* Called when 'xen_gcov_link' is specified on the kernel command line. */
+static int __init xen_gcov_link_setup(char *str)
+{
+    xen_gcov_link = (int)simple_strtol(str, NULL, 10);
+    return 1;
+}
+
+__setup("xen_gcov_link=", xen_gcov_link_setup);
+
+static int __init xen_gcov_update_setup(char *str)
+{
+    xen_gcov_update = (int)simple_strtol(str, NULL, 10);
+    return 1;
+}
+
+__setup("xen_gcov_update=", xen_gcov_update_setup);
+
+#endif                /* MODULE */
+
+/* create sysclt entry @/proc/sys/xen
+ * accomplish the count update frequency
+ * perform: echo <num> >/proc/sys/xen/xen_gcov_update
+ */
+#ifdef CONFIG_SYSCTL
+static ctl_table gcov_table[] = {
+    {
+        .ctl_name     = 4,
+    .procname     = "xen_gcov_update",
+    .data         = &xen_gcov_update,
+    .maxlen     = sizeof(xen_gcov_update),
+    .mode         = 0644,
+    .proc_handler     = proc_dointvec,
+    }
+    ,
+    {.ctl_name = 0}
+};
+
+static ctl_table gcov_root_table[] = {
+    {
+        .ctl_name     = 123,
+        .procname     = "xen",
+        .mode         = 0555,
+        .child        = gcov_table},
+    {.ctl_name = 0}
+};
+
+/*sysclt table specific declaration*/
+static struct ctl_table_header *gcov_table_header;
+
+#endif /* CONFIG_SYSCTL */
+
+/* Protect global variables from concurrent access. */
+static DEFINE_MUTEX(gcov_mutex);
+static DEFINE_MUTEX(write_mutex);
+
+/*workqueue function*/
+static void xeno_prof(void *);
+
+/*workqueue for the calling hypervisor count head*/
+static DECLARE_WORK(work, xeno_prof, NULL);
+
+/* Filesystem entry for /proc/gcov/vmlinux */
+static struct proc_dir_entry *proc_vmlinux = NULL;
+
+/* First leaf node. */
+static struct gcov_ftree_node *leaf_nodes = NULL;
+
+/* Cached node used to speed up sequential reads in /proc/vmlinux. */
+static struct gcov_ftree_node *cached_node = NULL;
+
+/* Root node for internal data tree. */
+static struct gcov_ftree_node tree_root;
+
+struct gcov_variables *gcov_var = NULL;
+
+/* Filename extension for data files. */
+static const char *da_ending = "gcda";
+
+/* Array of filename endings to use when creating links. */
+static const char *endings[] = { "gcno", "c" };
+
+/*Info which keeps no if file in xen info to be profile*/
+unsigned int xen_file_nos;
+
+/*head of xen-gcov-info linked list*/
+struct xen_gcov_info *list_head = NULL;
+
+/* xen_sourcepath length */
+static int sourcepath_len;
+
+unsigned long xen_gcov_version;
+
+/* Create nodes for all gcda files based on xen_file_nos value */
+static inline int create_bb_list(void)
+{
+    int i;
+    struct xen_gcov_info *node = NULL, *tmp_node = NULL;
+
+    for (i = 0; i < gcov_var->n_files; i++) 
+    {
+        node = kmalloc(sizeof(struct xen_gcov_info), GFP_KERNEL);
+        node->counts[0].values =
+            kmalloc(sizeof(gcov_type) * gcov_var->ctr_num[i],
+                GFP_KERNEL);
+        node->functions =
+            kmalloc(sizeof(struct xen_gcov_fn_info) *
+                gcov_var->n_funcs[i], GFP_KERNEL);
+        node->next = NULL;
+        if (list_head == NULL) 
+        {
+            list_head = node;
+            tmp_node = node;
+        } 
+        else 
+        {
+            tmp_node->next = node;
+            tmp_node = node;
+        }
+    }
+    
+    return 0;
+}
+
+/* free memory allocated for gcov_info list */
+static inline int free_bb_list(void)
+{
+    struct xen_gcov_info *node = NULL;
+    struct xen_gcov_info *temp = list_head;
+    for (;temp!=NULL;temp = temp->next) 
+    {
+        node = temp;
+        kfree(node->counts[0].values);
+        kfree(node->functions);
+        kfree(node);
+    }
+
+    return 0;
+}
+
+/* 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 (adapted from gcc/gcov-io.h). */
+static int store_gcov_type(gcov_type value, char *dest, size_t bytes)
+{
+    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;
+
+    return 0;
+}
+
+/* Determine whether counter TYPE is active in BB. */
+static int counter_active(struct xen_gcov_info *bb, unsigned int type)
+{
+    return ((1 << type) & bb->ctr_mask);
+}
+
+/* Return size of .gcda counter section. */
+static size_t
+sizeof_counter_data(struct xen_gcov_info *bb, unsigned int row,
+            unsigned int col)
+{
+
+    struct xen_gcov_fn_info *func =
+        (struct xen_gcov_fn_info *)&bb->functions[row];
+
+    if (counter_active(bb, col)) 
+    {
+        return /* tag */ 4 + /* length */ 4 +
+            /* counters */ func->n_ctrs[col] * 8;
+    } 
+    else
+        return 0;
+}
+
+/* Return size of .gcda data section associated with FUNC.  */
+static size_t sizeof_func_data(struct xen_gcov_info *bb, unsigned int row)
+{
+    size_t result;
+    unsigned int type;
+
+    result =
+        /* tag */ 4 + /* length */ 4 + /* ident */ 4 + /* checksum */ 4;
+    for (type = 0; type < GCOV_COUNTERS; type++)
+        result += sizeof_counter_data(bb, row, type);
+    return result;
+}
+
+/* Get size of .gcda file associated with BB. */
+static size_t sizeof_da_file(struct xen_gcov_info *bb)
+{
+    size_t result;
+    unsigned int i;
+
+    result = /* magic */ 4 + /* version */ 4 + /* stamp */ 4;
+    for (i = 0; i < bb->n_functions; i++)
+        result += sizeof_func_data(bb, i);
+    return result;
+}
+
+/* Store a 32 bit unsigned integer value in GCOV format to memory at address
+ *  * BUF. */
+static void store_int32(uint32_t i, char *buf)
+{
+    uint32_t *p;
+    p = (int *)buf;
+    *p = i;
+
+}
+
+/* Store a 64 bit unsigned integer value in GCOV format to memory at address
+ *  * BUF. */
+static void store_int64(uint64_t i, char *buf)
+{
+    store_int32((uint32_t) (i & 0xffff), buf);
+    store_int32((uint32_t) (i >> 32), buf + 4);
+}
+
+/* Store a gcov counter in GCOV format to memory at address BUF. The counter is
+ *  * identified by BB, FUNC, TYPE and COUNTER. */
+static void
+store_counter(struct xen_gcov_info *bb, unsigned int func, unsigned int type,
+          unsigned int counter, char *buf)
+{
+    unsigned int counter_off;
+    unsigned int type_off;
+    unsigned int i;
+    struct xen_gcov_fn_info *func_ptr;
+    /* Get offset into counts array */
+    type_off = 0;
+    for (i = 0; i < type; i++)
+        if (counter_active(bb, i))
+        type_off++;
+    /* Get offset into values array. */
+    counter_off = counter;
+    for (i = 0; i < func; i++) 
+    {
+        func_ptr = (struct xen_gcov_fn_info *)&bb->functions[i];
+        counter_off += func_ptr->n_ctrs[type];
+    }
+    /* Create in temporary storage */
+    store_int64(bb->counts[type_off].values[counter_off], buf);
+}
+
+/* Store a counter section in userspace memory. The counter section is
+ *  * identified by BB, FUNC and TYPE. The destination address is BUF. Store at
+ *   * most COUNT bytes beginning at OFFSET. Return the number of bytes stored or a
+ *    * negative value on error. */
+static ssize_t
+store_counter_data(struct xen_gcov_info *bb, unsigned int func,
+           unsigned int type, char *buf, size_t count, loff_t offset)
+{
+    struct xen_gcov_fn_info *func_ptr;
+    char data[8]; 
+    char *from;
+    size_t len;
+    ssize_t result;
+    unsigned int i;
+
+    func_ptr = (struct xen_gcov_fn_info *)&bb->functions[func];
+    result = 0;
+    while (count > 0) 
+    {
+        if (offset < 4) 
+        {
+            /* Tag ID */
+            store_int32((uint32_t) GCOV_TAG_FOR_COUNTER(type),
+               data);
+            len = 4 - offset;
+            from = data + offset;
+        } 
+        else if (offset < 8) 
+        {
+            /* Tag length in groups of 4 bytes */
+            store_int32((uint32_t)
+            func_ptr->n_ctrs[type] * 2, data);
+            len = 4 - (offset - 4);
+            from = data + (offset - 4);
+        } 
+        else 
+        {
+            /* Actual counter data */
+            i = (offset - 8) / 8;
+            /* Check for EOF */
+            if (i >= func_ptr->n_ctrs[type])
+                break;
+            store_counter(bb, func, type, i, data);
+            len = 8 - (offset - 8) % 8;
+            from = data + (offset - 8) % 8;
+        }
+        if (len > count)
+        len = count;
+        if (copy_to_user(buf, from, len))
+        return -EFAULT;
+        count -= len;
+        buf += len;
+        offset += len;
+        result += len;
+    }
+
+    return result;
+}
+
+/* Store a function section and associated counter sections in userspace memory.
+ *  * The function section is identified by BB and FUNC. The destination address is
+ *   * BUF. Store at most COUNT bytes beginning at OFFSET. Return the number of
+ *    * bytes stored or a negative value on error. */
+static ssize_t
+store_func_data(struct xen_gcov_info *bb, unsigned int func, char *buf,
+        size_t count, loff_t offset)
+{
+    char data[4];
+    char *from;
+    size_t len;
+    unsigned int i;
+    loff_t off;
+    size_t size;
+    ssize_t result;
+    ssize_t rc;
+    result = 0;
+    while (count > 0) 
+    {
+        if (offset < 16) 
+        {
+            if (offset < 4) 
+            {
+                /* Tag ID */
+                store_int32((uint32_t) GCOV_TAG_FUNCTION, data);
+                len = 4 - offset;
+                from = data + offset;
+            } 
+            else if (offset < 8) 
+            {
+                /* Tag length */
+                store_int32(2, data);
+                len = 4 - (offset - 4);
+                from = data + (offset - 4);
+            } 
+            else if (offset < 12) 
+            {
+                /* Function ident */
+                store_int32((uint32_t) bb->
+                functions[func].ident, data);
+                len = 4 - (offset - 8);
+                from = data + (offset - 8);
+            } 
+            else 
+            {
+                /* Function checksum */
+                store_int32((uint32_t) bb->
+                functions[func].checksum, data);
+                len = 4 - (offset - 12);
+                from = data + (offset - 12);
+            }
+            /* Do the actual store */
+            if (len > count)
+                len = count;
+            if (copy_to_user(buf, from, len))
+                return -EFAULT;
+        }  
+        else 
+        {
+            off = 16;
+            len = 0;
+            for (i = 0; i < GCOV_COUNTERS; i++) 
+            {
+                size = sizeof_counter_data(bb, func, i);
+                if (offset < off + size) 
+                {
+                    rc = store_counter_data(bb, func, i,
+                       buf, count,
+                       offset - off);
+                       if (rc < 0)
+                           return rc;
+                       len = rc;
+                       break;
+                }
+                off += size;
+            }
+            /* Check for EOF */
+            if (i == GCOV_COUNTERS)
+                break;
+         } 
+    count -= len;
+    buf += len;
+    offset += len;
+    result += len;
+    }
+
+    return result;
+}
+
+/* Store data of .gcda file associated with NODE to userspace memory at BUF.
+ *  * OFFSET specifies the offset inside the .da file. COUNT is the maximum
+ *   * number of bytes to store. Return the number of bytes stored, zero for
+ *    * EOF or a negative number in case of error. */
+static ssize_t
+store_da_file(struct gcov_ftree_node *node, char *buf, size_t count,
+          loff_t offset)
+{
+
+    struct xen_gcov_info *bb;
+    char data[4];
+    char *from;
+    size_t len;
+    unsigned int i;
+    loff_t off;
+    size_t size;
+    ssize_t result;
+    ssize_t rc;
+
+    bb = node->bb;
+    result = 0;
+    while (count > 0) 
+        {
+            if (offset < 12) 
+            {
+                if (offset < 4) 
+                {
+                    /* File magic */
+                    store_int32((uint32_t) GCOV_DATA_MAGIC, data);
+                    len = 4 - offset;
+                    from = data + offset;
+                } 
+                 else if (offset < 8) 
+                {
+                    /* File format/GCC version */
+                    store_int32(xen_gcov_version, data);
+                    len = 4 - (offset - 4);
+                    from = data + (offset - 4);
+                } 
+                else 
+                {
+                    /* Time stamp */
+                    store_int32((uint32_t) bb->stamp, data);
+                    len = 4 - (offset - 8);
+                    from = data + (offset - 8);
+                }
+            /* Do the actual store */
+            if (len > count)
+                len = count;
+            if (copy_to_user(buf, from, len))
+                return -EFAULT;
+            } 
+            else 
+            {
+                off = 12;
+                len = 0;
+                for (i = 0; i < bb->n_functions; i++) 
+                {
+
+                    size = sizeof_func_data(bb, i);
+                    if (offset < off + size) 
+                    {
+                        rc = store_func_data(bb, i, buf, count,
+                            offset - off);
+
+                        if (rc < 0)
+                            return rc;
+                        len = rc;
+                         break;
+                    }
+                    off += size;
+
+                }
+                /* Check for EOF */
+                if (i == bb->n_functions)
+                    break;
+            }
+            count -= len;
+            buf += len;
+            offset += len;
+            result += len;
+       }
+
+    return result;
+}
+
+/* Return size of header which precedes .da file entry associated with BB
+ * in the vmlinux file. */
+static size_t sizeof_vmlinux_header(struct xen_gcov_info *bb)
+{
+    return 8 + PAD8(strlen(bb->file) + 1);
+}
+
+/* Store data of header which precedes .da file entry associated with NODE
+ * in the vmlinux file to userspace memory at BUF. OFFSET specifies the offset
+ * inside the header file. COUNT is the maximum number of bytes to store.
+ * Return the number of bytes stored, zero for EOF or a negative number in
+ * case of error. */
+static ssize_t
+store_vmlinux_header(struct gcov_ftree_node *node, char *buf, size_t count,
+             loff_t offset)
+{
+    char data[8];
+    char *from;
+    size_t namelen;
+    ssize_t stored;
+    size_t len;
+
+    namelen = strlen(node->bb->file);
+    stored = 0;
+    while (count > 0) 
+    {
+        if (offset < 8) 
+        {
+            /* Filename length */
+            if (store_gcov_type(PAD8(namelen + 1), data, 8))
+                return -EINVAL;
+            from = data + offset;
+            len = 8 - offset;
+        } 
+        else if (offset < 8 + namelen) 
+        {
+            /* Filename */
+            from = (char *)node->bb->file + offset - 8;
+            len = namelen - (offset - 8);
+        } 
+        else if (offset < node->header_size) 
+        {
+            /* Nil byte padding */
+            memset(data, 0, 8);
+            from = data;
+            len = PAD8(namelen + 1) - (offset - 8);
+        }   
+        else
+            break;
+        if (len > count)
+            len = count;
+        if (copy_to_user(buf, from, len))
+            return -EFAULT;
+
+        stored += len;
+        count -= len;
+        offset += len;
+        buf += len;
+    }
+
+    return stored;
+}
+
+/* Update data related to vmlinux file. */
+static void update_vmlinux_data(void)
+{
+    struct gcov_ftree_node *node;
+    loff_t offset;
+
+    offset = 0;
+    for (node = leaf_nodes; node; node = node->next) 
+    {
+        node->offset = offset;
+        node->da_size = sizeof_da_file(node->bb);
+        node->header_size = sizeof_vmlinux_header(node->bb);
+        offset += node->header_size + node->da_size;
+    }
+    proc_vmlinux->size = offset;
+    cached_node = NULL;
+}
+
+/* Read .da or vmlinux file. */
+static ssize_t
+read_gcov(struct file *file, char *buf, size_t count, loff_t * pos)
+{
+    struct gcov_ftree_node *node;
+    struct proc_dir_entry *dir_entry;
+    ssize_t rc;
+
+    mutex_lock(&gcov_mutex);
+    dir_entry = PDE(file->f_dentry->d_inode);
+    rc = 0;
+    if (dir_entry == proc_vmlinux) 
+    {
+        /* Are we in a sequential read? */
+        if (cached_node && (*pos >= cached_node->offset))
+            node = cached_node;
+        else
+            node = leaf_nodes;
+            /* Find node corresponding to offset */
+        while (node && node->next && (*pos >= node->next->offset))
+            node = node->next;
+            cached_node = node;
+            if (node) 
+            {
+                if (*pos - node->offset < node->header_size)
+                    rc = store_vmlinux_header(node, buf, count,
+                    *pos - node->offset);
+                else
+                {
+                    rc = store_da_file(node, buf, count,
+                    *pos - node->offset -
+                    node->header_size);
+                }
+            }
+    } 
+    else 
+    {
+        node = (struct gcov_ftree_node *)dir_entry->data;
+        if (node) 
+        {
+            rc = store_da_file(node, buf, count, *pos);
+        }
+    }
+    if (rc > 0)
+        *pos += rc;
+    mutex_unlock(&gcov_mutex);
+    return rc;
+}
+
+/* Reset counters on write request. */
+static ssize_t
+write_gcov(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+    struct gcov_ftree_node *node;
+    struct proc_dir_entry *dir_entry;
+
+    mutex_lock(&write_mutex);
+    dir_entry = PDE(file->f_dentry->d_inode);
+    if (dir_entry == proc_vmlinux) 
+    {
+        /* Reset all nodes */
+        node = leaf_nodes;
+        if (HYPERVISOR_gcovprof_op(GCOVPROF_reset, NULL, !RESET_GCDA)) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                   "Failed to reset the vmlinux\n");
+            mutex_unlock(&write_mutex);
+            return EIO;
+        }
+
+    } 
+    else 
+    {
+        node = (struct gcov_ftree_node *)dir_entry->data;
+        if (HYPERVISOR_gcovprof_op
+            (GCOVPROF_reset, node->bb->file, RESET_GCDA)) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                   "Failed to reset file %s\n", node->bb->file);
+            mutex_unlock(&write_mutex);
+            return EIO;
+        }
+    }
+    mutex_unlock(&write_mutex);
+    return count;
+}
+
+/* Return a newly allocated copy of STRING. */
+static char *strdup(const char *string)
+{
+    char *result;
+
+    result = (char *)kmalloc(strlen(string) + 1, GFP_KERNEL);
+    if (result)
+        strcpy(result, string);
+    return result;
+}
+
+/* Allocate a new node and fill in NAME and BB. */
+static struct gcov_ftree_node *alloc_node(const char *name,
+                      struct xen_gcov_info *bb)
+{
+    struct gcov_ftree_node *node;
+
+    node = (struct gcov_ftree_node *)
+        kmalloc(sizeof(struct gcov_ftree_node), GFP_KERNEL);
+    if (!node)
+        return NULL;
+    memset(node, 0, sizeof(struct gcov_ftree_node));
+    node->fname = strdup(name);
+    if (!node->fname) 
+    {
+        kfree(node);
+        return NULL;
+    }
+    node->bb = bb;
+    return node;
+}
+
+/* Free memory allocated for NODE. */
+static void free_node(struct gcov_ftree_node *node)
+{
+    if (node == &tree_root)
+        return;
+    if (node->fname)
+    kfree(node->fname);
+    kfree(node);
+}
+
+/* Remove proc filesystem entries associated with NODE. */
+static void delete_from_proc(struct gcov_ftree_node *node)
+{
+    struct proc_dir_entry *parent;
+    int i;
+
+    if (node->parent)
+        parent = node->parent->proc[0];
+    else
+        parent = xen_base;
+    for (i = 0; i < sizeof(node->proc) / sizeof(node->proc[0]); i++)
+        if (node->proc[i])
+            remove_proc_entry(node->proc[i]->name, parent);
+}
+
+/* Release all resources associated with NODE. If NODE is a directory node,
+ * also clean up all children. */
+static void cleanup_node(struct gcov_ftree_node *node)
+{
+    struct gcov_ftree_node *curr;
+    struct gcov_ftree_node *next;
+    struct gcov_ftree_node *prev;
+
+    next = node;
+    do 
+    {
+        /* Depth first traversal of all children */
+        curr = next;
+        while (curr->files)
+            curr = curr->files;
+        if (curr->sibling)
+            next = curr->sibling;
+        else
+            next = curr->parent;
+        /* Remove from tree */
+        if (curr->parent) 
+        {
+            if (curr->parent->files == curr)
+                curr->parent->files = curr->sibling;
+            else 
+            {
+                for (prev = curr->parent->files;
+                    prev->sibling != curr;
+                    prev = prev->sibling) ;
+                    prev->sibling = curr->sibling;
+            }
+        }
+        /* Remove from leaf node list if necessary */
+        if (curr->bb) 
+        {
+            if (leaf_nodes == curr)
+                leaf_nodes = curr->next;
+            else 
+            {
+                for (prev = leaf_nodes;
+                     prev && (prev->next != curr);
+                     prev = prev->next) ;
+                if (prev)
+                    prev->next = curr->next;
+            }
+        }
+        /* Delete node */
+        delete_from_proc(curr);
+        free_node(curr);
+    } while (node != curr);
+}
+
+/* Clean up NODE and containing path in case it would be left empty. */
+static void cleanup_node_and_path(struct gcov_ftree_node *node)
+{
+    while (node->parent &&
+        node->parent != &tree_root && !node->parent->files->sibling)
+    node = node->parent;
+    cleanup_node(node);
+}
+
+/* Create a new directory node named NAME under PARENT. Upon success return
+ * zero and update RESULT to point to the newly created node. Return non-zero
+ * otherwise. */
+static int
+create_dir_node(struct gcov_ftree_node *parent, char *name,
+        struct gcov_ftree_node **result)
+{
+    struct gcov_ftree_node *node;
+
+    /* Initialize new node */
+    node = alloc_node(name, NULL);
+    if (!node)
+        return -ENOMEM;
+    /* Create proc filesystem entry */
+    node->proc[0] = proc_mkdir(name, parent->proc[0]);
+    if (!node->proc[0]) 
+    {
+        free_node(node);
+        return -EIO;
+    }
+    /* Insert node into tree */
+    node->parent = parent;
+    node->sibling = parent->files;
+    parent->files = node;
+    *result = node;
+    return 0;
+}
+
+static struct file_operations proc_gcov_operations = {
+    .owner        = THIS_MODULE,
+    .read         = read_gcov,
+    .write        = write_gcov,
+};
+
+/* Create a new file node named NAME under PARENT. Associate node with BB.
+ * Return zero upon success, non-zero otherwise. */
+static int
+create_file_node(struct gcov_ftree_node *parent, char *name,
+         struct xen_gcov_info *bb)
+{
+    struct gcov_ftree_node *node;
+    char *link_target;
+    char *link_name;
+    int i;
+    /* Initialize new node */
+    node = alloc_node(name, bb);
+    if (!node)
+        return -ENOMEM;
+    /* Create proc filesystem entry */
+    node->proc[0] = create_proc_entry(name, S_IWUSR | S_IRUGO,
+                      parent->proc[0]);
+    if (!node->proc[0]) 
+    {
+        free_node(node);
+        return -EIO;
+    }
+    node->proc[0]->data = node;
+    node->proc[0]->proc_fops = &proc_gcov_operations;
+    node->proc[0]->size = sizeof_da_file(bb);
+    /* Create symbolic links */
+    if (xen_gcov_link) 
+    {
+        /* Note: temp string length is calculated as upper limit */
+        link_target = (char *)kmalloc(strlen(bb->file) +
+                          strlen(da_ending) +
+                          strlen(gcov_var->src_path),
+                          GFP_KERNEL);
+        if (!link_target) 
+        {
+            delete_from_proc(node);
+            free_node(node);
+            return -ENOMEM;
+        }
+        for (i = 0; i < sizeof(endings) / sizeof(endings[0]); i++) 
+        {
+
+            strcpy(link_target, bb->file);
+            link_target[strlen(link_target) -
+                    strlen(da_ending)] = 0;
+            strcat(link_target, endings[i]);
+            link_name = strrchr(link_target, '/') + 1;
+            node->proc[i + 1] = proc_symlink(link_name,
+                             parent->proc[0],
+                             link_target);
+            if (!node->proc[i + 1]) 
+            {
+                kfree(link_target);
+                delete_from_proc(node);
+                free_node(node);
+                return -EIO;
+             }
+        }
+        kfree(link_target);
+    }
+    /* Insert node into tree */
+    node->parent = parent;
+    node->sibling = parent->files;
+    parent->files = node;
+    node->next = leaf_nodes;
+    leaf_nodes = node;
+    return 0;
+}
+
+/* Return proc filesystem entry name for FILENAME. */
+static inline char *get_proc_filename(const char *filename)
+{
+    char *result;
+
+    result = strdup(filename + sourcepath_len + 1);
+    return result;
+}
+
+/* Create tree node and proc filesystem entry for BB. Create subdirectories as
+ * necessary. Return zero upon success, non-zero otherwise. */
+static int create_node(struct xen_gcov_info *bb)
+{
+    struct gcov_ftree_node *parent;
+    struct gcov_ftree_node *node;
+    char *filename;
+    char *curr;
+    char *next;
+    int rc;
+
+    filename = get_proc_filename(bb->file);
+    if (!filename)
+        return -ENOMEM;
+    /* Recreate directory path in proc filesystem */
+    parent = &tree_root;
+    for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) 
+    {
+        /* Skip empty path components */
+        if (curr == next)
+            continue;
+        *next = 0;
+        /* Check whether directory already exists */
+        for (node = parent->files;
+             node && (strcmp(node->fname, curr) != 0);
+             node = node->sibling) ;
+        if (!node) 
+        {
+            /* Create directory node */
+            rc = create_dir_node(parent, curr, &node);
+            if (rc) 
+            {
+                if (parent != &tree_root)
+                    cleanup_node_and_path(parent);
+                kfree(filename);
+                return rc;
+            }
+        }
+        parent = node;
+    }
+    rc = create_file_node(parent, curr, bb);
+    kfree(filename);
+    return rc;
+}
+
+static void xeno_prof(void *data)
+{
+   /*update linked list with profiling info and line counts */
+    WARN_ON(HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0));
+    
+   /*again schedule work */
+   schedule_delayed_work(&work, xen_gcov_update * HZ);
+
+}
+
+/* Invokes hypercall to get all the necessary gcov variables from core */
+static inline int gcov_core_vars(void)
+{
+
+    int ret;
+    gcov_var = kmalloc(sizeof(struct gcov_variables), GFP_KERNEL);
+    if ((ret =
+        HYPERVISOR_gcovprof_op(GCOVPROF_seek_vars, gcov_var, 0)) != 0)
+    return ret;
+    
+    xen_gcov_version = gcov_var->g_version;
+
+    gcov_var->ctr_num =
+        kmalloc(gcov_var->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+    gcov_var->n_funcs =
+        kmalloc(gcov_var->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+    if ((ret =
+        HYPERVISOR_gcovprof_op(GCOVPROF_seek_vars, gcov_var, 1)) != 0)
+    return ret;
+
+   sourcepath_len = strlen(gcov_var->src_path);
+   return ret;
+}
+
+static int __init xen_gcov_init(void)
+{
+    struct xen_gcov_info *bb;
+    int ret, rc = 0;
+    /* seek gcov core variable information & construct the gcov_info list */
+    ret = gcov_core_vars();
+    if (ret) {
+        printk(KERN_ERR GCOV_PROC_HEADER
+           "No Core variable, Check if GCOV core is compiled in or not\n");
+        return -ENOENT;
+    }
+
+    /*add sys entry in /proc/sys/xen for count update tuning*/    
+#ifdef CONFIG_SYSCTL
+    gcov_table_header = register_sysctl_table(gcov_root_table, 0);
+    if (!gcov_table_header)
+        printk(KERN_INFO GCOV_PROC_HEADER
+              "no /proc/sys/xen added\n");
+#endif
+    printk(KERN_INFO GCOV_PROC_HEADER "xen_gcov_link=%d xen_gcov_update=%d\n", 
+                        xen_gcov_link, xen_gcov_update);
+    
+    /* Create xen_gcov_info linked list */
+    create_bb_list();
+
+    /*populate the linked list with profiling info and line counts */
+    ret = HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0);
+    if (ret) {
+        printk(KERN_ERR GCOV_PROC_HEADER
+                "Failed to retrive hypervisor profiling info\n");
+        return ret;
+    }
+    /* Initializing root node and /proc/gcov entry */
+    tree_root.fname = GCOV_PROC_ROOT;
+    tree_root.proc[0] = proc_mkdir(tree_root.fname, xen_base);
+    if (!tree_root.proc[0]) { 
+        printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+           "create root proc filesystem entry\n");
+        return -EIO;
+    }
+    /* Create /proc/gcov/vmlinux entry */
+    proc_vmlinux = create_proc_entry(GCOV_PROC_VMLINUX, S_IWUSR | S_IRUGO,
+                 tree_root.proc[0]);
+    if (!proc_vmlinux) 
+    {
+        printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+            "create proc filesystem entry %s\n", GCOV_PROC_VMLINUX);
+        cleanup_node(&tree_root);
+        return -EIO;
+    }
+    proc_vmlinux->proc_fops = &proc_gcov_operations;
+
+    /*populate /proc/xen/gcov entry with data files*/
+    for (bb = list_head; bb != NULL; bb = bb->next) 
+    {
+        rc = create_node(bb);
+        if (rc) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                "init failed: could not create node for %s (err=%d)\n",
+            bb->file, rc);
+            remove_proc_entry(proc_vmlinux->name,
+                   tree_root.proc[0]);
+            cleanup_node(&tree_root);
+            return rc;
+        }
+    } /*done*/
+   update_vmlinux_data();
+
+   /*schedule for profiling update after <xen_gcov_update> secs*/
+   schedule_delayed_work(&work, xen_gcov_update * HZ);
+   printk(KERN_INFO GCOV_PROC_HEADER "init done\n");
+   kfree(gcov_var);
+   return 0;
+}
+
+/* Clean up module data. */
+static void __exit xen_gcov_cleanup(void)
+{
+    cancel_delayed_work(&work);
+    flush_scheduled_work();
+#ifdef CONFIG_SYSCTL
+    unregister_sysctl_table(gcov_table_header);
+#endif
+    remove_proc_entry(proc_vmlinux->name, tree_root.proc[0]);
+    cleanup_node(&tree_root);
+    free_bb_list();
+    printk(KERN_INFO GCOV_PROC_HEADER "Module is now unloaded\n");
+}
+
+module_init(xen_gcov_init);
+module_exit(xen_gcov_cleanup);
diff -r 67a7ffcc5067 include/asm-i386/mach-xen/asm/hypercall.h
--- a/include/asm-i386/mach-xen/asm/hypercall.h	Wed Apr 01 11:43:01 2009 +0100
+++ b/include/asm-i386/mach-xen/asm/hypercall.h	Mon Apr 06 18:45:41 2009 +0530
@@ -404,6 +404,13 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 
 
 #endif /* __HYPERCALL_H__ */
diff -r 67a7ffcc5067 include/asm-x86_64/mach-xen/asm/hypercall.h
--- a/include/asm-x86_64/mach-xen/asm/hypercall.h	Wed Apr 01 11:43:01 2009 +0100
+++ b/include/asm-x86_64/mach-xen/asm/hypercall.h	Mon Apr 06 18:45:41 2009 +0530
@@ -405,4 +405,11 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 #endif /* __HYPERCALL_H__ */
diff -r 67a7ffcc5067 include/xen/interface/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/xen/interface/xen-gcov.h	Mon Apr 06 18:45:41 2009 +0530
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *  xen-gcov.h
+ *   
+ *  Interface for populating hypervisor profiling info under /proc/xen
+ *  
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to
+ *  deal in the Software without restriction, including without limitation the
+ *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ *  sell copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *   
+ *  The above copyright notice and this permission notice shall be included in
+ *  all copies or substantial portions of the Software.
+ *   
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ *  DEALINGS IN THE SOFTWARE.
+ *   
+ *  Author: tej parkash <tej.parkash@hcl.in>
+ *  Written by tej parkash and team
+ *  
+ */
+/**********************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_seek_vars            0
+#define GCOVPROF_start		      1
+#define GCOVPROF_reset		      2
+#define GCOVPROF_last_op              3
+
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS                 5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+/*xen-gcov-proc specific macros*/
+#define RESET_GCDA      0
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+
+
+struct gcov_ftree_node
+{
+    char *fname;             		/* Hierarchy-relative name */
+    struct gcov_ftree_node *sibling; 	/* First sibling of this node */
+    struct gcov_ftree_node *files;   	/* First child of this node */
+    struct gcov_ftree_node *parent;  	/* Parent of this node */
+    struct proc_dir_entry *proc[4];  	/* Entries for .da, .bb, .bbg, .c */
+    struct xen_gcov_info *bb;           	/* Associated struct bb */
+    loff_t offset;           		/* Offset in vmlinux file */
+    size_t da_size;          		/* Size of associated .da file */
+    size_t header_size;      		/* Size of associated file header */
+    struct gcov_ftree_node *next;    	/* Next leaf node */
+};
+
+
+struct xen_gcov_fn_info
+{
+    gcov_unsigned_t ident;    		/* unique ident of function */
+    gcov_unsigned_t checksum; 		/* function checksum */
+    unsigned int n_ctrs[GCOV_COUNTERS]; /* instrumented counters */
+};
+typedef struct xen_gcov_fn_info xen_gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_fn_info_t);
+
+struct xen_gcov_ctr_info
+{
+    gcov_unsigned_t num;      		/* number of counters.  */
+    gcov_type *values;			/* thier values. */
+    gcov_merge_fn merge;      		/* merge function  */
+};
+typedef struct  xen_gcov_ctr_info xen_gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct xen_gcov_info
+{
+    gcov_unsigned_t version;  		/* expected version number */
+    struct xen_gcov_info *next;   	/* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    		/* uniquifying time stamp */
+    char file[100];
+
+    unsigned int n_functions;     	/* number of functions */
+    struct xen_gcov_fn_info *functions;	/* table of functions */
+
+    unsigned int ctr_mask;        	/* mask of counters instrumented.  */
+    struct xen_gcov_ctr_info counts[GCOV_COUNTERS]; /* count data */
+};
+typedef struct xen_gcov_info xen_gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_info_t);
+
+struct gcov_variables
+{
+    char src_path[100];			/* source path */
+    unsigned long g_version;		/* gcov version */
+    unsigned int n_files;		/* num of file in hypervisor code */
+    gcov_unsigned_t *ctr_num;		/* count variables */
+    unsigned int *n_funcs;     		/* num of funcs per bb struct */
+	
+};
+typedef struct gcov_variables gcov_variables_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_variables_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r 67a7ffcc5067 include/xen/interface/xen.h
--- a/include/xen/interface/xen.h	Wed Apr 01 11:43:01 2009 +0100
+++ b/include/xen/interface/xen.h	Mon Apr 06 18:45:41 2009 +0530
@@ -91,6 +91,7 @@
 #define __HYPERVISOR_sysctl               35
 #define __HYPERVISOR_domctl               36
 #define __HYPERVISOR_kexec_op             37
+#define __HYPERVISOR_gcovprof_op          38
 
 /* Architecture-specific hypercall definitions. */
 #define __HYPERVISOR_arch_0               48
diff -r 67a7ffcc5067 include/xen/xen_proc.h
--- a/include/xen/xen_proc.h	Wed Apr 01 11:43:01 2009 +0100
+++ b/include/xen/xen_proc.h	Mon Apr 06 18:45:41 2009 +0530
@@ -3,6 +3,8 @@
 #define __ASM_XEN_PROC_H__
 
 #include <linux/proc_fs.h>
+
+extern struct proc_dir_entry *xen_base;
 
 extern struct proc_dir_entry *create_xen_proc_entry(
 	const char *name, mode_t mode);

[-- Attachment #3: xen-3.3-gcov-v4.patch --]
[-- Type: application/octet-stream, Size: 18338 bytes --]

diff -r d89eaa22b5b9 xen/Rules.mk
--- a/xen/Rules.mk	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/Rules.mk	Mon Apr 06 18:57:59 2009 +0530
@@ -8,6 +8,15 @@
 perfc_arrays  ?= n
 crash_debug   ?= n
 frame_pointer ?= n
+
+# gcov profiler build for xen hypervisor only
+gcov          ?= n
+
+# xen source path to get update in .gcno files
+ifeq ($(gcov),y)
+xen_src := $(CURDIR)
+xentree := $(if $(xen_src),$(xen_src),$(CURDIR))
+endif
 
 XEN_ROOT=$(BASEDIR)/..
 include $(XEN_ROOT)/Config.mk
@@ -56,6 +65,7 @@
 ALL_OBJS-y               += $(BASEDIR)/arch/$(TARGET_ARCH)/built_in.o
 
 CFLAGS-y                += -g -D__XEN__
+CFLAGS-$(gcov)          += -fprofile-arcs -ftest-coverage -DCONFIG_XEN_GCOV
 CFLAGS-$(XSM_ENABLE)    += -DXSM_ENABLE
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_ENABLE -DXSM_MAGIC=0xf97cff8c
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_DEVELOP -DFLASK_BOOTPARAM -DFLASK_AVC_STATS
@@ -112,12 +122,18 @@
 
 .PHONY: clean
 clean:: $(addprefix _clean_, $(subdir-all))
-	rm -f *.o *~ core
+	rm -f *.o *.gcno *~ core
 _clean_%/: FORCE
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C $* clean
 
 %.o: %.c $(HDRS) Makefile
+ifeq ($(gcov),y)
+	$(CC) $(CFLAGS) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+else
 	$(CC) $(CFLAGS) -c $< -o $@
+endif
+
 
 %.o: %.S $(AHDRS) Makefile
 	$(CC) $(AFLAGS) -c $< -o $@
diff -r d89eaa22b5b9 xen/arch/x86/mm/hap/Makefile
--- a/xen/arch/x86/mm/hap/Makefile	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/arch/x86/mm/hap/Makefile	Mon Apr 06 18:57:59 2009 +0530
@@ -4,8 +4,22 @@
 obj-y += guest_walk_4level.o
 obj-y += p2m-ept.o
 
+LN = /bin/ln
+
 guest_levels  = $(subst level,,$(filter %level,$(subst ., ,$(subst _, ,$(1)))))
 guest_walk_defns = -DGUEST_PAGING_LEVELS=$(call guest_levels,$(1))
 
+num = $(call guest_levels,$(@F))level.c
+
 guest_walk_%level.o: guest_walk.c $(HDRS) Makefile
+ifeq ($(gcov),y)
+	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s guest_walk.c guest_walk_$(num)
+else
 	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c $< -o $@
+endif
+	
+.PHONY: clean
+clean::
+	rm -f guest_walk_*.c
diff -r d89eaa22b5b9 xen/arch/x86/mm/shadow/Makefile
--- a/xen/arch/x86/mm/shadow/Makefile	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/arch/x86/mm/shadow/Makefile	Mon Apr 06 18:57:59 2009 +0530
@@ -1,5 +1,18 @@
 obj-$(x86_32) += common.o guest_2.o guest_3.o
 obj-$(x86_64) += common.o guest_2.o guest_3.o guest_4.o
 
+LN = /bin/ln
+num=$*.c
+
 guest_%.o: multi.c $(HDRS) Makefile
+ifeq ($(gcov),y)
+	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s multi.c guest_$(num)
+else
 	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
+endif
+
+.PHONY: clean
+clean::
+	rm -f guest_*.c
diff -r d89eaa22b5b9 xen/arch/x86/x86_32/entry.S
--- a/xen/arch/x86/x86_32/entry.S	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/arch/x86/x86_32/entry.S	Mon Apr 06 18:57:59 2009 +0530
@@ -703,6 +703,9 @@
         .long do_sysctl             /* 35 */
         .long do_domctl
         .long do_kexec_op
+#ifdef CONFIG_XEN_GCOV
+        .long do_gcovprof_op
+#endif
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/4)
         .long do_ni_hypercall
         .endr
@@ -750,6 +753,9 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec_op          */
+#ifdef CONFIG_XEN_GCOV
+        .byte 3 /* do_gcovprof_op	*/
+#endif
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
         .endr
diff -r d89eaa22b5b9 xen/arch/x86/x86_64/entry.S
--- a/xen/arch/x86/x86_64/entry.S	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/arch/x86/x86_64/entry.S	Mon Apr 06 18:57:59 2009 +0530
@@ -692,6 +692,9 @@
         .quad do_sysctl             /* 35 */
         .quad do_domctl
         .quad do_kexec_op
+#ifdef CONFIG_XEN_GCOV
+        .quad do_gcovprof_op
+#endif
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
         .quad do_ni_hypercall
         .endr
@@ -739,6 +742,9 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec             */
+#ifdef CONFIG_XEN_GCOV
+        .byte 3 /* do_gcovprof_op       */
+#endif
         .byte 1 /* do_xsm_op            */
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
diff -r d89eaa22b5b9 xen/common/Makefile
--- a/xen/common/Makefile	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/common/Makefile	Mon Apr 06 18:57:59 2009 +0530
@@ -28,6 +28,7 @@
 obj-y += xmalloc.o
 obj-y += rcupdate.o
 
+obj-$(gcov)        += xen-gcov-core.o
 obj-$(perfc)       += perfc.o
 obj-$(crash_debug) += gdbstub.o
 obj-$(xenoprof)    += xenoprof.o
@@ -42,6 +43,8 @@
 
 subdir-y += libelf
 
+CFLAGS += -DSRC_PATH='"$(BASEDIR)"'
+
 # Object file contains changeset and compiler information.
 version.o: $(BASEDIR)/include/xen/compile.h
 
diff -r d89eaa22b5b9 xen/common/xen-gcov-core.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/common/xen-gcov-core.c	Mon Apr 06 18:57:59 2009 +0530
@@ -0,0 +1,255 @@
+/**
+ *  xen-gcov-core.c: arch dependent code for hypervisor profiling
+ *
+ *  Written by tej parkash and team(tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <xen/init.h>
+#include <xen/kernel.h>
+#include <xen/guest_access.h>
+#include <xen/sched.h>
+#include <xen/event.h>
+#include <xen/string.h>
+#include <public/xen-gcov.h>
+#include <xen/paging.h>
+#include <xsm/xsm.h>
+#include <xen/config.h>
+#include <xen/lib.h>
+
+
+#define XEN_GCOV_CORE    "xen-gcov-core:"
+
+static int num_xen_files;
+gcov_unsigned_t gcov_version = 0;
+struct gcov_info *bb_head;
+
+struct ctor_list 
+{
+    unsigned long num;
+    ctor_t ctor[];
+};
+extern struct ctor_list __CTOR_LIST__;
+
+static inline int
+counter_active(struct gcov_info *bb, unsigned int type)
+{
+    return (1 << type) & bb->ctr_mask;
+}
+
+static inline unsigned int
+get_fn_stride(struct gcov_info *bb)
+{
+    unsigned int stride;
+
+    stride = sizeof(struct gcov_fn_info) + sizeof(unsigned int);
+    if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int)) 
+    {
+        stride += __alignof__(struct gcov_fn_info) - 1;
+        stride &= ~(__alignof__(struct gcov_fn_info) - 1);
+    }
+    return stride;
+}
+
+static inline struct gcov_fn_info *
+get_fn_info(struct gcov_info *bb, unsigned int func)
+{
+    return (struct gcov_fn_info *)
+        ((char *)bb->functions + func * get_fn_stride(bb));
+}
+
+static void reset_gcda(struct gcov_info *bb)
+{
+
+    unsigned int i;
+    struct gcov_ctr_info *ctr;
+    ctr = bb->counts;
+    for (i=0; i < GCOV_COUNTERS; i++)
+        if (counter_active(bb, i)) 
+        {
+            memset(ctr->values, 0, ctr->num * sizeof(gcov_type));
+            ctr++;
+        }
+}
+
+/* Function to reset the bb counter. */
+static int reset_bb(XEN_GUEST_HANDLE(void) arg, int val)
+{
+    struct gcov_info *tmp=bb_head;
+    char bb_file[100];
+    if ( val == 0)
+    {
+        /* reset single data file */
+        if ( copy_from_guest(&bb_file, arg, 1) )
+            return -EFAULT;
+        for(;tmp!=NULL;tmp=tmp->next)
+        {
+            if(!strcmp(tmp->filename,bb_file))
+            {
+                reset_gcda(tmp);
+                break;
+            }
+            else
+                continue;
+        }
+    }
+    else 
+    {
+        /* reset all data files */
+        for(;tmp!=NULL;tmp=tmp->next)
+            reset_gcda(tmp);
+    }
+    return 0;
+}
+
+/*copy bb_head info */
+static int start_prof(XEN_GUEST_HANDLE(void) arg, int val)
+{
+    /*Add the code to copy the no of files to be add to xen */
+    struct xen_gcov_info bb_tmp;
+    struct gcov_info *tmp = bb_head;
+    struct gcov_fn_info *func;
+    struct gcov_ctr_info *ctr;
+    struct xen_gcov_ctr_info  *xctr;
+    struct xen_gcov_info *bb = &bb_tmp;
+    int i,j;
+
+    if ( copy_from_guest(&bb_tmp, arg, 1) )
+        return -EFAULT;
+    for(;tmp!=NULL&&bb!=NULL;tmp=tmp->next)
+    {
+        ctr = tmp->counts;
+        xctr = bb->counts;	
+        memcpy(bb->counts[0].values, ctr->values, ctr->num*sizeof(gcov_type));
+        xctr->num = ctr->num;
+        xctr->merge = ctr->merge;
+        bb->version  = tmp->version;
+        bb->stamp    = tmp->stamp;
+        safe_strcpy(bb->file, tmp->filename);
+        bb->ctr_mask    = tmp->ctr_mask;
+        bb->n_functions = tmp->n_functions;
+        for(i=0;i<tmp->n_functions;i++) 
+        {
+            func = get_fn_info(tmp,i);
+            bb->functions[i].ident=func->ident;
+            bb->functions[i].checksum=func->checksum;
+            for (j=0;j<GCOV_COUNTERS;j++) 
+            {
+                bb->functions[i].n_ctrs[j]=func->n_ctrs[j];
+            }
+        }
+        /*increment the bb_head pointers*/
+        bb=bb->next;
+    }
+    if ( copy_to_guest(arg, &bb_tmp, 1) )
+        return -EFAULT;
+    return 0;
+}
+
+static int var_info(XEN_GUEST_HANDLE(void) arg,int val)
+{
+    struct gcov_variables gcov_var;
+    struct gcov_variables *tmp=&gcov_var;
+    struct gcov_info *bb;
+    int i;
+    if( copy_from_guest(&gcov_var, arg, 1) )
+        return -EFAULT;
+    if ( val == 0 ) 
+    {
+        safe_strcpy(tmp->src_path,SRC_PATH);	
+        tmp->g_version=gcov_version;
+        tmp->n_files=num_xen_files;
+    } 
+    else 
+    {
+        for(i=0,bb=bb_head;bb!=NULL;bb=bb->next,i++)
+        {
+            tmp->ctr_num[i]=bb->counts->num;
+            tmp->n_funcs[i]=bb->n_functions;
+       }
+    }
+
+    if( copy_to_guest(arg, &gcov_var, 1) )
+        return -EFAULT;
+    return 0;
+}
+
+/*internal hyercall functions*/
+int do_gcovprof_op(int op, XEN_GUEST_HANDLE(void) arg, int val)
+{
+    int ret = 0;
+    if ((op < 0) || (op > GCOVPROF_last_op)) 
+    {
+        printk(KERN_ERR XEN_GCOV_CORE "Invalid operation %d\n", op);
+        return -EINVAL;
+    }
+    switch (op) 
+    {
+        case GCOVPROF_seek_vars:
+            /*fun seeks vars to be used by proc code */
+            ret = var_info(arg, val);
+            break;
+        case GCOVPROF_start:
+            /*update linux kernel with profiling info */
+            ret = start_prof(arg,val);
+            break;
+        case GCOVPROF_reset:
+            /*reset the gcda or vmlinux*/
+            ret = reset_bb(arg,val);
+            break;
+        default:
+            ret = -ENOSYS;
+    }
+    return ret;
+}
+
+void do_global_ctors(ctor_t ctor[], unsigned long num)
+{
+    unsigned long i;
+
+    /* Call constructors and create gcov_info linked list*/
+    for (i = 0; i < num && ctor[i]; i++)
+        ctor[i] ();
+    num_xen_files = i-1;
+}
+
+/*Register supplied struct BB. Called by each object code constructor. */
+void __gcov_init(struct gcov_info *bb)
+{
+    if (!bb->version)
+        return;
+    /*Check for compatible gcc version */
+    if (gcov_version == 0)
+        gcov_version = bb->version;
+    else if (bb->version != gcov_version) 
+    {
+        printk(KERN_WARNING XEN_GCOV_CORE "gcc version mismatch in "
+                  "file '%s'!\n", bb->filename);
+        return;
+    }
+    /*Set up linked list */
+    bb->version = 0;
+    bb->next = bb_head;
+    bb_head = bb;
+}
+
+static int __init gcov_init(void)
+{
+    do_global_ctors(__CTOR_LIST__.ctor, __CTOR_LIST__.num);
+    printk(KERN_INFO XEN_GCOV_CORE "init done\n");
+    return 0;
+}
+
+/* Unused functions needed to prevent linker errors. */
+void __gcov_flush(void) {}
+void __gcov_merge_add(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_single(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_delta(gcov_type * counters, unsigned int n_counters){}
+
+EXPORT_SYMBOL(__gcov_init);
+EXPORT_SYMBOL(do_global_ctors);
+EXPORT_SYMBOL(__gcov_flush);
+EXPORT_SYMBOL(__gcov_merge_add);
+EXPORT_SYMBOL(__gcov_merge_single);
+EXPORT_SYMBOL(__gcov_merge_delta);
+__initcall(gcov_init);
diff -r d89eaa22b5b9 xen/include/public/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/public/xen-gcov.h	Mon Apr 06 18:57:59 2009 +0530
@@ -0,0 +1,139 @@
+/******************************************************************************
+ * xen-gcov.h
+ * 
+ * Interface for enabling and populating hypervisor profiling info.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: tej parkash <tej.parkash@hcl.in>
+ * Written by tej parkash and team
+ *
+ */
+/********************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_seek_vars          0
+#define GCOVPROF_start              1
+#define GCOVPROF_reset              2
+#define GCOVPROF_last_op            3
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS               5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*ctor_t)(void);
+
+struct gcov_fn_info{
+    gcov_unsigned_t ident;    /* unique ident of function */
+    gcov_unsigned_t checksum; /* function checksum */
+    unsigned n_ctrs[0];       /* instrumented counters */
+};
+
+struct gcov_ctr_info
+{
+    gcov_unsigned_t num;      /* number of counters.  */
+    gcov_type *values;        /* their values.  */
+    gcov_merge_fn merge;      /* The function used to merge them.  */
+};
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+    gcov_unsigned_t version;  /* expected version number */
+    struct gcov_info *next;   /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    /* uniquifying time stamp */
+    const char *filename;
+
+    unsigned n_functions;     /* number of functions */
+    struct gcov_fn_info *functions; /* table of functions */
+
+    unsigned ctr_mask;        /* mask of counters instrumented.  */
+    struct gcov_ctr_info counts[0]; /* count data. The number of bits
+                     set in the ctr_mask field
+                     determines how big this array
+                     is.  */
+};
+
+
+struct xen_gcov_fn_info{
+    gcov_unsigned_t ident;              /* unique ident of function */
+    gcov_unsigned_t checksum;           /* function checksum */
+    unsigned int n_ctrs[GCOV_COUNTERS]; /* instrumented counters */
+};
+typedef struct xen_gcov_fn_info xen_gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_fn_info_t);
+
+
+
+struct xen_gcov_ctr_info
+{
+    gcov_unsigned_t num;      /* number of counters.  */
+    gcov_type *values;        /* their values.  */
+    gcov_merge_fn merge;      /* The function used to merge them.  */
+};
+typedef struct  xen_gcov_ctr_info xen_gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct xen_gcov_info
+{
+    gcov_unsigned_t version;            /* expected version number */
+    struct xen_gcov_info *next;         /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;              /* uniquifying time stamp */
+    char file[100];
+
+    unsigned int n_functions;           /* number of functions */
+    struct xen_gcov_fn_info *functions; /* table of functions */
+
+    unsigned int ctr_mask;              /* mask of counters instrumented.  */
+    struct xen_gcov_ctr_info counts[GCOV_COUNTERS]; /* count data.*/
+};
+typedef struct xen_gcov_info xen_gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(xen_gcov_info_t);
+
+struct gcov_variables
+{
+    char src_path[100];
+    unsigned long g_version;
+    unsigned int n_files;
+    gcov_unsigned_t *ctr_num;
+    unsigned int *n_funcs;     
+};
+typedef struct gcov_variables gcov_variables_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_variables_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r d89eaa22b5b9 xen/include/xen/config.h
--- a/xen/include/xen/config.h	Mon Apr 06 18:19:39 2009 +0530
+++ b/xen/include/xen/config.h	Mon Apr 06 18:57:59 2009 +0530
@@ -96,4 +96,22 @@
 #define __cpuinitdata
 #define __cpuinit
 
+#ifdef CONFIG_XEN_GCOV
+#define SORT(x)        x
+#define CONSTRUCTORS                           \
+       __CTOR_LIST__ = .;                  \
+       LONG((__CTOR_END__ - __CTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       __CTOR_LIST2__ = .;                 \
+       *(SORT(.ctors))                     \
+       LONG(0)                         \
+       __CTOR_END__ = .;                   \
+       __DTOR_LIST__ = .;                  \
+       LONG((__DTOR_END__ - __DTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       *(SORT(.dtors))                     \
+       LONG(0)                         \
+       __DTOR_END__ = .;
+
+#endif /* CONFIG_XEN_GCOV */
 #endif /* __XEN_CONFIG_H__ */

[-- Attachment #4: gcov-readme --]
[-- Type: application/octet-stream, Size: 1088 bytes --]


---------------------------------------------------------------
- README file for enabling gcov by command line option
- Command Line Option is gcov=y
---------------------------------------------------------------

Steps to follow 
---------------

1. Compile the xen, for more information please see "README" of xen. 
2. If user needs Gcov to be enabled for xen hypervisor
   Compile the xen along with command line option "gcov=y".
   But make sure to run "make Clean" before enabling 
   gcov.
   	# make clean
        # make KERNELS=linux-2.6-xen0 gcov=y
	# make install KERNELS=linux-2.6-xen0 gcov=y
		or
	# make world KERNELS=linux-2.6-xen0 gcov=y
	# make install KERNELS=linux-2.6-xen0 gcov=y

3. User can now insert the module "xen-gcov-proc" and work on.
4. Again if user wants to disable the gcov from xen, 
   make sure to run "make Clean" then compile the xen without
   command line option.
	# make clean
	# make KERNELS=linux-2.6-xen0
        # make install KERNELS=linux-2.6-xen0
       
		or
	# make world KERNELS=linux-2.6-xen0
	# make install KERNELS=linux-2.6-xen0
	

[-- Attachment #5: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor)
  2009-03-26 10:57       ` Gianluca Guida
  2009-03-26 15:11         ` Tej
  2009-04-06 15:04         ` Tej
@ 2009-04-29 14:56         ` Tej
  2 siblings, 0 replies; 8+ messages in thread
From: Tej @ 2009-04-29 14:56 UTC (permalink / raw)
  To: Gianluca Guida; +Cc: George Dunlap, xen-devel, Keir Fraser

[-- Attachment #1: Type: text/plain, Size: 2893 bytes --]

On Thu, Mar 26, 2009 at 4:27 PM, Gianluca Guida
<Gianluca.Guida@eu.citrix.com> wrote:
> Hi,
>
> Sorry for the late reply.
>
> On Mar 26, 2009, at 7:59 AM, Tej wrote:
>
>> On Mon, Mar 2, 2009 at 9:36 PM, Tej <bewith.tej@gmail.com> wrote:
>>>
>>> On Sat, Feb 28, 2009 at 12:32 AM, Gianluca Guida
>>> <gianluca.guida@eu.citrix.com> wrote:
>>>>
>>>> While I still need to test the patch (building 3.3 right now) and to
>>>> understand gcov internals, I think that a few comments can be done,
>>>> mostly
>>>> aestethicals.
>>>>
>>>> - xen coding style: Using four-spaces tabs is generally the tradition.
>>>> Also
>>>> I do prefer to have brackets that start code blocks on a new line
>>>> aligned to
>>>> the previous line, but that's not followed everywhere in the code.
>>>>
>>>> - Makefiles: while the num=$*.c is still a mystery to me, my question
>>>> is: do
>>>> you really need to make links with different names to files compiled
>>>> multiple times? If so, it would be useful to remove them on 'make
>>>> clean'.
>>>> Also, it would be useful to make this feature enabled with a
>>>> compile-time
>>>> option.
>>>
>>> Attached patches address the four-spaces tabs and make clean issues.
>>> Hope the code is more clean and readable
>>
>> Hi
>>
>> Sorry for interrupting you guys from your busy schedule, as i just
>> want to know the current status for GCOV patches i submitted early
>> this month.
>> any feedback on "testing/coding/design/feature" will be appreciated.
>
> I've been testing it for a while, and it seems an useful and interesting
> feature.
>
> A few comments, though:
>
> - As I said, while the patch is minimal enough, the feature requires some
> intrusive changes in Xen, like changing the linker script (via the
> definition of CONSTRUCTORS) and in general it changes the compiler behavior,
> so I think it should be mandatory to make this feature explicitely enabled
> at compile time, e.g.  gcov=y.
> - I am talking about Xen code only, but I think a few part of the codes
> should be more clear. I find very confusing the difference between
> xen_gcov_info and gcov_info, and all the instructions that uses them. A
> better naming scheme for the variables and more comments would be
> appreciated.

Removed xen_gcov_info dependency all together. Complete implementation
is based on gcc "gcov_info" struct only.
We have also included some meaningful names and comments. I hope that
these changes makes code more clear/clean and readable.

I have tested code in amd-64 with gcov=y and gcov=n, all worked fine,

Are you looking for any other other specific feature to be included in this?
We are missing only modules implementation of gcov which is not
required for xen hypervisor.

Any further comments/suggestion are more than welcome.

thanks
-tej & team


>
> Thanks!
> Gianluca
>

[-- Attachment #2: linux-2.6.18-gcov-v5.patch --]
[-- Type: text/x-patch, Size: 44103 bytes --]

diff -r f85259abcddf drivers/xen/Kconfig
--- a/drivers/xen/Kconfig	Tue Feb 05 10:05:19 2008 +0000
+++ b/drivers/xen/Kconfig	Wed Apr 29 08:59:59 2009 +0530
@@ -231,6 +231,12 @@
 	help
 	  Xen hypervisor attributes will show up under /sys/hypervisor/.
 
+config XEN_GCOV_PROC
+	tristate "Code covarage for hypervisor"
+	help
+	  Select to profile Xen Hypervisor only. Create /proc/xen/gcov entry
+	  which provides access to the current code coverage state of xen hypervisor
+
 choice
 	prompt "Xen version compatibility"
 	default XEN_COMPAT_030002_AND_LATER
diff -r f85259abcddf drivers/xen/Makefile
--- a/drivers/xen/Makefile	Tue Feb 05 10:05:19 2008 +0000
+++ b/drivers/xen/Makefile	Wed Apr 29 08:59:59 2009 +0530
@@ -3,6 +3,7 @@
 obj-y	+= evtchn/
 obj-y	+= xenbus/
 obj-y	+= char/
+obj-y	+= gcov/
 
 obj-y	+= util.o
 obj-$(CONFIG_XEN_BALLOON)		+= balloon/
diff -r f85259abcddf drivers/xen/core/xen_proc.c
--- a/drivers/xen/core/xen_proc.c	Tue Feb 05 10:05:19 2008 +0000
+++ b/drivers/xen/core/xen_proc.c	Wed Apr 29 08:59:59 2009 +0530
@@ -3,7 +3,7 @@
 #include <linux/proc_fs.h>
 #include <xen/xen_proc.h>
 
-static struct proc_dir_entry *xen_base;
+struct proc_dir_entry *xen_base;
 
 struct proc_dir_entry *create_xen_proc_entry(const char *name, mode_t mode)
 {
@@ -20,4 +20,5 @@
 	remove_proc_entry(name, xen_base);
 }
 
+EXPORT_SYMBOL(xen_base);
 EXPORT_SYMBOL_GPL(remove_xen_proc_entry); 
diff -r f85259abcddf drivers/xen/gcov/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/Makefile	Wed Apr 29 08:59:59 2009 +0530
@@ -0,0 +1,1 @@
+obj-$(CONFIG_XEN_GCOV_PROC) := xen-gcov-proc.o
diff -r f85259abcddf drivers/xen/gcov/xen-gcov-proc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/gcov/xen-gcov-proc.c	Wed Apr 29 08:59:59 2009 +0530
@@ -0,0 +1,1235 @@
+/**
+ *  @file xen-gcov-proc.c
+ *
+ *  @Author: Hubertus Franke <frankeh@us.ibm.com>
+ *           Rajan Ravindran <rajancr@us.ibm.com>
+ *           Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
+ *           Paul Larson
+ *
+ *  Modified by tej parkash and team for Xen (tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/xen-gcov.h>
+#include <xen/evtchn.h>
+#include <xen/xen_proc.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+
+MODULE_LICENSE("GPL");
+
+#define GCOV_PROC_HEADER    "xen-gcov-proc: "
+#define GCOV_PROC_ROOT        "gcov"
+#define GCOV_PROC_VMLINUX    "vmlinux"
+#define PAD8(x)            (((x) + 7) & ~7)
+
+#define FILE_LENGTH  	100
+
+/*
+ * If set to non-zero, create links to additional files(.c and .gcno) in proc 
+ * filesystem entries. 
+ */
+static int xen_gcov_link = 1;
+
+/* Count update frequency */
+static int xen_gcov_update = 5;
+
+#ifdef MODULE
+/* Parameter handling. */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+/* module_param is a 2.6 thing (though there are compat macros since 2.4.25) */
+MODULE_PARM(xen_gcov_link, "i");
+MODULE_PARM(xen_gcov_update, "i");
+#else
+module_param(xen_gcov_link, int, 0400);
+module_param(xen_gcov_update, int, 0400);
+#endif                /* LINUX_VERSION_CODE */
+
+MODULE_PARM_DESC(xen_gcov_link, "If set to non-zero, create links to additional "
+         "files in proc filesystem entries");
+MODULE_PARM_DESC(xen_gcov_update, "decides the frequency of hypervisor count"
+         "update and default value is 5sec.");
+#else                /* MODULE */
+/* Called when 'xen_gcov_link' is specified on the kernel command line. */
+static int __init xen_gcov_link_setup(char *str)
+{
+    xen_gcov_link = (int)simple_strtol(str, NULL, 10);
+    return 1;
+}
+
+__setup("xen_gcov_link=", xen_gcov_link_setup);
+
+static int __init xen_gcov_update_setup(char *str)
+{
+    xen_gcov_update = (int)simple_strtol(str, NULL, 10);
+    return 1;
+}
+
+__setup("xen_gcov_update=", xen_gcov_update_setup);
+
+#endif                /* MODULE */
+
+/*
+ *  create sysclt entry @/proc/sys/xen 
+ * accomplish the count update frequency
+ * perform: echo <num> >/proc/sys/xen/xen_gcov_update
+ */
+#ifdef CONFIG_SYSCTL
+static ctl_table gcov_table[] = {
+    {
+        .ctl_name     = 4,
+    .procname     = "xen_gcov_update",
+    .data         = &xen_gcov_update,
+    .maxlen     = sizeof(xen_gcov_update),
+    .mode         = 0644,
+    .proc_handler     = proc_dointvec,
+    }
+    ,
+    {.ctl_name = 0}
+};
+
+static ctl_table gcov_root_table[] = {
+    {
+        .ctl_name     = 123,
+        .procname     = "xen",
+        .mode         = 0555,
+        .child        = gcov_table},
+    {.ctl_name = 0}
+};
+
+/*sysclt table specific declaration*/
+static struct ctl_table_header *gcov_table_header;
+
+#endif /* CONFIG_SYSCTL */
+
+/* Protect global variables from concurrent access. */
+static DEFINE_MUTEX(read_mutex);
+static DEFINE_MUTEX(write_mutex);
+
+/* workqueue function */
+static void xeno_prof(void *);
+
+/* workqueue for the calling hypervisor count head */
+static DECLARE_WORK(work, xeno_prof, NULL);
+
+/* Filesystem entry for /proc/gcov/vmlinux */
+static struct proc_dir_entry *proc_vmlinux = NULL;
+
+/* First leaf node. */
+static struct gcov_ftree_node *leaf_nodes = NULL;
+
+/* Cached node used to speed up sequential reads in /proc/vmlinux. */
+static struct gcov_ftree_node *cached_node = NULL;
+
+/* Root node for internal data tree. */
+static struct gcov_ftree_node tree_root;
+
+struct node_info *node_info = NULL;
+
+/* Filename extension for data files. */
+static const char *da_ending = "gcda";
+
+/* Array of filename endings to use when creating links. */
+static const char *endings[] = { "gcno", "c" };
+
+/* Info which keeps no if file in xen info to be profile */
+unsigned int xen_file_nos;
+
+/* head of xen-gcov-info linked list */
+struct gcov_info *list_head = NULL;
+
+/* xen_sourcepath length */
+static int sourcepath_len;
+
+/* gcov version no */
+unsigned long xen_gcov_version;
+
+
+/* Determine whether counter TYPE is active in BB. */
+static inline int
+counter_active(struct gcov_info *bb, unsigned int type)
+{
+    return (1 << type) & bb->ctr_mask;
+}
+
+/* Return the number of active counter types for BB. */
+static inline unsigned int
+num_counter_active(struct gcov_info *bb)
+{
+    unsigned int i;
+    unsigned int result;
+
+    result = 0;
+    for (i=0; i < GCOV_COUNTERS; i++)
+        if (counter_active(bb, i))
+            result++;
+    return result;
+}
+
+/*
+ * Get number of bytes used for one entry in the gcov_fn_info array pointed to
+ * by BB->functions.
+ */
+static inline unsigned int
+get_fn_stride(struct gcov_info *bb)
+{
+    unsigned int stride;
+
+    stride = sizeof(struct gcov_fn_info) + num_counter_active(bb) *
+             sizeof(unsigned int);
+    if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int))
+    {
+        stride += __alignof__(struct gcov_fn_info) - 1;
+        stride &= ~(__alignof__(struct gcov_fn_info) - 1);
+    }
+    return stride;
+}
+
+/* Get the address of gcov_fn_info for function FUNC of BB. */
+static inline struct gcov_fn_info *
+get_fn_info(struct gcov_info *bb, unsigned int func)
+{
+    return (struct gcov_fn_info *)
+        ((char *)bb->functions + func * get_fn_stride(bb));
+}
+
+/*
+ * Create xen_gcov_info linked list by creating nodes for all gcda files
+ * based on n_files value 
+ */
+static inline int create_bb_list(void)
+{
+    struct gcov_info *node = NULL, *tmp_node = NULL;
+    int i, j, len=0, active;
+    
+    for (i = 0; i < node_info->n_files; i++) 
+    {
+        /* Allocate memory for struct gcov_info */
+        active = node_info->active_counters[i];   
+        len = sizeof(struct gcov_info) + sizeof(struct gcov_ctr_info)*active;
+        node = (struct gcov_info *) kmalloc(len, GFP_KERNEL);
+        for (j=0; j < active; j++) {
+	    len =sizeof(gcov_type) * node_info->ctr_num[i][j];
+            node->counts[j].values = kmalloc(len, GFP_KERNEL);
+        }
+
+        /* Allocate memory for array of struct gcov_fn_info */
+        len = (sizeof(struct gcov_fn_info) + active*sizeof(unsigned int)) * 
+               node_info->n_funcs[i];
+        node->functions = (struct gcov_fn_info *) kmalloc(len,GFP_KERNEL);
+        node->filename   = (char *) kmalloc(FILE_SIZE*sizeof(char),GFP_KERNEL);
+        node->next = NULL;
+
+        if (list_head == NULL) 
+        {
+            list_head = node;
+            tmp_node = node;
+        } 
+        else 
+        {
+            tmp_node->next = node;
+            tmp_node = node;
+        }
+    }
+    return 0;
+}
+
+/* free memory allocated for gcov_info list */
+static inline int free_bb_list(void)
+{
+    struct gcov_info *node = NULL;
+    struct gcov_info *temp = list_head;
+    int active,i;
+
+    for (;temp!=NULL;temp = temp->next) 
+    {
+        node = temp;
+        active=num_counter_active(node);
+        for(i=0; i < active; i++) 
+            kfree(node->counts[i].values);
+        kfree(node->functions);
+        kfree(node);
+    }
+
+    return 0;
+}
+
+/*
+ * 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 (adapted from gcc/gcov-io.h).
+ */
+static int store_gcov_type(gcov_type value, char *dest, size_t bytes)
+{
+    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;
+
+    return 0;
+}
+
+/* Return size of .gcda counter section. */
+static size_t
+sizeof_counter_data(struct gcov_info *bb, unsigned int row,
+            unsigned int col)
+{
+
+    struct gcov_fn_info *func = get_fn_info(bb,row);
+
+    if (counter_active(bb, col)) 
+    {
+        return /* tag */ 4 + /* length */ 4 +
+            /* counters */ func->n_ctrs[col] * 8;
+    } 
+    else
+        return 0;
+}
+
+/* Return size of .gcda data section associated with FUNC.  */
+static size_t sizeof_func_data(struct gcov_info *bb, unsigned int row)
+{
+    size_t result;
+    unsigned int type;
+
+    result =
+        /* tag */ 4 + /* length */ 4 + /* ident */ 4 + /* checksum */ 4;
+    for (type = 0; type < GCOV_COUNTERS; type++)
+        result += sizeof_counter_data(bb, row, type);
+    return result;
+}
+
+/* Get size of .gcda file associated with BB. */
+static size_t sizeof_da_file(struct gcov_info *bb)
+{
+    size_t result;
+    unsigned int i;
+
+    result = /* magic */ 4 + /* version */ 4 + /* stamp */ 4;
+    for (i = 0; i < bb->n_functions; i++)
+        result += sizeof_func_data(bb, i);
+    return result;
+}
+
+/*
+ * Store a 32 bit unsigned integer value in GCOV format to memory at address
+ * BUF.
+ */
+static void store_int32(uint32_t i, char *buf)
+{
+    uint32_t *p;
+    p = (int *)buf;
+    *p = i;
+
+}
+
+/*
+ * Store a 64 bit unsigned integer value in GCOV format to memory at address
+ * BUF.
+ */
+static void store_int64(uint64_t i, char *buf)
+{
+    store_int32((uint32_t) (i & 0xffff), buf);
+    store_int32((uint32_t) (i >> 32), buf + 4);
+}
+
+/*
+ * Store a gcov counter in GCOV format to memory at address BUF. The counter is
+ * identified by BB, FUNC, TYPE and COUNTER.
+ */
+static void
+store_counter(struct gcov_info *bb, unsigned int func, unsigned int type,
+          unsigned int counter, char *buf)
+{
+    unsigned int counter_off;
+    unsigned int type_off;
+    unsigned int i;
+    struct gcov_fn_info *func_ptr;
+    /* Get offset into counts array */
+    type_off = 0;
+    for (i = 0; i < type; i++)
+        if (counter_active(bb, i))
+        type_off++;
+    /* Get offset into values array. */
+    counter_off = counter;
+    for (i = 0; i < func; i++) 
+    {
+        func_ptr = get_fn_info(bb,i);
+        counter_off += func_ptr->n_ctrs[type];
+    }
+    /* Create in temporary storage */
+    store_int64(bb->counts[type_off].values[counter_off], buf);
+}
+
+/*
+ * Store a counter section in userspace memory. The counter section is
+ * identified by BB, FUNC and TYPE. The destination address is BUF. Store at
+ * most COUNT bytes beginning at OFFSET. Return the number of bytes stored or a
+ * negative value on error.
+ */
+static ssize_t
+store_counter_data(struct gcov_info *bb, unsigned int func,
+           unsigned int type, char *buf, size_t count, loff_t offset)
+{
+    struct gcov_fn_info *func_ptr;
+    char data[8]; 
+    char *from;
+    size_t len;
+    ssize_t result;
+    unsigned int i;
+
+    func_ptr = get_fn_info(bb,func);
+    result = 0;
+    while (count > 0) 
+    {
+        if (offset < 4) 
+        {
+            /* Tag ID */
+            store_int32((uint32_t) GCOV_TAG_FOR_COUNTER(type),
+               data);
+            len = 4 - offset;
+            from = data + offset;
+        } 
+        else if (offset < 8) 
+        {
+            /* Tag length in groups of 4 bytes */
+            store_int32((uint32_t)
+            func_ptr->n_ctrs[type] * 2, data);
+            len = 4 - (offset - 4);
+            from = data + (offset - 4);
+        } 
+        else 
+        {
+            /* Actual counter data */
+            i = (offset - 8) / 8;
+            /* Check for EOF */
+            if (i >= func_ptr->n_ctrs[type])
+                break;
+            store_counter(bb, func, type, i, data);
+            len = 8 - (offset - 8) % 8;
+            from = data + (offset - 8) % 8;
+        }
+        if (len > count)
+        len = count;
+        if (copy_to_user(buf, from, len))
+        return -EFAULT;
+        count -= len;
+        buf += len;
+        offset += len;
+        result += len;
+    }
+
+    return result;
+}
+
+/* 
+ * Store a function section and associated counter sections in userspace memory.
+ * The function section is identified by BB and FUNC. The destination address is
+ * BUF. Store at most COUNT bytes beginning at OFFSET. Return the number of
+ * bytes stored or a negative value on error.
+ */
+static ssize_t
+store_func_data(struct gcov_info *bb, unsigned int func, char *buf,
+        size_t count, loff_t offset)
+{
+    struct gcov_fn_info *func_ptr;
+    char data[4];
+    char *from;
+    size_t len;
+    unsigned int i;
+    loff_t off;
+    size_t size;
+    ssize_t result;
+    ssize_t rc;
+
+    func_ptr = get_fn_info(bb, func); 
+    result = 0;
+    while (count > 0) 
+    {
+        if (offset < 16) 
+        {
+            if (offset < 4) 
+            {
+                /* Tag ID */
+                store_int32((uint32_t) GCOV_TAG_FUNCTION, data);
+                len = 4 - offset;
+                from = data + offset;
+            } 
+            else if (offset < 8) 
+            {
+                /* Tag length */
+                store_int32(2, data);
+                len = 4 - (offset - 4);
+                from = data + (offset - 4);
+            } 
+            else if (offset < 12) 
+            {
+                /* Function ident */
+                store_int32((uint32_t) func_ptr->ident ,data);
+              
+                len = 4 - (offset - 8);
+                from = data + (offset - 8);
+            } 
+            else 
+            {
+                /* Function checksum */
+                store_int32((uint32_t) func_ptr->checksum,data);
+                len = 4 - (offset - 12);
+                from = data + (offset - 12);
+            }
+            /* Do the actual store */
+            if (len > count)
+                len = count;
+            if (copy_to_user(buf, from, len))
+                return -EFAULT;
+        }  
+        else 
+        {
+            off = 16;
+            len = 0;
+            for (i = 0; i < GCOV_COUNTERS; i++) 
+            {
+                size = sizeof_counter_data(bb, func, i);
+                if (offset < off + size) 
+                {
+                    rc = store_counter_data(bb, func, i,
+                       buf, count,
+                       offset - off);
+                       if (rc < 0)
+                           return rc;
+                       len = rc;
+                       break;
+                }
+                off += size;
+            }
+            /* Check for EOF */
+            if (i == GCOV_COUNTERS)
+                break;
+         } 
+    count -= len;
+    buf += len;
+    offset += len;
+    result += len;
+    }
+
+    return result;
+}
+
+/*
+ * Store data of .gcda file associated with NODE to userspace memory at BUF.
+ * OFFSET specifies the offset inside the .da file. COUNT is the maximum
+ * number of bytes to store. Return the number of bytes stored, zero for
+ * EOF or a negative number in case of error.
+ */
+static ssize_t
+store_da_file(struct gcov_ftree_node *node, char *buf, size_t count,
+          loff_t offset)
+{
+
+    struct gcov_info *bb;
+    char data[4];
+    char *from;
+    size_t len;
+    unsigned int i;
+    loff_t off;
+    size_t size;
+    ssize_t result;
+    ssize_t rc;
+
+    bb = node->bb;
+    result = 0;
+    while (count > 0) 
+        {
+            if (offset < 12) 
+            {
+                if (offset < 4) 
+                {
+                    /* File magic */
+                    store_int32((uint32_t) GCOV_DATA_MAGIC, data);
+                    len = 4 - offset;
+                    from = data + offset;
+                } 
+                 else if (offset < 8) 
+                {
+                    /* File format/GCC version */
+                    store_int32(xen_gcov_version, data);
+                    len = 4 - (offset - 4);
+                    from = data + (offset - 4);
+                } 
+                else 
+                {
+                    /* Time stamp */
+                    store_int32((uint32_t) bb->stamp, data);
+                    len = 4 - (offset - 8);
+                    from = data + (offset - 8);
+                }
+            /* Do the actual store */
+            if (len > count)
+                len = count;
+            if (copy_to_user(buf, from, len))
+                return -EFAULT;
+            } 
+            else 
+            {
+                off = 12;
+                len = 0;
+                for (i = 0; i < bb->n_functions; i++) 
+                {
+
+                    size = sizeof_func_data(bb, i);
+                    if (offset < off + size) 
+                    {
+                        rc = store_func_data(bb, i, buf, count,
+                            offset - off);
+
+                        if (rc < 0)
+                            return rc;
+                        len = rc;
+                         break;
+                    }
+                    off += size;
+
+                }
+                /* Check for EOF */
+                if (i == bb->n_functions)
+                    break;
+            }
+            count -= len;
+            buf += len;
+            offset += len;
+            result += len;
+       }
+
+    return result;
+}
+
+/*
+ * Return size of header which precedes .da file entry associated with BB
+ * in the vmlinux file.
+ */
+static size_t sizeof_vmlinux_header(struct gcov_info *bb)
+{
+    return 8 + PAD8(strlen(bb->filename) + 1);
+}
+
+/*
+ * Store data of header which precedes .da file entry associated with NODE
+ * in the vmlinux file to userspace memory at BUF. OFFSET specifies the offset
+ * inside the header file. COUNT is the maximum number of bytes to store.
+ * Return the number of bytes stored, zero for EOF or a negative number in
+ * case of error.
+ */
+static ssize_t
+store_vmlinux_header(struct gcov_ftree_node *node, char *buf, size_t count,
+             loff_t offset)
+{
+    char data[8];
+    char *from;
+    size_t namelen;
+    ssize_t stored;
+    size_t len;
+
+    namelen = strlen(node->bb->filename);
+    stored = 0;
+    while (count > 0) 
+    {
+        if (offset < 8) 
+        {
+            /* Filename length */
+            if (store_gcov_type(PAD8(namelen + 1), data, 8))
+                return -EINVAL;
+            from = data + offset;
+            len = 8 - offset;
+        } 
+        else if (offset < 8 + namelen) 
+        {
+            /* Filename */
+            from = (char *)node->bb->filename + offset - 8;
+            len = namelen - (offset - 8);
+        } 
+        else if (offset < node->header_size) 
+        {
+            /* Nil byte padding */
+            memset(data, 0, 8);
+            from = data;
+            len = PAD8(namelen + 1) - (offset - 8);
+        }   
+        else
+            break;
+        if (len > count)
+            len = count;
+        if (copy_to_user(buf, from, len))
+            return -EFAULT;
+
+        stored += len;
+        count -= len;
+        offset += len;
+        buf += len;
+    }
+
+    return stored;
+}
+
+/* Update data related to vmlinux file. */
+static void update_vmlinux_data(void)
+{
+    struct gcov_ftree_node *node;
+    loff_t offset;
+
+    offset = 0;
+    for (node = leaf_nodes; node; node = node->next) 
+    {
+        node->offset = offset;
+        node->da_size = sizeof_da_file(node->bb);
+        node->header_size = sizeof_vmlinux_header(node->bb);
+        offset += node->header_size + node->da_size;
+    }
+    proc_vmlinux->size = offset;
+    cached_node = NULL;
+}
+
+/* Read .da or vmlinux file. */
+static ssize_t
+read_gcov(struct file *file, char *buf, size_t count, loff_t * pos)
+{
+    struct gcov_ftree_node *node;
+    struct proc_dir_entry *dir_entry;
+    ssize_t rc;
+
+    mutex_lock(&read_mutex);
+    dir_entry = PDE(file->f_dentry->d_inode);
+    rc = 0;
+    if (dir_entry == proc_vmlinux) 
+    {
+        /* Are we in a sequential read? */
+        if (cached_node && (*pos >= cached_node->offset))
+            node = cached_node;
+        else
+            node = leaf_nodes;
+            /* Find node corresponding to offset */
+        while (node && node->next && (*pos >= node->next->offset))
+            node = node->next;
+            cached_node = node;
+            if (node) 
+            {
+                if (*pos - node->offset < node->header_size)
+                    rc = store_vmlinux_header(node, buf, count,
+                    *pos - node->offset);
+                else
+                {
+                    rc = store_da_file(node, buf, count,
+                    *pos - node->offset -
+                    node->header_size);
+                }
+            }
+    } 
+    else 
+    {
+        node = (struct gcov_ftree_node *)dir_entry->data;
+        if (node) 
+        {
+            rc = store_da_file(node, buf, count, *pos);
+        }
+    }
+    if (rc > 0)
+        *pos += rc;
+    mutex_unlock(&read_mutex);
+    return rc;
+}
+
+/* Reset counters on write request. */
+static ssize_t
+write_gcov(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+    struct gcov_ftree_node *node;
+    struct proc_dir_entry *dir_entry;
+
+    mutex_lock(&write_mutex);
+    dir_entry = PDE(file->f_dentry->d_inode);
+    if (dir_entry == proc_vmlinux) 
+    {
+        /* Reset all nodes */
+        node = leaf_nodes;
+        if (HYPERVISOR_gcovprof_op(GCOVPROF_reset, NULL, !RESET_GCDA)) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                   "Failed to reset the vmlinux\n");
+            mutex_unlock(&write_mutex);
+            return EIO;
+        }
+    } 
+    else 
+    {
+        node = (struct gcov_ftree_node *)dir_entry->data;
+        if (HYPERVISOR_gcovprof_op
+            (GCOVPROF_reset, node->bb->filename, RESET_GCDA)) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                   "Failed to reset file %s\n", node->bb->filename);
+            mutex_unlock(&write_mutex);
+            return EIO;
+        }
+    }
+    mutex_unlock(&write_mutex);
+    return count;
+}
+
+/* Return a newly allocated copy of STRING. */
+static char *strdup(const char *string)
+{
+    char *result;
+
+    result = (char *)kmalloc(strlen(string) + 1, GFP_KERNEL);
+    if (result)
+        strcpy(result, string);
+    return result;
+}
+
+/* Allocate a new node and fill in NAME and BB. */
+static struct gcov_ftree_node *alloc_node(const char *name,
+                      struct gcov_info *bb)
+{
+    struct gcov_ftree_node *node;
+
+    node = (struct gcov_ftree_node *)
+        kmalloc(sizeof(struct gcov_ftree_node), GFP_KERNEL);
+    if (!node)
+        return NULL;
+    memset(node, 0, sizeof(struct gcov_ftree_node));
+    node->fname = strdup(name);
+    if (!node->fname) 
+    {
+        kfree(node);
+        return NULL;
+    }
+    node->bb = bb;
+    return node;
+}
+
+/* Free memory allocated for NODE. */
+static void free_node(struct gcov_ftree_node *node)
+{
+    if (node == &tree_root)
+        return;
+    if (node->fname)
+    kfree(node->fname);
+    kfree(node);
+}
+
+/* Remove proc filesystem entries associated with NODE. */
+static void delete_from_proc(struct gcov_ftree_node *node)
+{
+    struct proc_dir_entry *parent;
+    int i;
+
+    if (node->parent)
+        parent = node->parent->proc[0];
+    else
+        parent = xen_base;
+    for (i = 0; i < sizeof(node->proc) / sizeof(node->proc[0]); i++)
+        if (node->proc[i])
+            remove_proc_entry(node->proc[i]->name, parent);
+}
+
+/*
+ * Release all resources associated with NODE. If NODE is a directory node,
+ * also clean up all children.
+ */
+static void cleanup_node(struct gcov_ftree_node *node)
+{
+    struct gcov_ftree_node *curr;
+    struct gcov_ftree_node *next;
+    struct gcov_ftree_node *prev;
+
+    next = node;
+    do 
+    {
+        /* Depth first traversal of all children */
+        curr = next;
+        while (curr->files)
+            curr = curr->files;
+        if (curr->sibling)
+            next = curr->sibling;
+        else
+            next = curr->parent;
+        /* Remove from tree */
+        if (curr->parent) 
+        {
+            if (curr->parent->files == curr)
+                curr->parent->files = curr->sibling;
+            else 
+            {
+                for (prev = curr->parent->files;
+                    prev->sibling != curr;
+                    prev = prev->sibling) ;
+                    prev->sibling = curr->sibling;
+            }
+        }
+        /* Remove from leaf node list if necessary */
+        if (curr->bb) 
+        {
+            if (leaf_nodes == curr)
+                leaf_nodes = curr->next;
+            else 
+            {
+                for (prev = leaf_nodes;
+                     prev && (prev->next != curr);
+                     prev = prev->next) ;
+                if (prev)
+                    prev->next = curr->next;
+            }
+        }
+        /* Delete node */
+        delete_from_proc(curr);
+        free_node(curr);
+    } while (node != curr);
+}
+
+/* Clean up NODE and containing path in case it would be left empty. */
+static void cleanup_node_and_path(struct gcov_ftree_node *node)
+{
+    while (node->parent &&
+        node->parent != &tree_root && !node->parent->files->sibling)
+    node = node->parent;
+    cleanup_node(node);
+}
+
+/*
+ * Create a new directory node named NAME under PARENT. Upon success return
+ * zero and update RESULT to point to the newly created node. Return non-zero
+ * otherwise.
+ */
+static int
+create_dir_node(struct gcov_ftree_node *parent, char *name,
+        struct gcov_ftree_node **result)
+{
+    struct gcov_ftree_node *node;
+
+    /* Initialize new node */
+    node = alloc_node(name, NULL);
+    if (!node)
+        return -ENOMEM;
+    /* Create proc filesystem entry */
+    node->proc[0] = proc_mkdir(name, parent->proc[0]);
+    if (!node->proc[0]) 
+    {
+        free_node(node);
+        return -EIO;
+    }
+    /* Insert node into tree */
+    node->parent = parent;
+    node->sibling = parent->files;
+    parent->files = node;
+    *result = node;
+    return 0;
+}
+
+static struct file_operations proc_gcov_operations = {
+    .owner        = THIS_MODULE,
+    .read         = read_gcov,
+    .write        = write_gcov,
+};
+
+/*
+ * Create a new file node named NAME under PARENT. Associate node with BB.
+ * Return zero upon success, non-zero otherwise.
+ */
+static int
+create_file_node(struct gcov_ftree_node *parent, char *name,
+         struct gcov_info *bb)
+{
+    struct gcov_ftree_node *node;
+    char *link_target;
+    char *link_name;
+    int i;
+    /* Initialize new node */
+    node = alloc_node(name, bb);
+    if (!node)
+        return -ENOMEM;
+    /* Create proc filesystem entry */
+    node->proc[0] = create_proc_entry(name, S_IWUSR | S_IRUGO,
+                      parent->proc[0]);
+    if (!node->proc[0]) 
+    {
+        free_node(node);
+        return -EIO;
+    }
+    node->proc[0]->data = node;
+    node->proc[0]->proc_fops = &proc_gcov_operations;
+    node->proc[0]->size = sizeof_da_file(bb);
+    /* Create symbolic links */
+    if (xen_gcov_link) 
+    {
+        /* Note: temp string length is calculated as upper limit */
+        link_target = (char *)kmalloc(strlen(bb->filename) +
+                          strlen(da_ending) +
+                          strlen(node_info->src_path),
+                          GFP_KERNEL);
+        if (!link_target) 
+        {
+            delete_from_proc(node);
+            free_node(node);
+            return -ENOMEM;
+        }
+        for (i = 0; i < sizeof(endings) / sizeof(endings[0]); i++) 
+        {
+
+            strcpy(link_target, bb->filename);
+            link_target[strlen(link_target) -
+                    strlen(da_ending)] = 0;
+            strcat(link_target, endings[i]);
+            link_name = strrchr(link_target, '/') + 1;
+            node->proc[i + 1] = proc_symlink(link_name,
+                             parent->proc[0],
+                             link_target);
+            if (!node->proc[i + 1]) 
+            {
+                kfree(link_target);
+                delete_from_proc(node);
+                free_node(node);
+                return -EIO;
+             }
+        }
+        kfree(link_target);
+    }
+    /* Insert node into tree */
+    node->parent = parent;
+    node->sibling = parent->files;
+    parent->files = node;
+    node->next = leaf_nodes;
+    leaf_nodes = node;
+    return 0;
+}
+
+/* Return proc filesystem entry name for FILENAME. */
+static inline char *get_proc_filename(const char *filename)
+{
+    char *result;
+
+    result = strdup(filename + sourcepath_len + 1);
+    return result;
+}
+
+/*
+ * Create tree node and proc filesystem entry for BB. Create subdirectories as
+ * necessary. Return zero upon success, non-zero otherwise.
+ */
+static int create_node(struct gcov_info *bb)
+{
+    struct gcov_ftree_node *parent;
+    struct gcov_ftree_node *node;
+    char *filename;
+    char *curr;
+    char *next;
+    int rc;
+
+    filename = get_proc_filename(bb->filename);
+    if (!filename)
+        return -ENOMEM;
+    /* Recreate directory path in proc filesystem */
+    parent = &tree_root;
+    for (curr = filename; (next = strchr(curr, '/')); curr = next + 1) 
+    {
+        /* Skip empty path components */
+        if (curr == next)
+            continue;
+        *next = 0;
+        /* Check whether directory already exists */
+        for (node = parent->files;
+             node && (strcmp(node->fname, curr) != 0);
+             node = node->sibling) ;
+        if (!node) 
+        {
+            /* Create directory node */
+            rc = create_dir_node(parent, curr, &node);
+            if (rc) 
+            {
+                if (parent != &tree_root)
+                    cleanup_node_and_path(parent);
+                kfree(filename);
+                return rc;
+            }
+        }
+        parent = node;
+    }
+    rc = create_file_node(parent, curr, bb);
+    kfree(filename);
+    return rc;
+}
+
+static void xeno_prof(void *data)
+{
+    /*update linked list with profiling info and line counts */
+    WARN_ON(HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0));
+    
+    /*again schedule work */
+    schedule_delayed_work(&work, xen_gcov_update * HZ);
+
+}
+
+/* 
+ * Invokes hypercall to get all the necessary gcov information to construct 
+ * gcov linked list in Kernel
+ */
+static inline int get_list_info(void)
+{
+
+    int ret,i;
+    node_info = kmalloc(sizeof(struct node_info), GFP_KERNEL);
+    if ((ret =
+        HYPERVISOR_gcovprof_op(GCOVPROF_get_info, node_info, 0)) != 0)
+        return ret;
+    
+    xen_gcov_version = node_info->g_version;
+
+    node_info->ctr_num =  
+        kmalloc(node_info->n_files * sizeof(gcov_unsigned_t *), GFP_KERNEL);
+    for(i=0;i<node_info->n_files;i++){
+         node_info->ctr_num[i] =
+                kmalloc(GCOV_COUNTERS * sizeof(gcov_unsigned_t), GFP_KERNEL);
+    }
+
+    node_info->n_funcs = 
+        kmalloc(node_info->n_files * sizeof(gcov_unsigned_t), GFP_KERNEL);
+
+    node_info->active_counters = 
+        kmalloc(node_info->n_files * sizeof(unsigned int), GFP_KERNEL);
+    
+    if ((ret = 
+        HYPERVISOR_gcovprof_op(GCOVPROF_get_info, node_info, 1)) != 0)
+         return ret;
+
+    sourcepath_len = strlen(node_info->src_path);
+    return ret;
+}
+
+static int __init xen_gcov_init(void)
+{
+    struct gcov_info *bb;
+    int ret,rc = 0;
+
+    /* seek gcov core variable information & construct the gcov_info list */
+    ret = get_list_info();
+    if (ret) {
+        printk(KERN_ERR GCOV_PROC_HEADER
+           "No Core variable, Check if GCOV core is compiled in or not\n");
+        return -ENOENT;
+    }
+
+    /*add sys entry in /proc/sys/xen for count update tuning*/    
+#ifdef CONFIG_SYSCTL
+    gcov_table_header = register_sysctl_table(gcov_root_table, 0);
+    if (!gcov_table_header)
+        printk(KERN_INFO GCOV_PROC_HEADER
+              "no /proc/sys/xen added\n");
+#endif
+    printk(KERN_INFO GCOV_PROC_HEADER "xen_gcov_link=%d xen_gcov_update=%d\n", 
+                        xen_gcov_link, xen_gcov_update);
+
+
+    /* Create gcov_info linked list */
+    create_bb_list();
+    
+    /*populate the linked list with profiling info and line counts */
+    ret = HYPERVISOR_gcovprof_op(GCOVPROF_start, list_head, 0);
+    if (ret) {
+        printk(KERN_ERR GCOV_PROC_HEADER
+                "Failed to retrive hypervisor profiling info\n");
+        return ret;
+    }
+		
+    /* Initializing root node and /proc/gcov entry */
+    tree_root.fname = GCOV_PROC_ROOT;
+    tree_root.proc[0] = proc_mkdir(tree_root.fname, xen_base);
+    if (!tree_root.proc[0]) { 
+        printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+           "create root proc filesystem entry\n");
+        return -EIO;
+    }
+    /* Create /proc/gcov/vmlinux entry */
+    proc_vmlinux = create_proc_entry(GCOV_PROC_VMLINUX, S_IWUSR | S_IRUGO,
+                 tree_root.proc[0]);
+    if (!proc_vmlinux) 
+    {
+        printk(KERN_ERR GCOV_PROC_HEADER "init failed: could not "
+            "create proc filesystem entry %s\n", GCOV_PROC_VMLINUX);
+        cleanup_node(&tree_root);
+        return -EIO;
+    }
+    proc_vmlinux->proc_fops = &proc_gcov_operations;
+
+    /*populate /proc/xen/gcov entry with data files*/
+    for (bb = list_head; bb != NULL; bb = bb->next) 
+    {
+        rc = create_node(bb);
+        if (rc) 
+        {
+            printk(KERN_ERR GCOV_PROC_HEADER
+                "init failed: could not create node for %s (err=%d)\n",
+            bb->filename, rc);
+            remove_proc_entry(proc_vmlinux->name,
+                   tree_root.proc[0]);
+            cleanup_node(&tree_root);
+            return rc;
+        }
+    } /*done*/
+
+    update_vmlinux_data();
+
+    /*schedule for profiling update after <xen_gcov_update> secs*/
+    schedule_delayed_work(&work, xen_gcov_update * HZ);
+    printk(KERN_INFO GCOV_PROC_HEADER "init done\n");
+    kfree(node_info);
+    return 0;
+}
+
+/* Clean up module data. */
+static void __exit xen_gcov_cleanup(void)
+{
+    cancel_delayed_work(&work);
+    flush_scheduled_work();
+#ifdef CONFIG_SYSCTL
+    unregister_sysctl_table(gcov_table_header);
+#endif
+    remove_proc_entry(proc_vmlinux->name, tree_root.proc[0]);
+    cleanup_node(&tree_root);
+    free_bb_list();
+    printk(KERN_INFO GCOV_PROC_HEADER "Module is now unloaded\n");
+}
+
+module_init(xen_gcov_init);
+module_exit(xen_gcov_cleanup);
diff -r f85259abcddf include/asm-i386/mach-xen/asm/hypercall.h
--- a/include/asm-i386/mach-xen/asm/hypercall.h	Tue Feb 05 10:05:19 2008 +0000
+++ b/include/asm-i386/mach-xen/asm/hypercall.h	Wed Apr 29 08:59:59 2009 +0530
@@ -411,6 +411,13 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 
 
 #endif /* __HYPERCALL_H__ */
diff -r f85259abcddf include/asm-x86_64/mach-xen/asm/hypercall.h
--- a/include/asm-x86_64/mach-xen/asm/hypercall.h	Tue Feb 05 10:05:19 2008 +0000
+++ b/include/asm-x86_64/mach-xen/asm/hypercall.h	Wed Apr 29 08:59:59 2009 +0530
@@ -412,4 +412,11 @@
 	return _hypercall2(int, kexec_op, op, args);
 }
 
+static inline int __must_check
+HYPERVISOR_gcovprof_op(
+	int cmd, void *arg, int val)
+{
+	return _hypercall3(int, gcovprof_op, cmd, arg, val);
+}
+
 #endif /* __HYPERCALL_H__ */
diff -r f85259abcddf include/xen/interface/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/xen/interface/xen-gcov.h	Wed Apr 29 08:59:59 2009 +0530
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *  xen-gcov.h
+ *   
+ *  Interface for populating hypervisor profiling info under /proc/xen
+ *  
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to
+ *  deal in the Software without restriction, including without limitation the
+ *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ *  sell copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *   
+ *  The above copyright notice and this permission notice shall be included in
+ *  all copies or substantial portions of the Software.
+ *   
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ *  DEALINGS IN THE SOFTWARE.
+ *   
+ *  Author: tej parkash <tej.parkash@hcl.in>
+ *  Written by tej parkash and team
+ *  
+ */
+/******************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_get_info             0
+#define GCOVPROF_start		      1
+#define GCOVPROF_reset		      2
+#define GCOVPROF_last_op              3
+
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS                 5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+/*xen-gcov-proc specific macros*/
+#define RESET_GCDA      0
+
+#define FILE_SIZE       100
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+
+
+struct gcov_ftree_node
+{
+    char *fname;             	       /* Hierarchy-relative name */
+    struct gcov_ftree_node *sibling;   /* First sibling of this node */
+    struct gcov_ftree_node *files;     /* First child of this node */
+    struct gcov_ftree_node *parent;    /* Parent of this node */
+    struct proc_dir_entry *proc[4];    /* Entries for .da, .bb, .bbg, .c */
+    struct gcov_info *bb;              /* Associated struct bb */
+    loff_t offset;           	       /* Offset in vmlinux file */
+    size_t da_size;          	       /* Size of associated .da file */
+    size_t header_size;      	       /* Size of associated file header */
+    struct gcov_ftree_node *next;      /* Next leaf node */
+};
+
+
+struct gcov_fn_info
+{
+    gcov_unsigned_t ident;    	       /* unique ident of function */
+    gcov_unsigned_t checksum; 	       /* function checksum */
+    unsigned int n_ctrs[0];            /* instrumented counters */
+};
+typedef struct gcov_fn_info gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_fn_info_t);
+
+struct gcov_ctr_info
+{
+    gcov_unsigned_t num;      	       /* number of counters.  */
+    gcov_type *values;		       /* thier values. */
+    gcov_merge_fn merge;      	       /* merge function  */
+};
+typedef struct  gcov_ctr_info gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+    gcov_unsigned_t version;  	       /* expected version number */
+    struct gcov_info *next;   	       /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;    	       /* uniquifying time stamp */
+    char *filename;
+
+    unsigned int n_functions;          /* number of functions */
+    struct gcov_fn_info *functions;    /* table of functions */
+
+    unsigned int ctr_mask;             /* mask of counters instrumented.  */
+    struct gcov_ctr_info counts[0];    /* count data */
+};
+typedef struct gcov_info gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_info_t);
+
+struct node_info
+{
+    char src_path[100];		       /* source path */
+    unsigned long g_version;	       /* gcov version */
+    unsigned int n_files;	       /* num of file in hypervisor code */
+    gcov_unsigned_t **ctr_num;	       /* count variables */
+    unsigned int *n_funcs;             /* num of funcs per bb struct */
+    unsigned int *active_counters;     /* No of gcov counters */
+	
+};
+typedef struct node_info node_info_t;
+DEFINE_XEN_GUEST_HANDLE(node_info_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r f85259abcddf include/xen/interface/xen.h
--- a/include/xen/interface/xen.h	Tue Feb 05 10:05:19 2008 +0000
+++ b/include/xen/interface/xen.h	Wed Apr 29 08:59:59 2009 +0530
@@ -93,6 +93,7 @@
 #define __HYPERVISOR_sysctl               35
 #define __HYPERVISOR_domctl               36
 #define __HYPERVISOR_kexec_op             37
+#define __HYPERVISOR_gcovprof_op          38
 
 /* Architecture-specific hypercall definitions. */
 #define __HYPERVISOR_arch_0               48
diff -r f85259abcddf include/xen/xen_proc.h
--- a/include/xen/xen_proc.h	Tue Feb 05 10:05:19 2008 +0000
+++ b/include/xen/xen_proc.h	Wed Apr 29 08:59:59 2009 +0530
@@ -3,6 +3,8 @@
 #define __ASM_XEN_PROC_H__
 
 #include <linux/proc_fs.h>
+
+extern struct proc_dir_entry *xen_base;
 
 extern struct proc_dir_entry *create_xen_proc_entry(
 	const char *name, mode_t mode);

[-- Attachment #3: xen-3.3-gcov-v5.patch --]
[-- Type: text/x-patch, Size: 19646 bytes --]

diff -r 99c6de6e02f6 xen/Rules.mk
--- a/xen/Rules.mk	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/Rules.mk	Tue Apr 28 18:39:16 2009 +0530
@@ -8,6 +8,15 @@
 perfc_arrays  ?= n
 crash_debug   ?= n
 frame_pointer ?= n
+
+# gcov profiler build for xen hypervisor only
+gcov          ?= n
+
+# xen source path to get update in .gcno files
+ifeq ($(gcov),y)
+xen_src := $(CURDIR)
+xentree := $(if $(xen_src),$(xen_src),$(CURDIR))
+endif
 
 XEN_ROOT=$(BASEDIR)/..
 include $(XEN_ROOT)/Config.mk
@@ -56,6 +65,7 @@
 ALL_OBJS-y               += $(BASEDIR)/arch/$(TARGET_ARCH)/built_in.o
 
 CFLAGS-y                += -g -D__XEN__
+CFLAGS-$(gcov)          += -fprofile-arcs -ftest-coverage -DCONFIG_XEN_GCOV
 CFLAGS-$(XSM_ENABLE)    += -DXSM_ENABLE
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_ENABLE -DXSM_MAGIC=0xf97cff8c
 CFLAGS-$(FLASK_ENABLE)  += -DFLASK_DEVELOP -DFLASK_BOOTPARAM -DFLASK_AVC_STATS
@@ -112,12 +122,18 @@
 
 .PHONY: clean
 clean:: $(addprefix _clean_, $(subdir-all))
-	rm -f *.o *~ core
+	rm -f *.o *.gcno *~ core
 _clean_%/: FORCE
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C $* clean
 
 %.o: %.c $(HDRS) Makefile
+ifeq ($(gcov),y)
+	$(CC) $(CFLAGS) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+else
 	$(CC) $(CFLAGS) -c $< -o $@
+endif
+
 
 %.o: %.S $(AHDRS) Makefile
 	$(CC) $(AFLAGS) -c $< -o $@
diff -r 99c6de6e02f6 xen/arch/x86/mm/hap/Makefile
--- a/xen/arch/x86/mm/hap/Makefile	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/arch/x86/mm/hap/Makefile	Tue Apr 28 18:39:16 2009 +0530
@@ -4,8 +4,22 @@
 obj-y += guest_walk_4level.o
 obj-y += p2m-ept.o
 
+LN = /bin/ln
+
 guest_levels  = $(subst level,,$(filter %level,$(subst ., ,$(subst _, ,$(1)))))
 guest_walk_defns = -DGUEST_PAGING_LEVELS=$(call guest_levels,$(1))
 
+num = $(call guest_levels,$(@F))level.c
+
 guest_walk_%level.o: guest_walk.c $(HDRS) Makefile
+ifeq ($(gcov),y)
+	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s guest_walk.c guest_walk_$(num)
+else
 	$(CC) $(CFLAGS) $(call guest_walk_defns,$(@F)) -c $< -o $@
+endif
+	
+.PHONY: clean
+clean::
+	rm -f guest_walk_*.c
diff -r 99c6de6e02f6 xen/arch/x86/mm/shadow/Makefile
--- a/xen/arch/x86/mm/shadow/Makefile	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/arch/x86/mm/shadow/Makefile	Tue Apr 28 18:39:16 2009 +0530
@@ -1,5 +1,18 @@
 obj-$(x86_32) += common.o guest_2.o guest_3.o
 obj-$(x86_64) += common.o guest_2.o guest_3.o guest_4.o
 
+LN = /bin/ln
+num=$*.c
+
 guest_%.o: multi.c $(HDRS) Makefile
+ifeq ($(gcov),y)
+	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c -o $@ \
+		$(if $(filter-out /%,$<),$(xentree)/$<,$<)
+	$(LN) -f -s multi.c guest_$(num)
+else
 	$(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
+endif
+
+.PHONY: clean
+clean::
+	rm -f guest_*.c
diff -r 99c6de6e02f6 xen/arch/x86/x86_32/entry.S
--- a/xen/arch/x86/x86_32/entry.S	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/arch/x86/x86_32/entry.S	Tue Apr 28 18:39:16 2009 +0530
@@ -703,6 +703,9 @@
         .long do_sysctl             /* 35 */
         .long do_domctl
         .long do_kexec_op
+#ifdef CONFIG_XEN_GCOV
+        .long do_gcovprof_op
+#endif
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/4)
         .long do_ni_hypercall
         .endr
@@ -750,6 +753,9 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec_op          */
+#ifdef CONFIG_XEN_GCOV
+        .byte 3 /* do_gcovprof_op	*/
+#endif
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
         .endr
diff -r 99c6de6e02f6 xen/arch/x86/x86_64/entry.S
--- a/xen/arch/x86/x86_64/entry.S	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/arch/x86/x86_64/entry.S	Tue Apr 28 18:39:16 2009 +0530
@@ -692,6 +692,9 @@
         .quad do_sysctl             /* 35 */
         .quad do_domctl
         .quad do_kexec_op
+#ifdef CONFIG_XEN_GCOV
+        .quad do_gcovprof_op
+#endif
         .rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
         .quad do_ni_hypercall
         .endr
@@ -739,6 +742,9 @@
         .byte 1 /* do_sysctl            */  /* 35 */
         .byte 1 /* do_domctl            */
         .byte 2 /* do_kexec             */
+#ifdef CONFIG_XEN_GCOV
+        .byte 3 /* do_gcovprof_op       */
+#endif
         .byte 1 /* do_xsm_op            */
         .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
         .byte 0 /* do_ni_hypercall      */
diff -r 99c6de6e02f6 xen/common/Makefile
--- a/xen/common/Makefile	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/common/Makefile	Tue Apr 28 18:39:16 2009 +0530
@@ -28,6 +28,7 @@
 obj-y += xmalloc.o
 obj-y += rcupdate.o
 
+obj-$(gcov)        += xen-gcov-core.o
 obj-$(perfc)       += perfc.o
 obj-$(crash_debug) += gdbstub.o
 obj-$(xenoprof)    += xenoprof.o
@@ -42,6 +43,8 @@
 
 subdir-y += libelf
 
+CFLAGS += -DSRC_PATH='"$(BASEDIR)"'
+
 # Object file contains changeset and compiler information.
 version.o: $(BASEDIR)/include/xen/compile.h
 
diff -r 99c6de6e02f6 xen/common/xen-gcov-core.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/common/xen-gcov-core.c	Tue Apr 28 18:39:16 2009 +0530
@@ -0,0 +1,332 @@
+/**
+ *  xen-gcov-core.c: arch dependent code for hypervisor profiling
+ *
+ *  Written by tej parkash and team(tej.parkash@hcl.in)
+ *                
+ */
+/***************************************************************************/
+#include <xen/init.h>
+#include <xen/kernel.h>
+#include <xen/guest_access.h>
+#include <xen/sched.h>
+#include <xen/event.h>
+#include <xen/string.h>
+#include <public/xen-gcov.h>
+#include <xen/paging.h>
+#include <xsm/xsm.h>
+#include <xen/config.h>
+#include <xen/lib.h>
+
+
+#define XEN_GCOV_CORE    "xen-gcov-core:"
+
+/* No of XEN source files to profile */
+static int num_xen_files;
+
+gcov_unsigned_t gcov_version = 0;
+
+/* List head for gcov_info structure list */
+struct gcov_info *bb_head;
+
+/* global constructor list */
+struct ctor_list 
+{
+    unsigned long num;
+    ctor_t ctor[];
+};
+
+/* Start of global constructor list. */
+extern struct ctor_list __CTOR_LIST__;
+
+/* 
+ * Determine whether counter TYPE is active in list node 
+ * For gcc 3.4 to 4.3 only counter 0 will be active
+ *  
+ */
+static inline int
+counter_active(struct gcov_info *bb, unsigned int type)
+{
+    return (1 << type) & bb->ctr_mask;
+}
+
+/* Return the number of active counter types for BB. */
+static inline unsigned int
+num_counter_active(struct gcov_info *bb)
+{
+        unsigned int i;
+        unsigned int result;
+
+        result = 0;
+        for (i=0; i < GCOV_COUNTERS; i++)
+                if (counter_active(bb, i))
+                        result++;
+        return result;
+}
+
+/* 
+ * Get number of bytes used for one entry in the gcov_fn_info array pointed to
+ * by BB->functions.
+ */
+static inline unsigned int
+get_fn_stride(struct gcov_info *bb)
+{
+    unsigned int stride;
+
+    stride = sizeof(struct gcov_fn_info) + num_counter_active(bb)*
+             sizeof(unsigned int);
+    if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int)) 
+    {
+        stride += __alignof__(struct gcov_fn_info) - 1;
+        stride &= ~(__alignof__(struct gcov_fn_info) - 1);
+    }
+    return stride;
+}
+
+/* Get the address of gcov_fn_info for function FUNC of BB. */
+static inline struct gcov_fn_info *
+get_fn_info(struct gcov_info *bb, unsigned int func)
+{
+    return (struct gcov_fn_info *)
+        ((char *)bb->functions + func * get_fn_stride(bb));
+}
+
+/* Reset the execution count values on each list node */
+static void reset(struct gcov_info *bb)
+{
+
+    unsigned int i;
+    struct gcov_ctr_info *ctr;
+    ctr = bb->counts;
+    for (i=0; i < GCOV_COUNTERS; i++)
+    {
+        if (counter_active(bb, i)) 
+        {
+            memset(ctr->values, 0, ctr->num * sizeof(gcov_type));
+            ctr++;
+        }
+    }
+}
+
+/*
+ * Func to reset the execution counts. Reset all nodes count 
+ * if mul defined else reset single file count specified in arg.
+ */
+
+static int reset_counts(XEN_GUEST_HANDLE(void) arg, int mul)
+{
+    struct gcov_info *tmp=bb_head;
+    char bb_file[100];
+
+    if (!mul)
+    {
+        /* reset single data file */
+        if ( copy_from_guest(&bb_file, arg, 1) )
+            return -EFAULT;
+        for(;tmp!=NULL;tmp=tmp->next)
+        {
+            if(!strcmp(tmp->filename, bb_file))
+            {
+                reset(tmp);
+                break;
+            }
+            else
+                continue;
+        }
+    }
+    else
+    {
+        /* reset all data files */
+        for(;tmp!=NULL;tmp=tmp->next)
+            reset(tmp);
+    }
+    return 0;
+}
+
+/* 
+ * Func copies metadata in each gcov_info list nodes to guest. 
+ * with this metadeta, guest populates and update the /proc 
+ * with gcov data files (*.gcda)
+ */
+
+static int ret_profiled_data(XEN_GUEST_HANDLE(void) arg, int val)
+{
+    struct gcov_info *bb,*bb_tmp,*tmp = bb_head;
+    struct gcov_fn_info *func,*fn_ptr;
+    gcov_type *ctr_values=NULL;
+    int i,j,active,len; 
+
+    bb_tmp = 
+        xmalloc_bytes(sizeof(struct gcov_info) + sizeof(struct gcov_ctr_info) );
+    bb = bb_tmp;
+
+    if ( copy_from_guest(bb_tmp, arg, 1) )
+        return -EFAULT;
+
+     /* iterate through list nodes to copy metadata in Guest nodes*/
+    for(;tmp!=NULL, bb!=NULL; tmp = tmp->next)
+    {
+        bb->version  = tmp->version;
+        bb->stamp    = tmp->stamp;
+        len=strlen(tmp->filename)+1;
+        memcpy(bb->filename, tmp->filename,sizeof(char)*len);
+        bb->ctr_mask    = tmp->ctr_mask;
+        bb->n_functions = tmp->n_functions;
+        
+        active= num_counter_active(tmp); 
+        /* Copy functions data */        
+        for(i=0;i<tmp->n_functions;i++) 
+        {
+            fn_ptr= get_fn_info(bb,i);
+            func = get_fn_info(tmp,i);
+            fn_ptr->ident=func->ident;
+            fn_ptr->checksum=func->checksum;
+            for (j=0;j< active;j++) 
+                fn_ptr->n_ctrs[j]=func->n_ctrs[j];
+        }
+
+        /* Copy count data */
+        for (i=0; i < active; i++) {
+            bb->counts[i].num = tmp->counts[i].num;
+            bb->counts[i].merge = tmp->counts[i].merge;
+            len = sizeof(gcov_type)*tmp->counts[i].num;
+            ctr_values = xmalloc_bytes(len);
+            raw_copy_from_guest(ctr_values, bb->counts[i].values, len);
+	    memcpy(ctr_values, tmp->counts[i].values, len);
+	    raw_copy_to_guest(bb->counts[i].values, ctr_values, len);
+	    xfree(ctr_values);
+         }        
+        /*increment the bb_head pointers*/
+        bb=bb->next;
+    }
+    if ( copy_to_guest(arg, bb_tmp, 1) )
+        return -EFAULT;
+    return 0;
+}
+
+/* 
+ * Func to retrieve the inital gcov core information
+ * by Kernel 
+ * e.g source path, no. of files etc. 
+ */
+
+static int info_to_guest(XEN_GUEST_HANDLE(void) arg,int num)
+{
+    
+    struct gcov_info *bb = bb_head;
+    node_info_t *tmp  = xmalloc(struct node_info);
+    int i, j, active;
+    
+    if( copy_from_guest(tmp, arg, 1) )
+        return -EFAULT;
+    if ( num == 0 ) 
+    {
+        safe_strcpy(tmp->src_path,SRC_PATH);
+        tmp->g_version=gcov_version;
+        tmp->n_files=num_xen_files;
+    } 
+    else 
+    {
+       for(i=0;i<num_xen_files;i++)
+        {
+            tmp->n_funcs[i]=bb->n_functions;
+            active=num_counter_active(bb);
+            tmp->active_counters[i]= active;
+            for(j=0;j<GCOV_COUNTERS;j++) {
+                 tmp->ctr_num[i][j]= bb->counts[j].num;
+		}
+
+	    bb=bb->next;
+        }
+    }
+
+    if( copy_to_guest(arg, tmp, 1) )
+        return -EFAULT;
+    return 0;
+}
+
+/*internal hyercall functions*/
+int do_gcovprof_op(int op, XEN_GUEST_HANDLE(void) arg, int val)
+{
+    int ret = 0;
+    if ((op < 0) || (op > GCOVPROF_last_op)) 
+    {
+        printk(KERN_ERR XEN_GCOV_CORE "Invalid operation %d\n", op);
+        return -EINVAL;
+    }
+    switch (op) 
+    {
+        case GCOVPROF_get_info:
+            ret = info_to_guest(arg, val);
+            break;
+
+        case GCOVPROF_start:
+            ret = ret_profiled_data(arg,val);
+            break;
+
+        case GCOVPROF_reset:
+            ret = reset_counts(arg,val);
+            break;
+
+        default:
+            ret = -ENOSYS;
+    }
+    return ret;
+}
+
+/* 
+ * Call constructors and create gcov_info linked list 
+ * List contains metadeta to construct gcov data files
+ * e.g data file name, execution counts etc. 
+ */
+void do_global_ctors(ctor_t ctor[], unsigned long num)
+{
+    unsigned long i;
+
+    /* Call constructors and create gcov_info linked list*/
+    for (i = 0; i < num && ctor[i]; i++)
+        ctor[i] ();
+    /* Holds number of executable scanned by constructor */
+    num_xen_files = i-1;
+}
+
+/*Register supplied struct BB. Called by each object code constructor. */
+void __gcov_init(struct gcov_info *bb)
+{
+    if (!bb->version)
+        return;
+    /*Check for compatible gcc version */
+    if (gcov_version == 0)
+        gcov_version = bb->version;
+    else if (bb->version != gcov_version) 
+    {
+        printk(KERN_WARNING XEN_GCOV_CORE "gcc version mismatch in "
+                  "file '%s'!\n", bb->filename);
+        return;
+    }
+    /*Set up linked list */
+    bb->version = 0;
+    bb->next = bb_head;
+    bb_head = bb;
+}
+
+static int __init gcov_init(void)
+{
+    do_global_ctors(__CTOR_LIST__.ctor, __CTOR_LIST__.num);
+    printk(KERN_INFO XEN_GCOV_CORE "init done\n");
+    return 0;
+}
+
+__initcall(gcov_init);
+
+/* Unused functions needed to prevent linker errors. */
+void __gcov_flush(void) {}
+void __gcov_merge_add(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_single(gcov_type * counters, unsigned int n_counters){}
+void __gcov_merge_delta(gcov_type * counters, unsigned int n_counters){}
+
+EXPORT_SYMBOL(__gcov_init);
+EXPORT_SYMBOL(do_global_ctors);
+EXPORT_SYMBOL(__gcov_flush);
+EXPORT_SYMBOL(__gcov_merge_add);
+EXPORT_SYMBOL(__gcov_merge_single);
+EXPORT_SYMBOL(__gcov_merge_delta);
diff -r 99c6de6e02f6 xen/include/public/xen-gcov.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/public/xen-gcov.h	Tue Apr 28 18:39:16 2009 +0530
@@ -0,0 +1,109 @@
+/******************************************************************************
+ * xen-gcov.h
+ * 
+ * Interface for enabling and populating hypervisor profiling info.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: tej parkash <tej.parkash@hcl.in>
+ * Written by tej parkash and team
+ *
+ */
+/******************************************************************************/
+
+#ifndef __XEN_PUBLIC_GCOVPROF_H__
+#define __XEN_PUBLIC_GCOVPROF_H__
+
+#include "xen.h"
+
+/*
+ * Commands to HYPERVISOR_gcovprof_op()
+ */
+#define GCOVPROF_get_info           0
+#define GCOVPROF_start              1
+#define GCOVPROF_reset              2
+#define GCOVPROF_last_op            3
+
+/*gcc specific macros*/
+#define GCOV_COUNTERS               5
+#define GCOV_DATA_MAGIC     ((gcov_unsigned_t) 0x67636461)
+#define GCOV_TAG_FUNCTION   ((gcov_unsigned_t) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((gcov_unsigned_t) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(COUNT)                 \
+    (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t) (COUNT) << 17))
+
+
+typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
+typedef signed gcov_type __attribute__ ((mode (DI)));
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*ctor_t)(void);
+
+struct gcov_fn_info
+{
+    gcov_unsigned_t ident;             /* unique ident of function */
+    gcov_unsigned_t checksum;          /* function checksum */
+    unsigned n_ctrs[0];                /* instrumented counters */
+};
+typedef struct gcov_fn_info gcov_fn_info_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_fn_info_t);
+
+struct gcov_ctr_info
+{
+    gcov_unsigned_t num;               /* number of counters.  */
+    gcov_type *values;                 /* their values.  */
+    gcov_merge_fn merge;               /* The function used to merge them.  */
+};
+typedef struct  gcov_ctr_info gcov_ctr_info_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_ctr_info_t);
+
+/* Information about a single object file.  */
+struct gcov_info
+{
+    gcov_unsigned_t version;           /* expected version number */
+    struct gcov_info *next;            /* link to next, used by libgcov */
+
+    gcov_unsigned_t stamp;             /* uniquifying time stamp */
+    char *filename;
+
+    unsigned n_functions;              /* number of functions */
+    struct gcov_fn_info *functions;    /* table of functions */
+
+    unsigned ctr_mask;                 /* mask of counters instrumented.  */
+    struct gcov_ctr_info counts[0];    /* count data. The number of bits
+                                         set in the ctr_mask field
+                                         determines how big this array
+                                         is.  */
+};
+typedef struct gcov_info gcov_info_t;
+DEFINE_XEN_GUEST_HANDLE(gcov_info_t);
+
+/* info to construct guest Nodes */
+struct node_info 
+{
+    char src_path[100];		       /* xen source code path */
+    unsigned long g_version; 
+    unsigned int n_files;	       /* max. nodes */
+    gcov_unsigned_t **ctr_num;         /* count per node */
+    unsigned int *n_funcs;             /* funcs per node */
+    unsigned int *active_counters;     /* No of active gcov counters per BB */
+};
+typedef struct node_info node_info_t;
+DEFINE_XEN_GUEST_HANDLE(node_info_t);
+
+#endif /* __XEN_PUBLIC_GCOVPROF_H__ */
diff -r 99c6de6e02f6 xen/include/xen/config.h
--- a/xen/include/xen/config.h	Tue Apr 28 17:00:48 2009 +0530
+++ b/xen/include/xen/config.h	Tue Apr 28 18:39:16 2009 +0530
@@ -96,4 +96,22 @@
 #define __cpuinitdata
 #define __cpuinit
 
+#ifdef CONFIG_XEN_GCOV
+#define SORT(x)        x
+#define CONSTRUCTORS                           \
+       __CTOR_LIST__ = .;                  \
+       LONG((__CTOR_END__ - __CTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       __CTOR_LIST2__ = .;                 \
+       *(SORT(.ctors))                     \
+       LONG(0)                         \
+       __CTOR_END__ = .;                   \
+       __DTOR_LIST__ = .;                  \
+       LONG((__DTOR_END__ - __DTOR_LIST__) /           \
+           (__CTOR_LIST2__ - __CTOR_LIST__) - 2)       \
+       *(SORT(.dtors))                     \
+       LONG(0)                         \
+       __DTOR_END__ = .;
+
+#endif /* CONFIG_XEN_GCOV */
 #endif /* __XEN_CONFIG_H__ */

[-- Attachment #4: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-04-29 14:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-02-26 15:09 [PATCH-0/2] Hypervisor profiling using GCOV (64bit Hypervisor) Tej
2009-02-27 19:02 ` Gianluca Guida
2009-02-28  7:23   ` Tej
2009-03-02 16:06   ` Tej
     [not found]     ` <f1c9d250903252359p13f2fe47r84674d183fe8629f@mail.gmail.com>
2009-03-26 10:57       ` Gianluca Guida
2009-03-26 15:11         ` Tej
2009-04-06 15:04         ` Tej
2009-04-29 14:56         ` Tej

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.