2002-03-20 08:05:59

by hirao

[permalink] [raw]
Subject: [PATCH] enhance kernel profiling to loadable modules

Hi.

These are a kernel patch I wrote to enhance kernel profiling to loadable modules and
a patch which adds a new command READPROFILE to util-linux.
The kernel patch is for v2.4.18, and the command patch is for util-linux-2.11o.


===============================================================================
Installation
===============================================================================

1. Kernel

1.1) Apply kernel patch.

$ patch -p1 -d linux < moduleprofile-v0.3-v2.4.18.patch

1.2) Enable module profile.

Set Menu [Kernel Hacking][Module profiling support] to Y.

1.3) Set kernel boot option.

When you use LILO for the boot loader, add append="profile=N" just same
as original profiler.

2. Command

2.1) Apply util-linux patch.

$ patch -p1 -d util-linux < readprofile2-v0.2-v2.11o.patch

===============================================================================
Retrieving profile
===============================================================================

The kernel profile is retrieved by a new command READPROFILE.

When the system map of the kernel is /usr/src/linux/System.map, execute command

$ READPROFILE

or specify mapfile

$ READPROFILE -m mapfile

Excerpt of execution example.
---------------------------------------------------------------------->
4 rest_init 0.0476

13 [jbd]start_this_handle 0.0361

3 [ext3]ext3_get_group_desc 0.0234


total:
1326 kernel 0.0012
673 jbd 0.0246
396 ext3 0.0091
<----------------------------------------------------------------------

begin of moduleprofile-v0.3-v2.4.18.patch --->

diff -Naur linux-2.4.18/arch/i386/config.in
linux-2.4.18new/arch/i386/config.in
--- linux-2.4.18/arch/i386/config.in Tue Feb 26 04:37:52 2002
+++ linux-2.4.18new/arch/i386/config.in Mon Mar 18 11:31:24 2002
@@ -422,6 +422,7 @@
bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ
bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK
bool ' Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE
+ bool ' module profiling support' CONFIG_MODULE_PROFILE
fi

