diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-09-10 19:53:30 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-09-10 23:33:03 -0400 |
commit | e08d1c657f70bcaca11401cd6ac5c8fe59bd2bb7 (patch) | |
tree | 686c826210997cce9d9331629308610ee4ee8ae2 /kernel/trace | |
parent | 4a846b443b4e8633057946a2234e23559a67ce42 (diff) |
tracing/kprobes: Add event profiling support
Add *probe_profile_enable/disable to support kprobes raw events
sampling from perf counters, like other ftrace events, when
CONFIG_PROFILE_EVENT=y.
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <20090910235329.22412.94731.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace_kprobe.c | 110 |
1 files changed, 108 insertions, 2 deletions
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 4ce728ca1b18..730e992d28da 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/string.h> | 28 | #include <linux/string.h> |
29 | #include <linux/ctype.h> | 29 | #include <linux/ctype.h> |
30 | #include <linux/ptrace.h> | 30 | #include <linux/ptrace.h> |
31 | #include <linux/perf_counter.h> | ||
31 | 32 | ||
32 | #include "trace.h" | 33 | #include "trace.h" |
33 | #include "trace_output.h" | 34 | #include "trace_output.h" |
@@ -280,6 +281,7 @@ static struct trace_probe *alloc_trace_probe(const char *event, | |||
280 | } else | 281 | } else |
281 | tp->rp.kp.addr = addr; | 282 | tp->rp.kp.addr = addr; |
282 | 283 | ||
284 | /* Set handler here for checking whether this probe is return or not. */ | ||
283 | if (is_return) | 285 | if (is_return) |
284 | tp->rp.handler = kretprobe_trace_func; | 286 | tp->rp.handler = kretprobe_trace_func; |
285 | else | 287 | else |
@@ -929,10 +931,13 @@ static int probe_event_enable(struct ftrace_event_call *call) | |||
929 | { | 931 | { |
930 | struct trace_probe *tp = (struct trace_probe *)call->data; | 932 | struct trace_probe *tp = (struct trace_probe *)call->data; |
931 | 933 | ||
932 | if (probe_is_return(tp)) | 934 | if (probe_is_return(tp)) { |
935 | tp->rp.handler = kretprobe_trace_func; | ||
933 | return enable_kretprobe(&tp->rp); | 936 | return enable_kretprobe(&tp->rp); |
934 | else | 937 | } else { |
938 | tp->rp.kp.pre_handler = kprobe_trace_func; | ||
935 | return enable_kprobe(&tp->rp.kp); | 939 | return enable_kprobe(&tp->rp.kp); |
940 | } | ||
936 | } | 941 | } |
937 | 942 | ||
938 | static void probe_event_disable(struct ftrace_event_call *call) | 943 | static void probe_event_disable(struct ftrace_event_call *call) |
@@ -1105,6 +1110,101 @@ static int kretprobe_event_show_format(struct ftrace_event_call *call, | |||
1105 | "func, ret_ip"); | 1110 | "func, ret_ip"); |
1106 | } | 1111 | } |
1107 | 1112 | ||
1113 | #ifdef CONFIG_EVENT_PROFILE | ||
1114 | |||
1115 | /* Kprobe profile handler */ | ||
1116 | static __kprobes int kprobe_profile_func(struct kprobe *kp, | ||
1117 | struct pt_regs *regs) | ||
1118 | { | ||
1119 | struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); | ||
1120 | struct ftrace_event_call *call = &tp->call; | ||
1121 | struct kprobe_trace_entry *entry; | ||
1122 | int size, i, pc; | ||
1123 | unsigned long irq_flags; | ||
1124 | |||
1125 | local_save_flags(irq_flags); | ||
1126 | pc = preempt_count(); | ||
1127 | |||
1128 | size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args); | ||
1129 | |||
1130 | do { | ||
1131 | char raw_data[size]; | ||
1132 | struct trace_entry *ent; | ||
1133 | |||
1134 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; | ||
1135 | entry = (struct kprobe_trace_entry *)raw_data; | ||
1136 | ent = &entry->ent; | ||
1137 | |||
1138 | tracing_generic_entry_update(ent, irq_flags, pc); | ||
1139 | ent->type = call->id; | ||
1140 | entry->nargs = tp->nr_args; | ||
1141 | entry->ip = (unsigned long)kp->addr; | ||
1142 | for (i = 0; i < tp->nr_args; i++) | ||
1143 | entry->args[i] = call_fetch(&tp->args[i], regs); | ||
1144 | perf_tpcounter_event(call->id, entry->ip, 1, entry, size); | ||
1145 | } while (0); | ||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | /* Kretprobe profile handler */ | ||
1150 | static __kprobes int kretprobe_profile_func(struct kretprobe_instance *ri, | ||
1151 | struct pt_regs *regs) | ||
1152 | { | ||
1153 | struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); | ||
1154 | struct ftrace_event_call *call = &tp->call; | ||
1155 | struct kretprobe_trace_entry *entry; | ||
1156 | int size, i, pc; | ||
1157 | unsigned long irq_flags; | ||
1158 | |||
1159 | local_save_flags(irq_flags); | ||
1160 | pc = preempt_count(); | ||
1161 | |||
1162 | size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args); | ||
1163 | |||
1164 | do { | ||
1165 | char raw_data[size]; | ||
1166 | struct trace_entry *ent; | ||
1167 | |||
1168 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; | ||
1169 | entry = (struct kretprobe_trace_entry *)raw_data; | ||
1170 | ent = &entry->ent; | ||
1171 | |||
1172 | tracing_generic_entry_update(ent, irq_flags, pc); | ||
1173 | ent->type = call->id; | ||
1174 | entry->nargs = tp->nr_args; | ||
1175 | entry->func = (unsigned long)tp->rp.kp.addr; | ||
1176 | entry->ret_ip = (unsigned long)ri->ret_addr; | ||
1177 | for (i = 0; i < tp->nr_args; i++) | ||
1178 | entry->args[i] = call_fetch(&tp->args[i], regs); | ||
1179 | perf_tpcounter_event(call->id, entry->ret_ip, 1, entry, size); | ||
1180 | } while (0); | ||
1181 | return 0; | ||
1182 | } | ||
1183 | |||
1184 | static int probe_profile_enable(struct ftrace_event_call *call) | ||
1185 | { | ||
1186 | struct trace_probe *tp = (struct trace_probe *)call->data; | ||
1187 | |||
1188 | if (atomic_inc_return(&call->profile_count)) | ||
1189 | return 0; | ||
1190 | |||
1191 | if (probe_is_return(tp)) { | ||
1192 | tp->rp.handler = kretprobe_profile_func; | ||
1193 | return enable_kretprobe(&tp->rp); | ||
1194 | } else { | ||
1195 | tp->rp.kp.pre_handler = kprobe_profile_func; | ||
1196 | return enable_kprobe(&tp->rp.kp); | ||
1197 | } | ||
1198 | } | ||
1199 | |||
1200 | static void probe_profile_disable(struct ftrace_event_call *call) | ||
1201 | { | ||
1202 | if (atomic_add_negative(-1, &call->profile_count)) | ||
1203 | probe_event_disable(call); | ||
1204 | } | ||
1205 | |||
1206 | #endif /* CONFIG_EVENT_PROFILE */ | ||
1207 | |||
1108 | static int register_probe_event(struct trace_probe *tp) | 1208 | static int register_probe_event(struct trace_probe *tp) |
1109 | { | 1209 | { |
1110 | struct ftrace_event_call *call = &tp->call; | 1210 | struct ftrace_event_call *call = &tp->call; |
@@ -1130,6 +1230,12 @@ static int register_probe_event(struct trace_probe *tp) | |||
1130 | call->enabled = 1; | 1230 | call->enabled = 1; |
1131 | call->regfunc = probe_event_enable; | 1231 | call->regfunc = probe_event_enable; |
1132 | call->unregfunc = probe_event_disable; | 1232 | call->unregfunc = probe_event_disable; |
1233 | |||
1234 | #ifdef CONFIG_EVENT_PROFILE | ||
1235 | atomic_set(&call->profile_count, -1); | ||
1236 | call->profile_enable = probe_profile_enable; | ||
1237 | call->profile_disable = probe_profile_disable; | ||
1238 | #endif | ||
1133 | call->data = tp; | 1239 | call->data = tp; |
1134 | ret = trace_add_event_call(call); | 1240 | ret = trace_add_event_call(call); |
1135 | if (ret) { | 1241 | if (ret) { |