LWN.net Logo

kernel modules profile support (2.6.6)

From:  "Randy.Dunlap" <rddunlap@osdl.org>
To:  lkml <linux-kernel@vger.kernel.org>
Subject:  [patch] kernel modules profile support (2.6.6)
Date:  Wed, 12 May 2004 17:49:36 -0700
Cc:  hirao@estartu.open.nm.fujitsu.co.jp


Hi,

This patch (below) is an update of a 2.4.18 kernel patch that
supports module profiling in the in-kernel profiler.

Originally from:  hirao (hirao@estartu.open.nm.fujitsu.co.jp)


'readprofile2' changes to util-linux-2.12pre (as well as the
kernel patch) are available at:
  http://developer.osdl.org/rddunlap/modprofile/

Usage info:
  http://developer.osdl.org/rddunlap/modprofile/profile_modules.txt

--
~Randy



 arch/i386/Kconfig         |    6 
 fs/proc/proc_misc.c       |   72 --------
 include/asm-i386/hw_irq.h |   30 ++-
 include/linux/proc_fs.h   |    2 
 include/linux/profile.h   |   84 +++++++++
 kernel/Makefile           |    2 
 kernel/kallsyms.c         |   15 +
 kernel/module.c           |   13 +
 kernel/profile.c          |  410 +++++++++++++++++++++++++++++++++++++++++++++-
 scripts/kallsyms.c        |    8 
 10 files changed, 559 insertions(+), 83 deletions(-)


diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/arch/i386/Kconfig linux-266-modprof/arch/i386/Kconfig
--- linux-266-pv/arch/i386/Kconfig	2004-05-09 19:32:01.000000000 -0700
+++ linux-266-modprof/arch/i386/Kconfig	2004-05-10 12:35:07.000000000 -0700
@@ -1294,6 +1294,12 @@ config X86_MPPARSE
 	depends on X86_LOCAL_APIC && !X86_VISWS
 	default y
 
+config MODULE_PROFILE
+	bool "Module profiling support"
+	select PROFILING
+	help
+	  This enables profiling of kernel loadable modules.
+
 endmenu
 
 source "security/Kconfig"
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/fs/proc/proc_misc.c linux-266-modprof/fs/proc/proc_misc.c
--- linux-266-pv/fs/proc/proc_misc.c	2004-05-09 19:32:01.000000000 -0700
+++ linux-266-modprof/fs/proc/proc_misc.c	2004-05-10 12:35:07.000000000 -0700
@@ -555,70 +555,6 @@ static int execdomains_read_proc(char *p
 	return proc_calc_metrics(page, start, off, count, eof, len);
 }
 
