diff -r 86d6c6417cf9 drivers/xen/Kconfig --- a/drivers/xen/Kconfig Thu Feb 04 13:08:27 2010 +0000 +++ b/drivers/xen/Kconfig Sun Feb 07 00:13:40 2010 +0530 @@ -311,6 +311,13 @@ 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 86d6c6417cf9 drivers/xen/Makefile --- a/drivers/xen/Makefile Thu Feb 04 13:08:27 2010 +0000 +++ b/drivers/xen/Makefile Sun Feb 07 00:13:40 2010 +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 86d6c6417cf9 drivers/xen/core/xen_proc.c --- a/drivers/xen/core/xen_proc.c Thu Feb 04 13:08:27 2010 +0000 +++ b/drivers/xen/core/xen_proc.c Sun Feb 07 00:13:40 2010 +0530 @@ -3,7 +3,7 @@ #include #include -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 86d6c6417cf9 drivers/xen/gcov/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/gcov/Makefile Sun Feb 07 00:13:40 2010 +0530 @@ -0,0 +1,1 @@ +obj-$(CONFIG_XEN_GCOV_PROC) := xen-gcov-proc.o diff -r 86d6c6417cf9 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 Sun Feb 07 00:13:40 2010 +0530 @@ -0,0 +1,1235 @@ +/** + * @file xen-gcov-proc.c + * + * @Author: Hubertus Franke + * Rajan Ravindran + * Peter Oberparleiter + * Paul Larson + * + * Modified by tej parkash and team for Xen (tej.parkash@xxxxxx) + * + */ +/***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 >/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;in_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 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 86d6c6417cf9 include/asm-i386/mach-xen/asm/hypercall.h --- a/include/asm-i386/mach-xen/asm/hypercall.h Thu Feb 04 13:08:27 2010 +0000 +++ b/include/asm-i386/mach-xen/asm/hypercall.h Sun Feb 07 00:13:40 2010 +0530 @@ -411,5 +411,12 @@ return _hypercall1(int, tmem_op, op); } +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 86d6c6417cf9 include/asm-x86_64/mach-xen/asm/hypercall.h --- a/include/asm-x86_64/mach-xen/asm/hypercall.h Thu Feb 04 13:08:27 2010 +0000 +++ b/include/asm-x86_64/mach-xen/asm/hypercall.h Sun Feb 07 00:13:40 2010 +0530 @@ -419,4 +419,12 @@ return _hypercall1(int, tmem_op, op); } +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 86d6c6417cf9 include/xen/interface/xen-gcov.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/xen/interface/xen-gcov.h Sun Feb 07 00:13:40 2010 +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 + * 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 86d6c6417cf9 include/xen/interface/xen.h --- a/include/xen/interface/xen.h Thu Feb 04 13:08:27 2010 +0000 +++ b/include/xen/interface/xen.h Sun Feb 07 00:13:40 2010 +0530 @@ -93,6 +93,7 @@ #define __HYPERVISOR_domctl 36 #define __HYPERVISOR_kexec_op 37 #define __HYPERVISOR_tmem_op 38 +#define __HYPERVISOR_gcovprof_op 39 /* Architecture-specific hypercall definitions. */ #define __HYPERVISOR_arch_0 48 diff -r 86d6c6417cf9 include/xen/xen_proc.h --- a/include/xen/xen_proc.h Thu Feb 04 13:08:27 2010 +0000 +++ b/include/xen/xen_proc.h Sun Feb 07 00:13:40 2010 +0530 @@ -4,6 +4,7 @@ #include +extern struct proc_dir_entry *xen_base; extern struct proc_dir_entry *create_xen_proc_entry( const char *name, mode_t mode); extern void remove_xen_proc_entry(