endmenu
diff -Naur linux-2.4.18/fs/proc/proc_misc.c linux-2.4.18new/fs/proc/proc_misc.c
--- linux-2.4.18/fs/proc/proc_misc.c Wed Nov 21 14:29:09 2001
+++ linux-2.4.18new/fs/proc/proc_misc.c Mon Mar 18 11:31:24 2002
@@ -410,69 +410,6 @@
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 *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.
- */
-static ssize_t write_profile(struct file * file, const char * 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,
-};
-
extern struct seq_operations mounts_op;
static int mounts_open(struct inode *inode, struct file *file)
{
@@ -487,6 +424,10 @@

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;
@@ -547,11 +488,15 @@
(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
}
if (prof_shift) {
+ 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_PPC32
{
diff -Naur linux-2.4.18/include/asm-i386/hw_irq.h linux-2.4.18new/include/asm-i386/hw_irq.h
--- linux-2.4.18/include/asm-i386/hw_irq.h Fri Nov 23 04:46:18 2001
+++ linux-2.4.18new/include/asm-i386/hw_irq.h Mon Mar 18 11:31:24 2002
@@ -15,6 +15,7 @@
#include <linux/config.h>
#include <asm/atomic.h>
#include <asm/irq.h>
+#include <linux/profile.h>

/*
* IDT vectors usable for external interrupt sources start
@@ -180,17 +181,16 @@
"pushl $"#nr"-256\n\t" \
"jmp common_interrupt");

-extern unsigned long prof_cpu_mask;
-extern unsigned int * prof_buffer;
-extern unsigned long prof_len;
-extern unsigned long prof_shift;
-
/*
* x86 profiling function, SMP safe. We might want to do this in
* assembly totally?
*/
static inline void x86_do_profile (unsigned long eip)
{
+#ifdef CONFIG_MODULE_PROFILE
+ unsigned int * targ_prof_buffer;
+ unsigned long idx;
+#endif
if (!prof_buffer)
return;

@@ -201,16 +201,27 @@
if (!((1<<smp_processor_id()) & prof_cpu_mask))
return;

- eip -= (unsigned long) &_stext;
- eip >>= prof_shift;
+#ifdef CONFIG_MODULE_PROFILE
/*
- * 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.
+ * get profiling buffer address and offset
+ * @pc : program counter
+ * @idx : profiling buffer offset
*/
- if (eip > prof_len-1)
- eip = prof_len-1;
- atomic_inc((atomic_t *)&prof_buffer[eip]);
+ 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;
+ atomic_inc((atomic_t *)&prof_buffer[eip]);
+#endif
}

#ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */
diff -Naur linux-2.4.18/include/linux/proc_fs.h linux-2.4.18new/include/linux/proc_fs.h
--- linux-2.4.18/include/linux/proc_fs.h Fri Nov 23 04:46:23 2001
+++ linux-2.4.18new/include/linux/proc_fs.h Mon Mar 18 11:31:24 2002
@@ -82,6 +82,7 @@
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);
@@ -113,6 +114,7 @@
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 -Naur linux-2.4.18/include/linux/profile.h linux-2.4.18new/include/linux/profile.h
--- linux-2.4.18/include/linux/profile.h Thu Jan 1 09:00:00 1970
+++ linux-2.4.18new/include/linux/profile.h Mon Mar 18 11:31:24 2002
@@ -0,0 +1,100 @@
+/*
+ * kernel and module profile.
+ */
+
+#ifndef _LINUX_PROFILE_H
+#define _LINUX_PROFILE_H
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+extern char _stext, _etext;
+extern unsigned long prof_cpu_mask;
+extern unsigned int * prof_buffer;
+extern unsigned long prof_len;
+extern unsigned long prof_shift;
+
+
+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 infomation */
+struct mprof_info
+{
+ rwlock_t rwlock;
+ struct list_head mprof_head;
+};
+
+char *get_profile_name(struct module *mod);
+int create_module_profile(struct module *mod);
+void delete_module_profile(struct module *mod);
+
+extern struct mprof_info *module_profile_info;
+
+
+/*
+ * 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 */
+ pc -= mprof->start_text;
+ *idx = (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){
+ /* out of kernel .text, and out of module .text */
+ *idx = prof_len-1;
+ }else if(pc < (unsigned long)&_etext){
+ /* kernel profile */
+ pc -= (unsigned long) &_stext;
+ pc >>= prof_shift;
+ *idx = pc;
+ }else{
+ targ_prof_buffer = srch_module_prof_buffer(pc,idx);
+ }
+ return targ_prof_buffer;
+}
+
+#endif /* _LINUX_PROFILE_H */
+
diff -Naur linux-2.4.18/kernel/Makefile linux-2.4.18new/kernel/Makefile
--- linux-2.4.18/kernel/Makefile Mon Sep 17 13:22:40 2001
+++ linux-2.4.18new/kernel/Makefile Mon Mar 18 11:31:24 2002
@@ -14,7 +14,7 @@
obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
module.o exit.o itimer.o info.o time.o softirq.o resource.o \
sysctl.o acct.o capability.o ptrace.o timer.o user.o \
- signal.o sys.o kmod.o context.o
+ signal.o sys.o kmod.o context.o profile.o

obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += ksyms.o
diff -Naur linux-2.4.18/kernel/module.c linux-2.4.18new/kernel/module.c
--- linux-2.4.18/kernel/module.c Mon Nov 12 04:23:14 2001
+++ linux-2.4.18new/kernel/module.c Mon Mar 18 11:31:24 2002
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/seq_file.h>
+#include <linux/profile.h>

/*
* Originally by Anonymous (as far as I know...)
@@ -530,6 +531,13 @@
}
}

+#ifdef CONFIG_MODULE_PROFILE
+ if (create_module_profile(mod)) {
+ printk(KERN_WARNING "init_module: creation of module "
+ "profiling buffer failed for module(%s).\n", mod->name);
+ }
+#endif
+
/* Update module references. */
for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
struct module *d = dep->dep;
@@ -618,6 +626,9 @@

spin_lock(&unload_lock);
if (!__MOD_IN_USE(mod)) {
+#ifdef CONFIG_MODULE_PROFILE
+ delete_module_profile(mod);
+#endif
mod->flags |= MOD_DELETED;
spin_unlock(&unload_lock);
free_module(mod, 0);
@@ -646,6 +657,9 @@
spin_unlock(&unload_lock);
mod->flags &= ~MOD_VISITED;
} else {
+#ifdef CONFIG_MODULE_PROFILE
+ delete_module_profile(mod);
+#endif
mod->flags |= MOD_DELETED;
spin_unlock(&unload_lock);
free_module(mod, 1);
diff -Naur linux-2.4.18/kernel/profile.c linux-2.4.18new/kernel/profile.c
--- linux-2.4.18/kernel/profile.c Thu Jan 1 09:00:00 1970
+++ linux-2.4.18new/kernel/profile.c Mon Mar 18 11:31:24 2002
@@ -0,0 +1,412 @@
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/profile.h>
+
+
+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;
+
+
+#ifdef CONFIG_MODULE_PROFILE
+/*
+ * determine profile name by given module,
+ * alloc memoey 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)
+{
+ struct module_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->nsyms, 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->size) {
+ /* bug? */
+ printk(KERN_ERR
+ "create_mprofile: Invalid .text size in ksyms '%s'.\n",
+ 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) {
+ new_mprof->start_text = *((unsigned long *)&mod);
+ new_mprof->end_text = *((unsigned long *)&mod) +
+ mod->size - mod->size_of_struct;
+ }
+ /* 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);
+
+ return;
+}
+
+/*
+ * 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){
+ /* 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);
+
+ return;
+}
+
+struct file_operations proc_mprof_operations = {
+ read: read_module_profile,
+};
+#endif
+
+/*
+ * 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 *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 * 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,
+};

<-- end of moduleprofile-v0.3-v2.4.18.patch

begin of readprofile2-v0.2-v2.11o.patch --->

diff -Naur util-linux-2.11o/sys-utils/Makefile
util-linux-2.11onew/sys-utils/Makefile
--- util-linux-2.11o/sys-utils/Makefile Sat Nov 10 02:13:50 2001
+++ util-linux-2.11onew/sys-utils/Makefile Fri Mar 15 19:21:45 2002
@@ -21,7 +21,7 @@

USRBIN= cytune ipcrm ipcs renice setsid

-USRSBIN= readprofile tunelp
+USRSBIN= readprofile readprofile2 READPROFILE tunelp

SBIN= ctrlaltdel

@@ -64,6 +64,10 @@
nosln:
@echo sln not made since static compilation fails here

+%: %.sh
+ cp [email protected] $@
+ chmod 755 $@
+
# Rules for everything else

arch: arch.o
@@ -75,6 +79,7 @@
rdev: rdev.o
renice: renice.o
readprofile: readprofile.o
+readprofile2: readprofile2.o
setsid: setsid.o

ipc.info: ipc.texi
diff -Naur util-linux-2.11o/sys-utils/READPROFILE.sh util-linux-2.11onew/sys-utils/READPROFILE.sh
--- util-linux-2.11o/sys-utils/READPROFILE.sh Thu Jan 1 09:00:00 1970
+++ util-linux-2.11onew/sys-utils/READPROFILE.sh Fri Mar 15 19:21:45 2002
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+USRSBIN=/usr/sbin
+DEFAULTMAP="/usr/src/linux/System.map"
+NEWMAP="/tmp/System_incmod.map"
+
+map_cnv=yes
+while getopts ":m:p:M:ivarVt" opt; do
+ case $opt in
+ m ) old_map=$OPTARG
+ map_cnt=$(($map_cnt + 1))
+ new_args="$new_args -m $NEWMAP" ;;
+ p ) new_args="$new_args -$opt $OPTARG" ;;
+ v | a ) new_args="$new_args -$opt" ;;
+ M ) new_args="$new_args -$opt $OPTARG"
+ map_cnv=no ;;
+ * ) new_args="$new_args -$opt"
+ map_cnv=no ;;
+ esac
+done
+
+if [ $map_cnv = yes ]; then
+ if [ -z $old_map ]; then
+ if [ -f $DEFAULTMAP ]; then
+ ksymoops -s $NEWMAP -m $DEFAULTMAP < /dev/null &> /dev/null
+ new_args="$* -m $NEWMAP"
+ else
+ new_args=$*
+ fi
+ else
+ if [ $map_cnt = 1 ] && [ -f $old_map ]; then
+ ksymoops -s $NEWMAP -m $old_map < /dev/null &> /dev/null
+ else
+ new_args=$*
+ fi
+ fi
+else
+ new_args=$*
+fi
+
+readprofile2 $new_args
diff -Naur util-linux-2.11o/sys-utils/readprofile2.c util-linux-2.11onew/sys-utils/readprofile2.c
--- util-linux-2.11o/sys-utils/readprofile2.c Thu Jan 1 09:00:00 1970
+++ util-linux-2.11onew/sys-utils/readprofile2.c Fri Mar 15 19:21:45 2002
@@ -0,0 +1,651 @@
+/*
+ * readprofile2.c - used to read /proc/profile
+ * and /proc/mprof=/${module_name}_profile
+ *
+ * Copyright (C) 1994,1996 Alessandro Rubini ([email protected])
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Miカ・瘢雹ewicz <[email protected]>
+ * - added Native Language Support
+ * 1999-09-01 Stephane Eranian <[email protected]>
+ * - 64bit clean patch
+ * 3Feb2001 Andrew Morton <[email protected]>
+ * - -M option to write profile multiplier.
+ * 2102-01-20 Yoshinori Hirao <[email protected]>
+ * - added Module Profiling Support
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include "nls.h"
+
+#define RELEASE "2.0, May 1996"
+
+#define S_LEN 128
+
+#define TEST
+
+static char *prgname;
+
+/* These are the defaults */
+static char defaultmap[]="/usr/src/linux/System.map";
+static char defaultpro[]="/proc/profile";
+static char mprof_procdir[]="/proc/mprof";
+static char optstring[]="M:m:p:itvarV";
+
+struct profile_list
+{
+ struct profile_list *nxt;
+ struct profile_list *prv;
+ char *mod_name;
+ char *profile_name;
+ unsigned long len;
+ unsigned int *buf;
+ unsigned long start_text;
+ unsigned long cnt_total;
+ unsigned long cdsz_total;
+};
+
+static void
+usage(void) {
+ fprintf(stderr,
+ _("%s: Usage: \"%s [options]\n"
+ "\t -m <mapfile> (default = \"%s\")\n"
+ "\t -p <pro-file> (default = \"%s\")\n"
+ "\t -M <mult> set the profiling multiplier to <mult>\n"
+ "\t -i print only info about the sampling step\n"
+ "\t -v print verbose data\n"
+ "\t -a print all symbols, even if count is 0\n"
+ "\t -r reset all the counters (root only)\n"
+ "\t -V print version and exit\n")
+ ,prgname,prgname,defaultmap,defaultpro);
+ exit(1);
+}
+
+static void *
+xmalloc (size_t size) {
+ void *t;
+
+ if (size == 0)
+ return NULL;
+
+ t = malloc (size);
+ if (t == NULL) {
+ fprintf(stderr, _("out of memory"));
+ exit(1);
+ }
+
+ return t;
+}
+
+static FILE *
+myopen(char *name, char *mode, int *flag) {
+ int len = strlen(name);
+
+ if (!strcmp(name+len-3,".gz")) {
+ FILE *res;
+ char *cmdline = xmalloc(len+6);
+ sprintf(cmdline, "zcat %s", name);
+ res = popen(cmdline,mode);
+ free(cmdline);
+ *flag = 1;
+ return res;
+ }
+ *flag = 0;
+ return fopen(name,mode);
+}
+
+struct profile_list *proList = NULL;
+struct profile_list *doneList = NULL;
+
+static struct profile_list *
+create_profile_list(void)
+{
+ DIR *dir;
+ struct dirent *ent;
+ int proFd;
+ struct profile_list *profl = NULL, *list = NULL, *list_tail = NULL;
+ char *prgname ="create_profile_list";
+
+
+ dir = opendir(mprof_procdir);
+ if (!dir) {
+ /* module-profile is not invalid */
+ proList = list;
+ return list;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+
+ /* skip directory */
+ if (strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0) {
+ continue;
+ }
+
+ /*
+ * alloc memory for profile_list, chain by dir-entory order
+ */
+ profl = xmalloc(sizeof(struct profile_list));
+ memset(profl, 0, sizeof(struct profile_list));
+ if(list){
+ profl->prv = list_tail;
+ list_tail->nxt = profl;
+ }else{
+ list = profl;
+ list_tail = profl;
+ }
+ list_tail = profl;
+
+ /* profile_name */
+ profl->profile_name = xmalloc(sizeof(mprof_procdir) + 1 +
+ strlen(ent->d_name));
+ sprintf(profl->profile_name,"%s/%s", mprof_procdir,ent->d_name);
+
+ if ( ((proFd=open(profl->profile_name,O_RDONLY)) < 0)
+ || ((int)(profl->len=lseek(proFd,0,SEEK_END)) < 0)
+ || (lseek(proFd,0,SEEK_SET)<0) ) {
+ fprintf(stderr,"%s: %s: %s\n",
+ prgname,profl->profile_name,strerror(errno));
+ exit(1);
+ }
+
+ /* alloc memory for read buffer */
+ if (!(profl->buf = malloc(profl->len)) ) {
+ fprintf(stderr,"%s: malloc(): %s\n",
+ prgname, strerror(errno));
+ exit(1);
+ }
+ memset(profl->buf, 0, profl->len);
+
+ /* read profiling buffer */
+ if (read(proFd,profl->buf,profl->len) != profl->len) {
+ fprintf(stderr,"%s: %s: %s\n", prgname,
+ profl->profile_name,strerror(errno));
+ exit(1);
+ }
+
+ /* change pointer to profile,
+ * to search for these in main routine */
+ profl->profile_name += sizeof(mprof_procdir);
+
+ close(proFd);
+
+ }
+
+ closedir (dir);
+ proList = list;
+ return list;
+}
+
+static struct profile_list *
+rechain_profile_list(struct profile_list *profl)
+{
+ struct profile_list *c_prof;
+
+ /* unchain proList */
+ if (profl->prv){
+ profl->prv->nxt = profl->nxt;
+ }
+ if (profl->nxt){
+ profl->nxt->prv = profl->prv;
+ }
+ if (profl == proList){
+ proList = profl->nxt;
+ }
+
+ /* chain tail of doneList */
+ if (doneList) {
+ for (c_prof = doneList; c_prof->nxt != NULL;
+ c_prof = c_prof->nxt) {
+ ;
+ }
+ profl->prv = c_prof;
+ c_prof->nxt = profl;
+ } else {
+ profl->prv = NULL;
+ doneList = profl;
+ }
+ profl->nxt = NULL;
+
+ return proList;
+}
+
+int
+main (int argc, char **argv) {
+ FILE *map;
+ int proFd;
+ char *mapFile, *proFile, *mult=0;
+ unsigned long len=0, add0=0, indx=0;
+ unsigned int step;
+ unsigned int *buf, total, fn_len;
+ unsigned long fn_add, next_add; /* current and next address */
+ char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */
+ char module[S_LEN], last_module[S_LEN], m_fn_name[S_LEN];
+ char mode[8];
+ int c, i;
+ int optAll=0, optInfo=0, optReset=0, optVerbose=0;
+ char mapline[S_LEN];
+ int maplineno=0;
+ int popenMap; /* flag to tell if popen() has been used */
+ int modulemap_end;
+ struct profile_list *profl;
+#ifdef TEST
+ int optTest=0;
+#endif
+
+#define next (current^1)
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ prgname=argv[0];
+ proFile=defaultpro;
+ mapFile=defaultmap;
+
+ while ((c = getopt(argc,argv,optstring)) != -1) {
+ switch(c) {
+ case 'm': mapFile=optarg; break;
+ case 'p': proFile=optarg; break;
+ case 'a': optAll++; break;
+ case 'i': optInfo++; break;
+ case 'M': mult=optarg; break;
+ case 'r': optReset++; break;
+ case 'v': optVerbose++; break;
+ case 'V': printf(_("%s Version %s\n"),prgname,RELEASE);
+ exit(0);
+#ifdef TEST
+ case 't': optTest++; break;
+#endif
+ default: usage();
+ }
+ }
+
+ if (optReset || mult) {
+ int multiplier, fd, to_write;
+
+ /*
+ * When writing the multiplier, if the length of the write is
+ * not sizeof(int), the multiplier is not changed
+ */
+ if (mult) {
+ multiplier = strtoul(mult, 0, 10);
+ to_write = sizeof(int);
+ } else {
+ multiplier = 0;
+ to_write = 1; /* sth different from sizeof(int) */
+ }
+ /* try to become root, just in case */
+ setuid(0);
+ fd = open(defaultpro,O_WRONLY);
+ if (fd < 0) {
+ perror(defaultpro);
+ exit(1);
+ }
+ if (write(fd, &multiplier, to_write) != to_write) {
+ fprintf(stderr, "readprofile: error writing %s: %s\n",
+ defaultpro, strerror(errno));
+ exit(1);
+ }
+ close(fd);
+ exit(0);
+ }
+
+ /*
+ * Use an fd for the profiling buffer, to skip stdio overhead
+ */
+ if ( ((proFd=open(proFile,O_RDONLY)) < 0)
+ || ((int)(len=lseek(proFd,0,SEEK_END)) < 0)
+ || (lseek(proFd,0,SEEK_SET)<0) ) {
+ fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno));
+ exit(1);
+ }
+
+ if ( !(buf=malloc(len)) ) {
+ fprintf(stderr,"%s: malloc(): %s\n",prgname, strerror(errno));
+ exit(1);
+ }
+
+ if (read(proFd,buf,len) != len) {
+ fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno));
+ exit(1);
+ }
+ close(proFd);
+
+ step=buf[0];
+ if (optInfo) {
+ printf(_("Sampling_step: %i\n"),step);
+ exit(0);
+ }
+
+
+ /*
+ * create profiling buffer list
+ */
+ /* create kernel profililng buffer list */
+ profl = xmalloc(sizeof(struct profile_list));
+ memset(profl, 0, sizeof(struct profile_list));
+ profl->buf = buf;
+ profl->len = len;
+
+ /* create module profiling buffer list */
+ proList = create_profile_list();
+ if(proList){
+ /* link head of profiling buffer list chain */
+ profl->nxt = proList;
+ }
+ proList = profl;
+
+
+ if (!(map=myopen(mapFile,"r",&popenMap))) {
+ fprintf(stderr,"%s: ",prgname);perror(mapFile);
+ exit(1);
+ }
+
+ /*
+ * skip first line, because ksymoops make mapfile
+ * that set first line "00000001 Using_Versions"
+ */
+ fgets(mapline,S_LEN,map);
+ maplineno++;
+
+ while(fgets(mapline,S_LEN,map)) {
+ maplineno++;
+ if (sscanf(mapline,"%lx %s %s",&fn_add,mode,fn_name)!=3) {
+ fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
+ prgname,mapFile, maplineno);
+ exit(1);
+ }
+ if (!strcmp(fn_name,"_stext")) /* only elf works like this */ {
+ add0=fn_add;
+ break;
+ }
+ }
+
+ if (!add0) {
+ fprintf(stderr,_("%s: can't find \"_stext\" in %s\n"),
+ prgname, mapFile);
+ exit(1);
+ }
+
+ /*
+ * Main loop for reading kernel profiling.
+ */
+ total=0;
+ profl = proList;
+ while(fgets(mapline,S_LEN,map)) {
+ unsigned int this=0;
+
+ maplineno++;
+ if (sscanf(mapline,"%lx %s %s",&next_add,mode,next_name)!=3) {
+ fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
+ prgname,mapFile, maplineno);
+ exit(1);
+ }
+
+ /* ignore any LEADING (before a '[tT]' symbol is found)
+ Absolute symbols */
+ if (*mode == 'A' && total == 0) continue;
+ if (*mode!='T' && *mode!='t') break;/* only text is profiled */
+
+ if (indx >= profl->len / sizeof(*profl->buf)) {
+ fprintf(stderr, _("%s: profile address out of range. "
+ "Wrong map file?(%i)\n"), prgname,maplineno);
+ exit(1);
+ }
+
+ while (indx < (next_add-add0)/step)
+ this += profl->buf[indx++];
+ total += this;
+
+ fn_len = next_add-fn_add;
+ if (fn_len && (this || optAll)) {
+ if (optVerbose)
+#ifdef TEST
+ if (optTest) {
+ printf("%08lx %4.4x %-50s %6i %8.4f\n",
+ fn_add,fn_len,
+ fn_name,this,this/(double)fn_len);
+ } else {
+ printf("%08lx %-50s %6i %8.4f\n", fn_add,
+ fn_name,this,this/(double)fn_len);
+ }
+#else
+ printf("%08lx %-50s %6i %8.4f\n", fn_add,
+ fn_name,this,this/(double)fn_len);
+#endif
+ else
+ printf("%6i %-50s %8.4f\n",
+ this,fn_name,this/(double)fn_len);
+ }
+ fn_add=next_add; strcpy(fn_name,next_name);
+ }
+
+ profl->cnt_total = total;
+ profl->cdsz_total = (fn_add-add0);
+ proList = rechain_profile_list(profl);
+ /* set module name "kernel" */
+ profl->mod_name = xmalloc(sizeof("kernel"));
+ strcpy(profl->mod_name, "kernel");
+ if (!proList){
+ /* no module profiling buffer */
+ goto trailer;
+ }
+
+ /*
+ * skip remain part in kernel map
+ */
+ while(fgets(mapline,S_LEN,map)) {
+ maplineno++;
+
+ if (sscanf(mapline,"%lx %s %s %s",
+ &fn_add,mode,fn_name,module)!=3) {
+ /* kernel map end mark "_end" is not found */
+ fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
+ prgname,mapFile, maplineno);
+ exit(1);
+ }
+ if (!strcmp(fn_name,"_end")) {
+ break;
+ }
+ }
+
+
+ /*
+ * Main loop for reading module profiling.
+ */
+ modulemap_end = 0;
+ indx = 0;
+ total=0;
+ add0 = 0;
+ profl = proList;
+ memset(module, 0, sizeof(module));
+ memset(last_module, 0, sizeof(last_module));
+ while(fgets(mapline,S_LEN,map)) {
+ unsigned int this=0;
+
+ maplineno++;
+ if (sscanf(mapline,"%lx %s %s %s",
+ &next_add,mode,next_name,module)!=4) {
+ fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
+ prgname,mapFile, maplineno);
+ exit(1);
+ }
+
+ /*
+ * if module-name is not diffrent from last_module-name,
+ * then change profiling buffer.
+ */
+ if (strcmp(module, last_module)){
+ char *p;
+ int mod_name_len = 0;
+
+ /* module-name is put between '[' and ']' */
+ for (p = module+1; *p != ']'; p++){
+ mod_name_len++;
+ }
+
+ /* determine proc file name */
+ p = xmalloc(mod_name_len + sizeof("_profile"));
+ memcpy(p, module + 1, mod_name_len);
+ strcpy(p + mod_name_len, "_profile");
+
+ /* search profiling buffer for this module */
+ for (profl = proList; profl != NULL;
+ profl = profl->nxt){
+
+ if (strncmp(p, profl->profile_name,
+ strlen(p)+1) == 0){
+ break;
+ }
+ }
+ strcpy(last_module, module);
+ if (!profl){
+ /* no profiling buffer */
+ continue;
+ }
+
+ /* if profiling buffer for this module is found,
+ * then set module name in profile_list */
+ profl->mod_name = xmalloc(mod_name_len + 1);
+ memset(profl->mod_name, 0, mod_name_len + 1);
+ memcpy(profl->mod_name, module + 1, mod_name_len);
+ }
+
+ /*
+ * skip invalid lines on any mode[tTg...]
+ */
+ if (profl && !add0) {
+ modulemap_end = 0;
+ }
+
+ /* ignore any LEADING (before a '[tT]' symbol is found)
+ Absolute symbols */
+ if (*mode!='T' && *mode!='t'){
+ if (strcmp(next_name, ".text.end") == 0){
+ modulemap_end = 1;
+ } else if (strcmp(next_name, ".text.start") == 0){
+ add0 = next_add;
+ if (fn_add == next_add) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+
+ /* skip line, because .text start address is not found
+ or function name is not found */
+ if (!add0 ||
+ (strcmp(fn_name, "_end") == 0) ||
+ (strcmp(fn_name, ".text.end") == 0)) {
+ fn_add = next_add;
+ strcpy(fn_name, next_name);
+ continue;
+ }
+
+ if (!profl) {
+ fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
+ prgname,mapFile, maplineno);
+ exit(1);
+ }
+
+ if (indx >= profl->len / sizeof(*profl->buf)) {
+ fprintf(stderr, _("%s: profile address out of range. "
+ "Wrong map file?(%i)\n"), prgname,maplineno);
+ exit(1);
+ }
+
+ while (indx < (next_add-add0)/step) {
+ this += profl->buf[indx++];
+ }
+ total += this;
+
+ fn_len = next_add-fn_add;
+ if (fn_len && (this || optAll)) {
+ sprintf(m_fn_name,"%s%s", module,fn_name);
+ if (optVerbose)
+#ifdef TEST
+ if (optTest) {
+ printf("%08lx %4.4x %-50s %6i %8.4f\n",
+ fn_add,fn_len,
+ m_fn_name,this,this/(double)fn_len);
+ } else {
+ printf("%08lx %-50s %6i %8.4f\n", fn_add,
+ m_fn_name,this,this/(double)fn_len);
+ }
+#else
+ printf("%08lx %-50s %6i %8.4f\n", fn_add,
+ m_fn_name,this,this/(double)fn_len);
+#endif
+ else
+ printf("%6i %-50s %8.4f\n",
+ this,m_fn_name,this/(double)fn_len);
+ }
+
+ fn_add=next_add; strcpy(fn_name,next_name);
+ strcpy(last_module, module);
+
+ /*
+ * if rearch end of a module, save profiling count total
+ * function total code size, and initialize variables
+ */
+ if (modulemap_end) {
+
+ profl->cnt_total = total;
+ profl->cdsz_total = (fn_add-add0);
+ proList = rechain_profile_list(profl);
+
+ if (proList){
+ indx = 0;
+ total = 0;
+ add0 = 0;
+ }
+ profl = NULL;
+ }
+ }
+
+trailer:
+ /* trailer */
+ i = 0;
+ printf("\ntotal:\n");
+ for (profl = doneList; profl != NULL; profl = profl->nxt){
+ if (profl->cnt_total || optAll) {
+ if (optVerbose)
+ printf("%8d %-50s %6i %8.4f\n",
+ ++i,profl->mod_name,(int)profl->cnt_total,
+ profl->cnt_total/(double)profl->cdsz_total);
+ else
+ printf("%6i %-50s %8.4f\n",
+ (int)profl->cnt_total,profl->mod_name,
+ profl->cnt_total/(double)profl->cdsz_total);
+
+ }
+ }
+
+ popenMap ? pclose(map) : fclose(map);
+ exit(0);
+}