-/*
- * This function accesses profiling information. The returned data is
- * binary: the sampling step and the actual contents of the profile
- * buffer. Use of the program readprofile is recommended in order to
- * get meaningful info out of these data.
- */
-static ssize_t
-read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
-	unsigned long p = *ppos;
-	ssize_t read;
-	char * pnt;
-	unsigned int sample_step = 1 << prof_shift;
-
-	if (p >= (prof_len+1)*sizeof(unsigned int))
-		return 0;
-	if (count > (prof_len+1)*sizeof(unsigned int) - p)
-		count = (prof_len+1)*sizeof(unsigned int) - p;
-	read = 0;
-
-	while (p < sizeof(unsigned int) && count > 0) {
-		put_user(*((char *)(&sample_step)+p),buf);
-		buf++; p++; count--; read++;
-	}
-	pnt = (char *)prof_buffer + p - sizeof(unsigned int);
-	if (copy_to_user(buf,(void *)pnt,count))
-		return -EFAULT;
-	read += count;
-	*ppos += read;
-	return read;
-}
-
-/*
- * Writing to /proc/profile resets the counters
- *
- * Writing a 'profiling multiplier' value into it also re-sets the profiling
- * interrupt frequency, on architectures that support this.
- */
-static ssize_t write_profile(struct file *file, const char __user *buf,
-			     size_t count, loff_t *ppos)
-{
-#ifdef CONFIG_SMP
-	extern int setup_profiling_timer (unsigned int multiplier);
-
-	if (count == sizeof(int)) {
-		unsigned int multiplier;
-
-		if (copy_from_user(&multiplier, buf, sizeof(int)))
-			return -EFAULT;
-
-		if (setup_profiling_timer(multiplier))
-			return -EINVAL;
-	}
-#endif
-
-	memset(prof_buffer, 0, prof_len * sizeof(*prof_buffer));
-	return count;
-}
-
-static struct file_operations proc_profile_operations = {
-	.read		= read_profile,
-	.write		= write_profile,
-};
-
 #ifdef CONFIG_MAGIC_SYSRQ
 /*
  * writing 'C' to /proc/sysrq-trigger is like sysrq-C
@@ -643,6 +579,10 @@ static struct file_operations proc_sysrq
 
 struct proc_dir_entry *proc_root_kcore;
 
+#ifdef CONFIG_MODULE_PROFILE	
+struct proc_dir_entry *proc_root_mprof;
+#endif
+
 static void create_seq_entry(char *name, mode_t mode, struct file_operations *f)
 {
 	struct proc_dir_entry *entry;
@@ -707,11 +647,15 @@ void __init proc_misc_init(void)
 	}
 #endif
 	if (prof_on) {
+		extern struct file_operations proc_profile_operations;
 		entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);
 		if (entry) {
 			entry->proc_fops = &proc_profile_operations;
 			entry->size = (1+prof_len) * sizeof(unsigned int);
 		}
+#ifdef CONFIG_MODULE_PROFILE	
+		proc_root_mprof = proc_mkdir("mprof", 0);
+#endif
 	}
 #ifdef CONFIG_MAGIC_SYSRQ
 	entry = create_proc_entry("sysrq-trigger", S_IWUSR, NULL);
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/include/asm-i386/hw_irq.h linux-266-modprof/include/asm-i386/hw_irq.h
--- linux-266-pv/include/asm-i386/hw_irq.h	2004-05-09 19:33:21.000000000 -0700
+++ linux-266-modprof/include/asm-i386/hw_irq.h	2004-05-10 12:35:07.000000000 -0700
@@ -77,15 +77,20 @@ extern atomic_t irq_mis_count;
 static inline void x86_do_profile(struct pt_regs * regs)
 {
 	unsigned long eip;
-	extern unsigned long prof_cpu_mask;
+#ifdef CONFIG_MODULE_PROFILE
+	unsigned int * targ_prof_buffer;
+	unsigned long idx;
+#endif
  
 	profile_hook(regs);
  
-	if (user_mode(regs))
+	if (user_mode(regs)) {
 		return;
+	}
  
-	if (!prof_buffer)
+	if (!prof_buffer) {
 		return;
+	}
 
 	eip = regs->eip;
  
@@ -93,19 +98,30 @@ static inline void x86_do_profile(struct
 	 * Only measure the CPUs specified by /proc/irq/prof_cpu_mask.
 	 * (default is all CPUs.)
 	 */
-	if (!((1<<smp_processor_id()) & prof_cpu_mask))
+	if (!((1<<smp_processor_id()) & prof_cpu_mask)) {
 		return;
+	}
 
-	eip -= (unsigned long)_stext;
+#ifdef CONFIG_MODULE_PROFILE
+	/*
+	 * get profiling buffer address and offset
+	 * @pc	: program counter
+	 * @idx	: profiling buffer offset
+	 */
+	targ_prof_buffer = srch_prof_buffer(eip, &idx);
+	atomic_inc((atomic_t *)&targ_prof_buffer[idx]);
+#else
+	eip -= (unsigned long) &_stext;
 	eip >>= prof_shift;
 	/*
 	 * Don't ignore out-of-bounds EIP values silently,
 	 * put them into the last histogram slot, so if
 	 * present, they will show up as a sharp peak.
 	 */
-	if (eip > prof_len-1)
-		eip = prof_len-1;
+	if (eip > prof_len - 1)
+		eip = prof_len - 1;
 	atomic_inc((atomic_t *)&prof_buffer[eip]);
