profiling: dynamically enable readprofile at runtime
Way too often, I have a machine that exhibits some kind of crappy behavior. The CPU looks wedged in the kernel or it is spending way too much system time and I wonder what is responsible. I try to run readprofile. But, of course, Ubuntu doesn't enable it by default. Dang! The reason we boot-time enable it is that it takes a big bufffer that we generally can only bootmem alloc. But, does it hurt to at least try and runtime-alloc it? To use: echo 2 > /sys/kernel/profile Then run readprofile like normal. This should fix the compile issue with allmodconfig. I've compile-tested on a bunch more configs now including a few more architectures. Signed-off-by: Dave Hansen <dave@linux.vnet.ibm.com> Acked-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
0c2d64fb6c
commit
22b8ce9470
|
@ -0,0 +1,13 @@
|
||||||
|
What: /sys/kernel/profile
|
||||||
|
Date: September 2008
|
||||||
|
Contact: Dave Hansen <dave@linux.vnet.ibm.com>
|
||||||
|
Description:
|
||||||
|
/sys/kernel/profile is the runtime equivalent
|
||||||
|
of the boot-time profile= option.
|
||||||
|
|
||||||
|
You can get the same effect running:
|
||||||
|
|
||||||
|
echo 2 > /sys/kernel/profile
|
||||||
|
|
||||||
|
as you would by issuing profile=2 on the boot
|
||||||
|
command line.
|
|
@ -35,7 +35,9 @@ enum profile_type {
|
||||||
extern int prof_on __read_mostly;
|
extern int prof_on __read_mostly;
|
||||||
|
|
||||||
/* init basic kernel profiler */
|
/* init basic kernel profiler */
|
||||||
void __init profile_init(void);
|
int profile_init(void);
|
||||||
|
int profile_setup(char *str);
|
||||||
|
int create_proc_profile(void);
|
||||||
void profile_tick(int type);
|
void profile_tick(int type);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -84,9 +86,9 @@ struct pt_regs;
|
||||||
|
|
||||||
#define prof_on 0
|
#define prof_on 0
|
||||||
|
|
||||||
static inline void profile_init(void)
|
static inline int profile_init(void)
|
||||||
{
|
{
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void profile_tick(int type)
|
static inline void profile_tick(int type)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
|
#include <linux/profile.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
|
||||||
#define KERNEL_ATTR_RO(_name) \
|
#define KERNEL_ATTR_RO(_name) \
|
||||||
|
@ -53,6 +54,37 @@ static ssize_t uevent_helper_store(struct kobject *kobj,
|
||||||
KERNEL_ATTR_RW(uevent_helper);
|
KERNEL_ATTR_RW(uevent_helper);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PROFILING
|
||||||
|
static ssize_t profiling_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
return sprintf(buf, "%d\n", prof_on);
|
||||||
|
}
|
||||||
|
static ssize_t profiling_store(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (prof_on)
|
||||||
|
return -EEXIST;
|
||||||
|
/*
|
||||||
|
* This eventually calls into get_option() which
|
||||||
|
* has a ton of callers and is not const. It is
|
||||||
|
* easiest to cast it away here.
|
||||||
|
*/
|
||||||
|
profile_setup((char *)buf);
|
||||||
|
ret = profile_init();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
ret = create_proc_profile();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
KERNEL_ATTR_RW(profiling);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KEXEC
|
#ifdef CONFIG_KEXEC
|
||||||
static ssize_t kexec_loaded_show(struct kobject *kobj,
|
static ssize_t kexec_loaded_show(struct kobject *kobj,
|
||||||
struct kobj_attribute *attr, char *buf)
|
struct kobj_attribute *attr, char *buf)
|
||||||
|
@ -109,6 +141,9 @@ static struct attribute * kernel_attrs[] = {
|
||||||
&uevent_seqnum_attr.attr,
|
&uevent_seqnum_attr.attr,
|
||||||
&uevent_helper_attr.attr,
|
&uevent_helper_attr.attr,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_PROFILING
|
||||||
|
&profiling_attr.attr,
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_KEXEC
|
#ifdef CONFIG_KEXEC
|
||||||
&kexec_loaded_attr.attr,
|
&kexec_loaded_attr.attr,
|
||||||
&kexec_crash_loaded_attr.attr,
|
&kexec_crash_loaded_attr.attr,
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
#include <asm/sections.h>
|
#include <asm/sections.h>
|
||||||
#include <asm/irq_regs.h>
|
#include <asm/irq_regs.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
|
@ -50,11 +52,11 @@ static DEFINE_PER_CPU(int, cpu_profile_flip);
|
||||||
static DEFINE_MUTEX(profile_flip_mutex);
|
static DEFINE_MUTEX(profile_flip_mutex);
|
||||||
#endif /* CONFIG_SMP */
|
#endif /* CONFIG_SMP */
|
||||||
|
|
||||||
static int __init profile_setup(char *str)
|
int profile_setup(char *str)
|
||||||
{
|
{
|
||||||
static char __initdata schedstr[] = "schedule";
|
static char schedstr[] = "schedule";
|
||||||
static char __initdata sleepstr[] = "sleep";
|
static char sleepstr[] = "sleep";
|
||||||
static char __initdata kvmstr[] = "kvm";
|
static char kvmstr[] = "kvm";
|
||||||
int par;
|
int par;
|
||||||
|
|
||||||
if (!strncmp(str, sleepstr, strlen(sleepstr))) {
|
if (!strncmp(str, sleepstr, strlen(sleepstr))) {
|
||||||
|
@ -100,14 +102,33 @@ static int __init profile_setup(char *str)
|
||||||
__setup("profile=", profile_setup);
|
__setup("profile=", profile_setup);
|
||||||
|
|
||||||
|
|
||||||
void __init profile_init(void)
|
int profile_init(void)
|
||||||
{
|
{
|
||||||
|
int buffer_bytes;
|
||||||
if (!prof_on)
|
if (!prof_on)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
/* only text is profiled */
|
/* only text is profiled */
|
||||||
prof_len = (_etext - _stext) >> prof_shift;
|
prof_len = (_etext - _stext) >> prof_shift;
|
||||||
prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t));
|
buffer_bytes = prof_len*sizeof(atomic_t);
|
||||||
|
if (!slab_is_available()) {
|
||||||
|
prof_buffer = alloc_bootmem(buffer_bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL);
|
||||||
|
if (prof_buffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
prof_buffer = alloc_pages_exact(buffer_bytes, GFP_KERNEL|__GFP_ZERO);
|
||||||
|
if (prof_buffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
prof_buffer = vmalloc(buffer_bytes);
|
||||||
|
if (prof_buffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Profile event notifications */
|
/* Profile event notifications */
|
||||||
|
@ -527,7 +548,7 @@ static void __init profile_nop(void *unused)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init create_hash_tables(void)
|
static int create_hash_tables(void)
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
|
@ -575,14 +596,14 @@ out_cleanup:
|
||||||
#define create_hash_tables() ({ 0; })
|
#define create_hash_tables() ({ 0; })
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int __init create_proc_profile(void)
|
int create_proc_profile(void)
|
||||||
{
|
{
|
||||||
struct proc_dir_entry *entry;
|
struct proc_dir_entry *entry;
|
||||||
|
|
||||||
if (!prof_on)
|
if (!prof_on)
|
||||||
return 0;
|
return 0;
|
||||||
if (create_hash_tables())
|
if (create_hash_tables())
|
||||||
return -1;
|
return -ENOMEM;
|
||||||
entry = proc_create("profile", S_IWUSR | S_IRUGO,
|
entry = proc_create("profile", S_IWUSR | S_IRUGO,
|
||||||
NULL, &proc_profile_operations);
|
NULL, &proc_profile_operations);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
|
|
Loading…
Reference in New Issue