diff options
author | Dave Hansen <dave@linux.vnet.ibm.com> | 2008-10-16 01:01:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 14:21:31 -0400 |
commit | 22b8ce94708f7cdf0b04965c6f7443dfd374c35c (patch) | |
tree | e2d5b60e9b881cf251185b23c3853c8b3e52d42a | |
parent | 0c2d64fb6cae9aae480f6a46cfe79f8d7d48b59f (diff) |
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>
-rw-r--r-- | Documentation/ABI/testing/sysfs-profiling | 13 | ||||
-rw-r--r-- | include/linux/profile.h | 8 | ||||
-rw-r--r-- | kernel/ksysfs.c | 35 | ||||
-rw-r--r-- | kernel/profile.c | 41 |
4 files changed, 84 insertions, 13 deletions
diff --git a/Documentation/ABI/testing/sysfs-profiling b/Documentation/ABI/testing/sysfs-profiling new file mode 100644 index 000000000000..b02d8b8c173a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-profiling | |||
@@ -0,0 +1,13 @@ | |||
1 | What: /sys/kernel/profile | ||
2 | Date: September 2008 | ||
3 | Contact: Dave Hansen <dave@linux.vnet.ibm.com> | ||
4 | Description: | ||
5 | /sys/kernel/profile is the runtime equivalent | ||
6 | of the boot-time profile= option. | ||
7 | |||
8 | You can get the same effect running: | ||
9 | |||
10 | echo 2 > /sys/kernel/profile | ||
11 | |||
12 | as you would by issuing profile=2 on the boot | ||
13 | command line. | ||
diff --git a/include/linux/profile.h b/include/linux/profile.h index 7e7087239af5..570045053ce9 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h | |||
@@ -35,7 +35,9 @@ enum profile_type { | |||
35 | extern int prof_on __read_mostly; | 35 | extern int prof_on __read_mostly; |
36 | 36 | ||
37 | /* init basic kernel profiler */ | 37 | /* init basic kernel profiler */ |
38 | void __init profile_init(void); | 38 | int profile_init(void); |
39 | int profile_setup(char *str); | ||
40 | int create_proc_profile(void); | ||
39 | void profile_tick(int type); | 41 | void profile_tick(int type); |
40 | 42 | ||
41 | /* | 43 | /* |
@@ -84,9 +86,9 @@ struct pt_regs; | |||
84 | 86 | ||
85 | #define prof_on 0 | 87 | #define prof_on 0 |
86 | 88 | ||
87 | static inline void profile_init(void) | 89 | static inline int profile_init(void) |
88 | { | 90 | { |
89 | return; | 91 | return 0; |
90 | } | 92 | } |
91 | 93 | ||
92 | static inline void profile_tick(int type) | 94 | static inline void profile_tick(int type) |
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index e53bc30e9ba5..08dd8ed86c77 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/kexec.h> | 16 | #include <linux/kexec.h> |
17 | #include <linux/profile.h> | ||
17 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
18 | 19 | ||
19 | #define KERNEL_ATTR_RO(_name) \ | 20 | #define KERNEL_ATTR_RO(_name) \ |
@@ -53,6 +54,37 @@ static ssize_t uevent_helper_store(struct kobject *kobj, | |||
53 | KERNEL_ATTR_RW(uevent_helper); | 54 | KERNEL_ATTR_RW(uevent_helper); |
54 | #endif | 55 | #endif |
55 | 56 | ||
57 | #ifdef CONFIG_PROFILING | ||
58 | static ssize_t profiling_show(struct kobject *kobj, | ||
59 | struct kobj_attribute *attr, char *buf) | ||
60 | { | ||
61 | return sprintf(buf, "%d\n", prof_on); | ||
62 | } | ||
63 | static ssize_t profiling_store(struct kobject *kobj, | ||
64 | struct kobj_attribute *attr, | ||
65 | const char *buf, size_t count) | ||
66 | { | ||
67 | int ret; | ||
68 | |||
69 | if (prof_on) | ||
70 | return -EEXIST; | ||
71 | /* | ||
72 | * This eventually calls into get_option() which | ||
73 | * has a ton of callers and is not const. It is | ||
74 | * easiest to cast it away here. | ||
75 | */ | ||
76 | profile_setup((char *)buf); | ||
77 | ret = profile_init(); | ||
78 | if (ret) | ||
79 | return ret; | ||
80 | ret = create_proc_profile(); | ||
81 | if (ret) | ||
82 | return ret; | ||
83 | return count; | ||
84 | } | ||
85 | KERNEL_ATTR_RW(profiling); | ||
86 | #endif | ||
87 | |||
56 | #ifdef CONFIG_KEXEC | 88 | #ifdef CONFIG_KEXEC |
57 | static ssize_t kexec_loaded_show(struct kobject *kobj, | 89 | static ssize_t kexec_loaded_show(struct kobject *kobj, |
58 | struct kobj_attribute *attr, char *buf) | 90 | struct kobj_attribute *attr, char *buf) |
@@ -109,6 +141,9 @@ static struct attribute * kernel_attrs[] = { | |||
109 | &uevent_seqnum_attr.attr, | 141 | &uevent_seqnum_attr.attr, |
110 | &uevent_helper_attr.attr, | 142 | &uevent_helper_attr.attr, |
111 | #endif | 143 | #endif |
144 | #ifdef CONFIG_PROFILING | ||
145 | &profiling_attr.attr, | ||
146 | #endif | ||
112 | #ifdef CONFIG_KEXEC | 147 | #ifdef CONFIG_KEXEC |
113 | &kexec_loaded_attr.attr, | 148 | &kexec_loaded_attr.attr, |
114 | &kexec_crash_loaded_attr.attr, | 149 | &kexec_crash_loaded_attr.attr, |
diff --git a/kernel/profile.c b/kernel/profile.c index cd26bed4cc26..a9e422df6bf6 100644 --- a/kernel/profile.c +++ b/kernel/profile.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #include <linux/cpu.h> | 22 | #include <linux/cpu.h> |
23 | #include <linux/highmem.h> | 23 | #include <linux/highmem.h> |
24 | #include <linux/mutex.h> | 24 | #include <linux/mutex.h> |
25 | #include <linux/slab.h> | ||
26 | #include <linux/vmalloc.h> | ||
25 | #include <asm/sections.h> | 27 | #include <asm/sections.h> |
26 | #include <asm/irq_regs.h> | 28 | #include <asm/irq_regs.h> |
27 | #include <asm/ptrace.h> | 29 | #include <asm/ptrace.h> |
@@ -50,11 +52,11 @@ static DEFINE_PER_CPU(int, cpu_profile_flip); | |||
50 | static DEFINE_MUTEX(profile_flip_mutex); | 52 | static DEFINE_MUTEX(profile_flip_mutex); |
51 | #endif /* CONFIG_SMP */ | 53 | #endif /* CONFIG_SMP */ |
52 | 54 | ||
53 | static int __init profile_setup(char *str) | 55 | int profile_setup(char *str) |
54 | { | 56 | { |
55 | static char __initdata schedstr[] = "schedule"; | 57 | static char schedstr[] = "schedule"; |
56 | static char __initdata sleepstr[] = "sleep"; | 58 | static char sleepstr[] = "sleep"; |
57 | static char __initdata kvmstr[] = "kvm"; | 59 | static char kvmstr[] = "kvm"; |
58 | int par; | 60 | int par; |
59 | 61 | ||
60 | if (!strncmp(str, sleepstr, strlen(sleepstr))) { | 62 | if (!strncmp(str, sleepstr, strlen(sleepstr))) { |
@@ -100,14 +102,33 @@ static int __init profile_setup(char *str) | |||
100 | __setup("profile=", profile_setup); | 102 | __setup("profile=", profile_setup); |
101 | 103 | ||
102 | 104 | ||
103 | void __init profile_init(void) | 105 | int profile_init(void) |
104 | { | 106 | { |
107 | int buffer_bytes; | ||
105 | if (!prof_on) | 108 | if (!prof_on) |
106 | return; | 109 | return 0; |
107 | 110 | ||
108 | /* only text is profiled */ | 111 | /* only text is profiled */ |
109 | prof_len = (_etext - _stext) >> prof_shift; | 112 | prof_len = (_etext - _stext) >> prof_shift; |
110 | prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t)); | 113 | buffer_bytes = prof_len*sizeof(atomic_t); |
114 | if (!slab_is_available()) { | ||
115 | prof_buffer = alloc_bootmem(buffer_bytes); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL); | ||
120 | if (prof_buffer) | ||
121 | return 0; | ||
122 | |||
123 | prof_buffer = alloc_pages_exact(buffer_bytes, GFP_KERNEL|__GFP_ZERO); | ||
124 | if (prof_buffer) | ||
125 | return 0; | ||
126 | |||
127 | prof_buffer = vmalloc(buffer_bytes); | ||
128 | if (prof_buffer) | ||
129 | return 0; | ||
130 | |||
131 | return -ENOMEM; | ||
111 | } | 132 | } |
112 | 133 | ||
113 | /* Profile event notifications */ | 134 | /* Profile event notifications */ |
@@ -527,7 +548,7 @@ static void __init profile_nop(void *unused) | |||
527 | { | 548 | { |
528 | } | 549 | } |
529 | 550 | ||
530 | static int __init create_hash_tables(void) | 551 | static int create_hash_tables(void) |
531 | { | 552 | { |
532 | int cpu; | 553 | int cpu; |
533 | 554 | ||
@@ -575,14 +596,14 @@ out_cleanup: | |||
575 | #define create_hash_tables() ({ 0; }) | 596 | #define create_hash_tables() ({ 0; }) |
576 | #endif | 597 | #endif |
577 | 598 | ||
578 | static int __init create_proc_profile(void) | 599 | int create_proc_profile(void) |
579 | { | 600 | { |
580 | struct proc_dir_entry *entry; | 601 | struct proc_dir_entry *entry; |
581 | 602 | ||
582 | if (!prof_on) | 603 | if (!prof_on) |
583 | return 0; | 604 | return 0; |
584 | if (create_hash_tables()) | 605 | if (create_hash_tables()) |
585 | return -1; | 606 | return -ENOMEM; |
586 | entry = proc_create("profile", S_IWUSR | S_IRUGO, | 607 | entry = proc_create("profile", S_IWUSR | S_IRUGO, |
587 | NULL, &proc_profile_operations); | 608 | NULL, &proc_profile_operations); |
588 | if (!entry) | 609 | if (!entry) |