<-- end of readprofile2-v0.2-v2.11o.patch

--
hirao <[email protected]>


2002-03-21 05:05:22

by Keith Owens

[permalink] [raw]
Subject: Re: [PATCH] enhance kernel profiling to loadable modules

On Wed, 20 Mar 2002 17:04:08 +0900,
hirao <[email protected]> wrote:
>These are a kernel patch I wrote to enhance kernel profiling to loadable modules and
>a patch which adds a new command READPROFILE to util-linux.
>diff -Naur linux-2.4.18/kernel/module.c linux-2.4.18new/kernel/module.c
>--- linux-2.4.18/kernel/module.c Mon Nov 12 04:23:14 2001
>+++ linux-2.4.18new/kernel/module.c Mon Mar 18 11:31:24 2002
>@@ -10,6 +10,7 @@
> #include <linux/slab.h>
> #include <linux/kmod.h>
> #include <linux/seq_file.h>
>+#include <linux/profile.h>
>
> /*
> * Originally by Anonymous (as far as I know...)
>@@ -530,6 +531,13 @@
> }
> }
>
>+#ifdef CONFIG_MODULE_PROFILE
>+ if (create_module_profile(mod)) {
>+ printk(KERN_WARNING "init_module: creation of module "
>+ "profiling buffer failed for module(%s).\n", mod->name);
>+ }
>+#endif

Instead of patching kernel/module.c and maintaining a separate list of
module related profiling data, use the kernel_data field in struct
module. I added that field specificially so to track any kernel data
that relates to each module.

Change the module_arch_init() and free_module() functions in
include/asm-i386/module.h to allocate and free the kernel_data
structure during module load and unload. Take the profile lock in
those routines (disabling interrupts) when you update kernel_data.

To map a profile eip to a module, run the module list looking for the
address, see kernel_text_address() in arch/i386/kernel/traps.c for
example code.

With this approach, the mainline module code is unchanged, all arch
specific profile code is in include/asm-$(ARCH)/module.h.

Your srch_prof_buffer() algorithm is ix86 specific, you assume that
modules are always above the end of the kernel. That is not true on
all architectures. The correct method is to treat the kernel as just
another module and store the profile data for the kernel in
kernel_module.kernel_data, using arch_init_modules(). Running the
module list (which includes the kernel itself) will find the correct
address range in an architecture portable way. Add one extra slot to
the kernel profile table for out of range addresses.

2002-03-25 02:25:47

by hirao

[permalink] [raw]
Subject: Re: [PATCH] enhance kernel profiling to loadable modules

Hello, Keith
Thank you for the comment.

> Instead of patching kernel/module.c and maintaining a separate list of
> module related profiling data, use the kernel_data field in struct
> module. I added that field specificially so to track any kernel data
> that relates to each module.
It is necessary to use kernel_data field in struct module. I see.

> Change the module_arch_init() and free_module() functions in
> include/asm-i386/module.h to allocate and free the kernel_data
> structure during module load and unload. Take the profile lock in
> those routines (disabling interrupts) when you update kernel_data.
Certainly, I made patch for the x86 architecture.
However, I guessed that neither creation nor free of profiling buffer
would depend on architecture.
And, I had it write these processing (create_module_profile(),
delete_module_profile()) in kernel/profile.c, and call from module().
Isn't this idea correct?
If it is not correct, it is necessary to inpriment as processing of
the architecture dependence like your point it.

>
> To map a profile eip to a module, run the module list looking for the
> address, see kernel_text_address() in arch/i386/kernel/traps.c for
> example code.
>
> With this approach, the mainline module code is unchanged, all arch
> specific profile code is in include/asm-$(ARCH)/module.h.
>
> Your srch_prof_buffer() algorithm is ix86 specific, you assume that
> modules are always above the end of the kernel. That is not true on
> all architectures. The correct method is to treat the kernel as just
> another module and store the profile data for the kernel in
> kernel_module.kernel_data, using arch_init_modules(). Running the
> module list (which includes the kernel itself) will find the correct
> address range in an architecture portable way. Add one extra slot to
> the kernel profile table for out of range addresses.
It is so. Processing which specifies module from eip is corrected as
shown in the point.


--
hirao <[email protected]>