+#endif
 }
  
 #if defined(CONFIG_X86_IO_APIC)
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/include/linux/proc_fs.h linux-266-modprof/include/linux/proc_fs.h
--- linux-266-pv/include/linux/proc_fs.h	2004-05-09 19:33:22.000000000 -0700
+++ linux-266-modprof/include/linux/proc_fs.h	2004-05-10 12:35:07.000000000 -0700
@@ -82,6 +82,7 @@ extern struct proc_dir_entry *proc_net;
 extern struct proc_dir_entry *proc_bus;
 extern struct proc_dir_entry *proc_root_driver;
 extern struct proc_dir_entry *proc_root_kcore;
+extern struct proc_dir_entry *proc_root_mprof;
 
 extern void proc_root_init(void);
 extern void proc_misc_init(void);
@@ -114,6 +115,7 @@ extern struct dentry *proc_lookup(struct
 extern struct file_operations proc_kcore_operations;
 extern struct file_operations proc_kmsg_operations;
 extern struct file_operations ppc_htab_operations;
+extern struct file_operations proc_mprof_operations;
 
 /*
  * proc_tty.c
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/include/linux/profile.h linux-266-modprof/include/linux/profile.h
--- linux-266-pv/include/linux/profile.h	2004-05-09 19:32:29.000000000 -0700
+++ linux-266-modprof/include/linux/profile.h	2004-05-12 15:53:36.000000000 -0700
@@ -6,7 +6,11 @@
 #include <linux/kernel.h>
 #include <linux/config.h>
 #include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+///#include <linux/stddef.h>
 #include <asm/errno.h>
+#include <asm-generic/sections.h>
 
 /* parse command line */
 int __init profile_setup(char * str);
@@ -18,7 +22,7 @@ extern unsigned int * prof_buffer;
 extern unsigned long prof_len;
 extern unsigned long prof_shift;
 extern int prof_on;
-
+extern unsigned long prof_cpu_mask;
 
 enum profile_type {
 	EXIT_TASK,
@@ -32,6 +36,28 @@ struct notifier_block;
 struct task_struct;
 struct mm_struct;
 
+struct module_profile {
+	char *proc_name;
+	struct list_head list;
+	unsigned long prof_len;
+	unsigned long start_text;
+	unsigned long end_text;
+	unsigned int *prof_buffer;
+	struct module *module;
+};
+
+/* module profile root information */
+struct mprof_info {
+	rwlock_t rwlock;
+	struct list_head mprof_head;
+};
+
+extern struct mprof_info *module_profile_info;
+
+char *get_profile_name(struct module *mod);
+int create_module_profile(struct module *mod);
+void delete_module_profile(struct module *mod);
+
 /* task is in do_exit() */
 void profile_exit_task(struct task_struct * task);
 
@@ -53,6 +79,62 @@ struct pt_regs;
 /* profiling hook activated on each timer interrupt */
 void profile_hook(struct pt_regs * regs);
 
+/*
+ * called by profiling functions
+ */
+static inline unsigned int *
+srch_module_prof_buffer(unsigned long pc, unsigned long *idx)
+{
+	struct list_head *tmp;
+	struct module_profile *mprof;
+
+	/*
+	 * look for module_profile_buffer address with PC
+	 * into module_profile list chains
+	 */
+	list_for_each(tmp, &module_profile_info->mprof_head) {
+		mprof = list_entry(tmp, struct module_profile, list);
+
+		if (mprof->start_text <= pc && pc < mprof->end_text) { /* hit */
+			unsigned long rel_pc = pc - mprof->start_text;
+			*idx = rel_pc >> prof_shift;
+			return mprof->prof_buffer;
+		}
+	}
+
+	/* mprof == NULL: no module profiling buffer */
+	*idx = prof_len - 2;
+	return prof_buffer;
+}
+
+static inline unsigned int *
+srch_prof_buffer(unsigned long pc, unsigned long *idx)
+{
+	unsigned int *targ_prof_buffer = prof_buffer;
+
+	/*
+	 * Don't ignore out-of-bounds PC values silently,
+	 * put them into the last histogram slot, so if
+	 * present, they will show up as a sharp peak.
+	 */
+	if (pc < (unsigned long)&_stext) {
+		/* before kernel .text, could be in a module */
+		targ_prof_buffer = srch_module_prof_buffer(pc, idx);
+	}
+	else if (pc < (unsigned long)&_etext) { /* kernel profile */
+		unsigned long rel_pc = pc - (unsigned long)&_stext;
+		*idx = rel_pc >> prof_shift;
+	}
+	else if (pc < (unsigned long)&__init_end) { /* kernel init/exit code */
+		/* out of kernel .text, and out of module .text */
+		*idx = prof_len - 1;
+	}
+	else {
+		targ_prof_buffer = srch_module_prof_buffer(pc, idx);
+	}
+	return targ_prof_buffer;
+}
+
 #else
 
 static inline int profile_event_register(enum profile_type t, struct notifier_block * n)
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/kernel/kallsyms.c linux-266-modprof/kernel/kallsyms.c
--- linux-266-pv/kernel/kallsyms.c	2004-05-09 19:33:21.000000000 -0700
+++ linux-266-modprof/kernel/kallsyms.c	2004-05-10 12:35:07.000000000 -0700
@@ -171,21 +171,23 @@ static int get_ksymbol_mod(struct kallsy
 	return 1;
 }
 
-static void get_ksymbol_core(struct kallsym_iter *iter)
+/* Returns space to next name. */
+static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
 {
-	unsigned stemlen;
+	unsigned stemlen, off = iter->nameoff;
 
 	/* First char of each symbol name indicates prefix length
 	   shared with previous name (stem compression). */
-	stemlen = kallsyms_names[iter->nameoff++];
+	stemlen = kallsyms_names[off++];
 
-	strlcpy(iter->name+stemlen, kallsyms_names+iter->nameoff, 128-stemlen);
-	iter->nameoff += strlen(kallsyms_names + iter->nameoff) + 1;
+	strlcpy(iter->name+stemlen, kallsyms_names + off, 128-stemlen);
+	off += strlen(kallsyms_names + off) + 1;
 	iter->owner = NULL;
 	iter->value = kallsyms_addresses[iter->pos];
 	iter->type = 't';
 
 	upcase_if_global(iter);
+	return off - iter->nameoff;
 }
 
 static void reset_iter(struct kallsym_iter *iter)
@@ -210,9 +212,10 @@ static int update_iter(struct kallsym_it
 
 	/* We need to iterate through the previous symbols: can be slow */
 	for (; iter->pos != pos; iter->pos++) {
-		get_ksymbol_core(iter);
+		iter->nameoff += get_ksymbol_core(iter);
 		cond_resched();
 	}
+	get_ksymbol_core(iter);
 	return 1;
 }
 
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/kernel/Makefile linux-266-modprof/kernel/Makefile
--- linux-266-pv/kernel/Makefile	2004-05-09 19:32:02.000000000 -0700
+++ linux-266-modprof/kernel/Makefile	2004-05-10 12:35:07.000000000 -0700
@@ -7,7 +7,7 @@ obj-y     = sched.o fork.o exec_domain.o
 	    sysctl.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o workqueue.o pid.o \
 	    rcupdate.o intermodule.o extable.o params.o posix-timers.o \
-	    kthread.o
+	    kthread.o profile.o
 
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/kernel/module.c linux-266-modprof/kernel/module.c
--- linux-266-pv/kernel/module.c	2004-05-09 19:32:54.000000000 -0700
+++ linux-266-modprof/kernel/module.c	2004-05-10 12:35:07.000000000 -0700
@@ -33,6 +33,7 @@
 #include <linux/err.h>
 #include <linux/vermagic.h>
 #include <linux/notifier.h>
+#include <linux/profile.h>
 #include <linux/stop_machine.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
@@ -947,6 +948,10 @@ static unsigned long resolve_symbol(Elf_
 /* Free a module, remove from lists, etc (must hold module mutex). */
 static void free_module(struct module *mod)
 {
+#ifdef CONFIG_MODULE_PROFILE
+	delete_module_profile(mod);
+#endif
+
 	/* Delete from various lists */
 	spin_lock_irq(&modlist_lock);
 	list_del(&mod->list);
@@ -1559,6 +1564,14 @@ static struct module *load_module(void _
 	if (err < 0)
 		goto arch_cleanup;
 
+#ifdef CONFIG_MODULE_PROFILE
+	if (create_module_profile(mod)) {
+		printk(KERN_WARNING "%s: creation of module "
+			"profiling buffer failed for module(%s).\n",
+			__FUNCTION__, mod->name);
+	}
+#endif
+
 	/* Get rid of temporary copy */
 	vfree(hdr);
 
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/kernel/profile.c linux-266-modprof/kernel/profile.c
--- linux-266-pv/kernel/profile.c	2004-05-09 19:33:20.000000000 -0700
+++ linux-266-modprof/kernel/profile.c	2004-05-10 12:35:07.000000000 -0700
@@ -8,7 +8,13 @@
 #include <linux/bootmem.h>
 #include <linux/notifier.h>
 #include <linux/mm.h>
+///#include <linux/slab.h>
+///#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
 #include <asm/sections.h>
+#include <asm/uaccess.h>
 
 unsigned int * prof_buffer;
 unsigned long prof_len;
@@ -91,10 +97,8 @@ int profile_event_register(enum profile_
 	}
  
 	up_write(&profile_rwsem);
- 
 	return err;
 }
-
  
 int profile_event_unregister(enum profile_type type, struct notifier_block * n)
 {
@@ -151,6 +155,408 @@ void profile_hook(struct pt_regs * regs)
 EXPORT_SYMBOL_GPL(register_profile_notifier);
 EXPORT_SYMBOL_GPL(unregister_profile_notifier);
 
+#ifdef CONFIG_MODULE_PROFILE
+
+static spinlock_t module_profile_lock = SPIN_LOCK_UNLOCKED;
+
+static struct mprof_info mprof_info = {
+	.rwlock =	RW_LOCK_UNLOCKED,
+	.mprof_head =	LIST_HEAD_INIT(mprof_info.mprof_head),
+};
+struct mprof_info *module_profile_info = &mprof_info;
+
+/*
+ * determine profile name by given module,
+ * alloc memory for profile name, and set profile name
+ */
+char *get_profile_name(struct module *mod)
+{
+	char *proc_name;
+	int l;
+
+	/* construct module-profile-name */
+	l = strlen(mod->name)	/* module name */
+		+ 8;		/* "_profile" */
+	proc_name = kmalloc(l + 1, GFP_KERNEL);
+	if (proc_name)
+		snprintf(proc_name, l+1, "%s_profile", mod->name);
+	return proc_name;
+}
+
+/*
+ * create module profile for specified module
+ */
+int create_module_profile(struct module *mod)
+{
+	const struct kernel_symbol *sym;
+	char *name_prefix;
+	int i, l;
+	struct list_head *mprof_head, *tmp;
+	struct module_profile *new_mprof, *mprof;
+	struct proc_dir_entry *entry;
+	long error = 0;
+	unsigned long flags, buffer_size;
+	const char symprefix[] = "__insmod_";
+
+	if (proc_root_mprof == NULL) {
+		/* profile maybe not specified on boot option */
+		goto err0;
+	}
+
+	/* get memory for struct module_profile */
+	new_mprof = kmalloc(sizeof(struct module_profile), GFP_KERNEL);
+	if (new_mprof == NULL) {
+		error = -ENOMEM;
+		goto err0;
+	}
+	memset(new_mprof, 0, sizeof(*new_mprof));
+
+	/*
+	 * research .text start address and .text size in ksym,
+	 * set module profiling information for this module in module_profile.
+	 */
+	l = sizeof(symprefix) - 1	/* "__insmod_" (have NULL) */
+		+ strlen(mod->name)	/* module name */
+		+ 2			/* "_S" */
+		+ 5			/* section name".text" */
+		+ 2;			/* "_L" */
+	name_prefix = kmalloc(l + 1, GFP_KERNEL);
+	if (name_prefix == NULL) {
+		error = -ENOMEM;
+		goto err1;
+	}
+	snprintf(name_prefix, l+1, "%s%s_S.text_L", symprefix, mod->name);
+
+	for (i = mod->num_syms, sym = mod->syms; i > 0; --i, ++sym) {
+
+		if (strncmp(sym->name, name_prefix, l) == 0) {
+			unsigned long text_size;
+
+			text_size = simple_strtoul(sym->name + l, NULL, 10);
+
+			/* check .text size in ksym */
+			if (text_size > mod->core_size) { /* bug? */
+				printk(KERN_ERR
+					"%s: Invalid .text size in ksyms '%s'.\n",
+					__FUNCTION__, name_prefix);
+				kfree(name_prefix); /* temporary field */
+				error = -EINVAL;
+				goto err1;
+			}
+
+			/* set .text start address, .text size */
+			new_mprof->start_text = sym->value;
+			new_mprof->end_text = sym->value + text_size;
+			break;
+		}
+	}
+
+	/* if symbol "%s%s_S.text_L%d" is not registed in ksym,
+	   look on whole module as .text */
+	if (new_mprof->start_text == 0) {
+		printk(KERN_DEBUG "modprof: defaulting to entire module as .text:\n");
+#if 0
+		new_mprof->start_text = *((unsigned long *)&mod);
+		new_mprof->end_text = *((unsigned long *)&mod)
+					+ mod->core_size
+					/*- sizeof(struct module)*/;
+#endif
+#if 1
+		new_mprof->start_text = (unsigned long)mod->module_core;
+		new_mprof->end_text = (unsigned long)mod->module_core
+					+ mod->core_size
+					/*- sizeof(struct module)*/;
+#endif
+	}
+	/* determine module profile buffer size */
+	new_mprof->prof_len =
+		((new_mprof->end_text - new_mprof->start_text) >> prof_shift);
+	new_mprof->module = mod;
+	kfree(name_prefix);	/* temporary field */
+	buffer_size = new_mprof->prof_len * sizeof(unsigned int);
+
+	/*
+	 * alloc memory for module_profiling buffer
+	 */
+	new_mprof->prof_buffer = vmalloc(buffer_size);
+	if (new_mprof->prof_buffer == NULL) {
+		error = -ENOMEM;
+		goto err1;
+	}
+	memset(new_mprof->prof_buffer, 0, buffer_size);
+
+	/*
+	 * create /proc/mprof/$(mod->name)_profile
+	*/
+	new_mprof->proc_name = get_profile_name(mod);
+	if (new_mprof->proc_name == NULL) {
+		error = -ENOMEM;
+		goto err2;
+	}
+
+	/* create proc-entry /proc/mprof/$(mod->name)_profile */
+	entry = create_proc_entry(new_mprof->proc_name, S_IWUSR|S_IRUGO,
+					proc_root_mprof);
+	if (entry) {
+		entry->proc_fops = &proc_mprof_operations;
+		entry->size = buffer_size;
+	} else {
+		error = -ENOMEM;
+		/* in other cause, incorrect parameter */
+		goto err3;
+	}
+
+	/*
+	 * insert new module_profile into module_profile chains
+	 */
+	mprof_head = &module_profile_info->mprof_head;
+	write_lock(&module_profile_info->rwlock);
+
+	list_for_each(tmp, mprof_head) {
+		mprof = list_entry(tmp, struct module_profile, list);
+
+		if (new_mprof->end_text < mprof->end_text) {
+			/* insert previous module_profile */
+			spin_lock_irqsave(&module_profile_lock, flags);
+			list_add(&new_mprof->list, &mprof->list);
+			spin_unlock_irqrestore(&module_profile_lock, flags);
+			break;
+		}
+	}
+	if (tmp == mprof_head) {
+		/* if not hit, place new_prof at tail of chain */
+		spin_lock_irqsave(&module_profile_lock, flags);
+		list_add_tail(&new_mprof->list, mprof_head);
+		spin_unlock_irqrestore(&module_profile_lock, flags);
+	}
+
+	write_unlock(&module_profile_info->rwlock);
+
+	/* normal end */
+	goto err0;
+
+err3:
+	kfree(new_mprof->proc_name);
+err2:
+	vfree(new_mprof->prof_buffer);
+err1:
+	kfree(new_mprof);
+err0:
+	return error;
+}
+
+/*
+ * delete module profiling buffer
+ */
+void delete_module_profile(struct module *mod)
+{
+	struct list_head *mprof_head, *tmp;
+	struct module_profile *mprof = NULL;
+	unsigned long flags;
+	size_t name_len;
+
+	if (proc_root_mprof == NULL) {
+		/* profile maybe not specified on boot option */
+		return;
+	}
+
+	name_len = strlen(mod->name);
+
+	/* check module profile */
+	mprof_head = &module_profile_info->mprof_head;
+	read_lock(&module_profile_info->rwlock);
+
+	list_for_each(tmp, mprof_head) {
+		mprof = list_entry(tmp, struct module_profile, list);
+		if (strncmp(mprof->proc_name, mod->name, name_len) == 0 &&
+		    strcmp(mprof->proc_name + name_len, "_profile") == 0) {
+			/* find module profiling buffer ! */
+			break;
+		}
+	}
+
+	read_unlock(&module_profile_info->rwlock);
+
+	/* no module profiling buffer */
+	if (tmp == mprof_head) {
+		printk(KERN_WARNING "delete_module_profile: "
+			"no profiling buffer for module(%s).\n", mod->name);
+		return;
+	}
+
+	/* remove proc-file */
+	remove_proc_entry(mprof->proc_name, proc_root_mprof);
+
+	/* unchain module_profile */
+	write_lock(&module_profile_info->rwlock);
+	spin_lock_irqsave(&module_profile_lock, flags);
+
+	list_del(&mprof->list);
+
+	spin_unlock_irqrestore(&module_profile_lock, flags);
+	write_unlock(&module_profile_info->rwlock);
+
+	/* free memory */
+	kfree(mprof->proc_name);
+	vfree(mprof->prof_buffer);
+	kfree(mprof);
+}
+
+/*
+ * Caller must lock rwlock field into module_profile_info,
+ * for read or write operation.
+ */
+static struct module_profile *search_module_profile(char *profile_name)
+{
+	struct list_head *mprof_head, *tmp;
+	struct module_profile *mprof = NULL;
+
+	mprof_head = &module_profile_info->mprof_head;
+	list_for_each(tmp, mprof_head) {
+		mprof = list_entry(tmp, struct module_profile, list);
+
+		if (strcmp(mprof->proc_name, profile_name) == 0)
+			break;
+	}
+	if (tmp == mprof_head)
+		mprof = NULL;
+	return mprof;
+}
+
+/*
+ * This function accesses module profiling information. The returned data
+ * is binary: the actual contents of the profile buffer.
+ * Use of the program readprofile is recommended in order to
+ * get meaningful info out of these data.
+ */
+static ssize_t read_module_profile(struct file *file, char *buf,
+					size_t count, loff_t *ppos)
+{
+	unsigned long p = *ppos;
+	struct module_profile *mprof;
+
+	read_lock(&module_profile_info->rwlock);
+
+	/* search module profiling buffer */
+	mprof = search_module_profile((char *)file->f_dentry->d_name.name);
+	if (!mprof) {
+		printk(KERN_DEBUG "read_module_profile: search_module_profile failed\n");
+		/* module profile is already removed ? */
+		read_unlock(&module_profile_info->rwlock);
+		return -ENXIO;
+	}
+
+	/* check file offset */
+	if (p >= (mprof->prof_len * sizeof(unsigned int))) {
+		read_unlock(&module_profile_info->rwlock);
+		return 0;
+	}
+
+	/* copy profiling contents */
+	if (count > (mprof->prof_len * sizeof(unsigned int) - p))
+		count = mprof->prof_len * sizeof(unsigned int) - p;
+
+	copy_to_user(buf, (char *)mprof->prof_buffer + p, count);
+
+	read_unlock(&module_profile_info->rwlock);
+
+	*ppos += count;
+	return count;
+}
+
+static void reset_module_profiles(void)
+{
+	struct module_profile *mprof;
+	struct list_head *mprof_head, *tmp;
+
+	mprof_head = &module_profile_info->mprof_head;
+
+	write_lock(&module_profile_info->rwlock);
+
+	list_for_each(tmp, mprof_head) {
+		mprof = list_entry(tmp, struct module_profile, list);
+
+		/* zero-clear module profiling buffer */
+		memset(mprof->prof_buffer, 0,
+			mprof->prof_len * sizeof(*mprof->prof_buffer));
+	}
+
+	write_unlock(&module_profile_info->rwlock);
+}
+
+struct file_operations proc_mprof_operations = {
+	.read =		read_module_profile,
+};
+
+#endif	/* CONFIG_MODULE_PROFILE */
+
+/*
+ * This function accesses profiling information. The returned data is
+ * binary: the sampling step and the actual contents of the profile
+ * buffer. Use of the program readprofile is recommended in order to
+ * get meaningful info out of these data.
+ */
+ssize_t read_profile(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	unsigned long p = *ppos;
+	ssize_t read;
+	char * pnt;
+	unsigned int sample_step = 1 << prof_shift;
+
+	if (p >= (prof_len + 1) * sizeof(unsigned int))
+		return 0;
+	if (count > (prof_len + 1) * sizeof(unsigned int) - p)
+		count = (prof_len + 1) * sizeof(unsigned int) - p;
+	read = 0;
+
+	while (p < sizeof(unsigned int) && count > 0) {
+		put_user(*((char *)(&sample_step) + p), buf);
+		buf++; p++; count--; read++;
+	}
+	pnt = (char *)prof_buffer + p - sizeof(unsigned int);
+	copy_to_user(buf,(void *)pnt, count);
+	read += count;
+	*ppos += read;
+	return read;
+}
+
+/*
+ * Writing to /proc/profile resets the counters
+ *
+ * Writing a 'profiling multiplier' value into it also re-sets the profiling
+ * interrupt frequency, on architectures that support this.
+ */
+ssize_t write_profile(struct file * file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+#ifdef CONFIG_SMP
+	extern int setup_profiling_timer (unsigned int multiplier);
+
+	if (count == sizeof(int)) {
+		unsigned int multiplier;
+
+		if (copy_from_user(&multiplier, buf, sizeof(int)))
+			return -EFAULT;
+
+		if (setup_profiling_timer(multiplier))
+			return -EINVAL;
+	}
+#endif
+
+	memset(prof_buffer, 0, prof_len * sizeof(*prof_buffer));
+
+#ifdef CONFIG_MODULE_PROFILE
+	reset_module_profiles();
+#endif
+	return count;
+}
+
+struct file_operations proc_profile_operations = {
+	.read =		read_profile,
+	.write =	write_profile,
+};
+
 #endif /* CONFIG_PROFILING */
 
 EXPORT_SYMBOL_GPL(profile_event_register);
diff -Naurp -X /home/rddunlap/doc/dontdiff-osdl linux-266-pv/scripts/kallsyms.c linux-266-modprof/scripts/kallsyms.c
--- linux-266-pv/scripts/kallsyms.c	2004-05-09 19:33:21.000000000 -0700
+++ linux-266-modprof/scripts/kallsyms.c	2004-05-10 16:01:02.000000000 -0700
@@ -115,7 +115,10 @@ write_src(void)
 		if (!symbol_valid(&table[i]))
 			continue;
 		
-		if (table[i].addr == last_addr)
+		if (table[i].addr == last_addr &&
+		    last_addr != _etext)
+			/* don't duplicate addresses, except always
+			 * make sure that _etext is in kallsyms */
 			continue;
 
 		printf("\tPTR\t%#llx\n", table[i].addr);
@@ -140,7 +143,8 @@ write_src(void)
 		if (!symbol_valid(&table[i]))
 			continue;
 		
-		if (table[i].addr == last_addr)
+		if (table[i].addr == last_addr &&
+		    last_addr != _etext)
 			continue;
 
 		for (k = 0; table[i].sym[k] && table[i].sym[k] == prev[k]; ++k)
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Copyright © 2004, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds