aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Baron <jbaron@redhat.com>2009-08-10 16:53:02 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2009-08-11 14:35:29 -0400
commitf4b5ffccc83c82947f5d9f15d6f1b6edb1b71cd7 (patch)
tree06ec4a005b40ba0b6039e6c3425dd186486b8c6f
parent64c12e0444fcc6b75eb49144ba46d43dbdc6bc8f (diff)
tracing: Add perf counter support for syscalls tracing
The perf counter support is automated for usual trace events. But we have to define specific callbacks for this to handle syscalls trace events Make 'perf stat -e syscalls:sys_enter_blah' work with syscall style tracepoints. Signed-off-by: Jason Baron <jbaron@redhat.com> Cc: Lai Jiangshan <laijs@cn.fujitsu.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> Cc: Jiaying Zhang <jiayingz@google.com> Cc: Martin Bligh <mbligh@google.com> Cc: Li Zefan <lizf@cn.fujitsu.com> Cc: Masami Hiramatsu <mhiramat@redhat.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
-rw-r--r--include/linux/perf_counter.h2
-rw-r--r--include/linux/syscalls.h52
-rw-r--r--include/trace/syscall.h7
-rw-r--r--kernel/trace/trace_syscalls.c121
4 files changed, 181 insertions, 1 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h
index a9d823a93fe8..8e6460fb4c02 100644
--- a/include/linux/perf_counter.h
+++ b/include/linux/perf_counter.h
@@ -734,6 +734,8 @@ extern int sysctl_perf_counter_mlock;
734extern int sysctl_perf_counter_sample_rate; 734extern int sysctl_perf_counter_sample_rate;
735 735
736extern void perf_counter_init(void); 736extern void perf_counter_init(void);
737extern void perf_tpcounter_event(int event_id, u64 addr, u64 count,
738 void *record, int entry_size);
737 739
738#ifndef perf_misc_flags 740#ifndef perf_misc_flags
739#define perf_misc_flags(regs) (user_mode(regs) ? PERF_EVENT_MISC_USER : \ 741#define perf_misc_flags(regs) (user_mode(regs) ? PERF_EVENT_MISC_USER : \
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index ce4b01c658eb..5541e75e140a 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -98,6 +98,53 @@ struct perf_counter_attr;
98#define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__) 98#define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__)
99#define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__) 99#define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__)
100 100
101#ifdef CONFIG_EVENT_PROFILE
102#define TRACE_SYS_ENTER_PROFILE(sname) \
103static int prof_sysenter_enable_##sname(struct ftrace_event_call *event_call) \
104{ \
105 int ret = 0; \
106 if (!atomic_inc_return(&event_enter_##sname.profile_count)) \
107 ret = reg_prof_syscall_enter("sys"#sname); \
108 return ret; \
109} \
110 \
111static void prof_sysenter_disable_##sname(struct ftrace_event_call *event_call)\
112{ \
113 if (atomic_add_negative(-1, &event_enter_##sname.profile_count)) \
114 unreg_prof_syscall_enter("sys"#sname); \
115}
116
117#define TRACE_SYS_EXIT_PROFILE(sname) \
118static int prof_sysexit_enable_##sname(struct ftrace_event_call *event_call) \
119{ \
120 int ret = 0; \
121 if (!atomic_inc_return(&event_exit_##sname.profile_count)) \
122 ret = reg_prof_syscall_exit("sys"#sname); \
123 return ret; \
124} \
125 \
126static void prof_sysexit_disable_##sname(struct ftrace_event_call *event_call) \
127{ \
128 if (atomic_add_negative(-1, &event_exit_##sname.profile_count)) \
129 unreg_prof_syscall_exit("sys"#sname); \
130}
131
132#define TRACE_SYS_ENTER_PROFILE_INIT(sname) \
133 .profile_count = ATOMIC_INIT(-1), \
134 .profile_enable = prof_sysenter_enable_##sname, \
135 .profile_disable = prof_sysenter_disable_##sname,
136
137#define TRACE_SYS_EXIT_PROFILE_INIT(sname) \
138 .profile_count = ATOMIC_INIT(-1), \
139 .profile_enable = prof_sysexit_enable_##sname, \
140 .profile_disable = prof_sysexit_disable_##sname,
141#else
142#define TRACE_SYS_ENTER_PROFILE(sname)
143#define TRACE_SYS_ENTER_PROFILE_INIT(sname)
144#define TRACE_SYS_EXIT_PROFILE(sname)
145#define TRACE_SYS_EXIT_PROFILE_INIT(sname)
146#endif
147
101#ifdef CONFIG_FTRACE_SYSCALLS 148#ifdef CONFIG_FTRACE_SYSCALLS
102#define __SC_STR_ADECL1(t, a) #a 149#define __SC_STR_ADECL1(t, a) #a
103#define __SC_STR_ADECL2(t, a, ...) #a, __SC_STR_ADECL1(__VA_ARGS__) 150#define __SC_STR_ADECL2(t, a, ...) #a, __SC_STR_ADECL1(__VA_ARGS__)
@@ -113,7 +160,6 @@ struct perf_counter_attr;
113#define __SC_STR_TDECL5(t, a, ...) #t, __SC_STR_TDECL4(__VA_ARGS__) 160#define __SC_STR_TDECL5(t, a, ...) #t, __SC_STR_TDECL4(__VA_ARGS__)
114#define __SC_STR_TDECL6(t, a, ...) #t, __SC_STR_TDECL5(__VA_ARGS__) 161#define __SC_STR_TDECL6(t, a, ...) #t, __SC_STR_TDECL5(__VA_ARGS__)
115 162
116
117#define SYSCALL_TRACE_ENTER_EVENT(sname) \ 163#define SYSCALL_TRACE_ENTER_EVENT(sname) \
118 static struct ftrace_event_call event_enter_##sname; \ 164 static struct ftrace_event_call event_enter_##sname; \
119 struct trace_event enter_syscall_print_##sname = { \ 165 struct trace_event enter_syscall_print_##sname = { \
@@ -134,6 +180,7 @@ struct perf_counter_attr;
134 init_preds(&event_enter_##sname); \ 180 init_preds(&event_enter_##sname); \
135 return 0; \ 181 return 0; \
136 } \ 182 } \
183 TRACE_SYS_ENTER_PROFILE(sname); \
137 static struct ftrace_event_call __used \ 184 static struct ftrace_event_call __used \
138 __attribute__((__aligned__(4))) \ 185 __attribute__((__aligned__(4))) \
139 __attribute__((section("_ftrace_events"))) \ 186 __attribute__((section("_ftrace_events"))) \
@@ -145,6 +192,7 @@ struct perf_counter_attr;
145 .regfunc = reg_event_syscall_enter, \ 192 .regfunc = reg_event_syscall_enter, \
146 .unregfunc = unreg_event_syscall_enter, \ 193 .unregfunc = unreg_event_syscall_enter, \
147 .data = "sys"#sname, \ 194 .data = "sys"#sname, \
195 TRACE_SYS_ENTER_PROFILE_INIT(sname) \
148 } 196 }
149 197
150#define SYSCALL_TRACE_EXIT_EVENT(sname) \ 198#define SYSCALL_TRACE_EXIT_EVENT(sname) \
@@ -167,6 +215,7 @@ struct perf_counter_attr;
167 init_preds(&event_exit_##sname); \ 215 init_preds(&event_exit_##sname); \
168 return 0; \ 216 return 0; \
169 } \ 217 } \
218 TRACE_SYS_EXIT_PROFILE(sname); \
170 static struct ftrace_event_call __used \ 219 static struct ftrace_event_call __used \
171 __attribute__((__aligned__(4))) \ 220 __attribute__((__aligned__(4))) \
172 __attribute__((section("_ftrace_events"))) \ 221 __attribute__((section("_ftrace_events"))) \
@@ -178,6 +227,7 @@ struct perf_counter_attr;
178 .regfunc = reg_event_syscall_exit, \ 227 .regfunc = reg_event_syscall_exit, \
179 .unregfunc = unreg_event_syscall_exit, \ 228 .unregfunc = unreg_event_syscall_exit, \
180 .data = "sys"#sname, \ 229 .data = "sys"#sname, \
230 TRACE_SYS_EXIT_PROFILE_INIT(sname) \
181 } 231 }
182 232
183#define SYSCALL_METADATA(sname, nb) \ 233#define SYSCALL_METADATA(sname, nb) \
diff --git a/include/trace/syscall.h b/include/trace/syscall.h
index df628404241a..3ab6dd18fa3a 100644
--- a/include/trace/syscall.h
+++ b/include/trace/syscall.h
@@ -58,5 +58,12 @@ extern void unreg_event_syscall_exit(void *ptr);
58enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags); 58enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags);
59enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags); 59enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags);
60#endif 60#endif
61#ifdef CONFIG_EVENT_PROFILE
62int reg_prof_syscall_enter(char *name);
63void unreg_prof_syscall_enter(char *name);
64int reg_prof_syscall_exit(char *name);
65void unreg_prof_syscall_exit(char *name);
66
67#endif
61 68
62#endif /* _TRACE_SYSCALL_H */ 69#endif /* _TRACE_SYSCALL_H */
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index e58a9c11ba85..f4eaec3d559a 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -1,6 +1,7 @@
1#include <trace/syscall.h> 1#include <trace/syscall.h>
2#include <linux/kernel.h> 2#include <linux/kernel.h>
3#include <linux/ftrace.h> 3#include <linux/ftrace.h>
4#include <linux/perf_counter.h>
4#include <asm/syscall.h> 5#include <asm/syscall.h>
5 6
6#include "trace_output.h" 7#include "trace_output.h"
@@ -252,3 +253,123 @@ struct trace_event event_syscall_enter = {
252struct trace_event event_syscall_exit = { 253struct trace_event event_syscall_exit = {
253 .trace = print_syscall_exit, 254 .trace = print_syscall_exit,
254}; 255};
256
257#ifdef CONFIG_EVENT_PROFILE
258static DECLARE_BITMAP(enabled_prof_enter_syscalls, FTRACE_SYSCALL_MAX);
259static DECLARE_BITMAP(enabled_prof_exit_syscalls, FTRACE_SYSCALL_MAX);
260static int sys_prof_refcount_enter;
261static int sys_prof_refcount_exit;
262
263static void prof_syscall_enter(struct pt_regs *regs, long id)
264{
265 struct syscall_metadata *sys_data;
266 int syscall_nr;
267
268 syscall_nr = syscall_get_nr(current, regs);
269 if (!test_bit(syscall_nr, enabled_prof_enter_syscalls))
270 return;
271
272 sys_data = syscall_nr_to_meta(syscall_nr);
273 if (!sys_data)
274 return;
275
276 perf_tpcounter_event(sys_data->enter_id, 0, 1, NULL, 0);
277}
278
279int reg_prof_syscall_enter(char *name)
280{
281 int ret = 0;
282 int num;
283
284 num = syscall_name_to_nr(name);
285 if (num < 0 || num >= FTRACE_SYSCALL_MAX)
286 return -ENOSYS;
287
288 mutex_lock(&syscall_trace_lock);
289 if (!sys_prof_refcount_enter)
290 ret = register_trace_syscall_enter(prof_syscall_enter);
291 if (ret) {
292 pr_info("event trace: Could not activate"
293 "syscall entry trace point");
294 } else {
295 set_bit(num, enabled_prof_enter_syscalls);
296 sys_prof_refcount_enter++;
297 }
298 mutex_unlock(&syscall_trace_lock);
299 return ret;
300}
301
302void unreg_prof_syscall_enter(char *name)
303{
304 int num;
305
306 num = syscall_name_to_nr(name);
307 if (num < 0 || num >= FTRACE_SYSCALL_MAX)
308 return;
309
310 mutex_lock(&syscall_trace_lock);
311 sys_prof_refcount_enter--;
312 clear_bit(num, enabled_prof_enter_syscalls);
313 if (!sys_prof_refcount_enter)
314 unregister_trace_syscall_enter(prof_syscall_enter);
315 mutex_unlock(&syscall_trace_lock);
316}
317
318static void prof_syscall_exit(struct pt_regs *regs, long ret)
319{
320 struct syscall_metadata *sys_data;
321 int syscall_nr;
322
323 syscall_nr = syscall_get_nr(current, regs);
324 if (!test_bit(syscall_nr, enabled_prof_exit_syscalls))
325 return;
326
327 sys_data = syscall_nr_to_meta(syscall_nr);
328 if (!sys_data)
329 return;
330
331 perf_tpcounter_event(sys_data->exit_id, 0, 1, NULL, 0);
332}
333
334int reg_prof_syscall_exit(char *name)
335{
336 int ret = 0;
337 int num;
338
339 num = syscall_name_to_nr(name);
340 if (num < 0 || num >= FTRACE_SYSCALL_MAX)
341 return -ENOSYS;
342
343 mutex_lock(&syscall_trace_lock);
344 if (!sys_prof_refcount_exit)
345 ret = register_trace_syscall_exit(prof_syscall_exit);
346 if (ret) {
347 pr_info("event trace: Could not activate"
348 "syscall entry trace point");
349 } else {
350 set_bit(num, enabled_prof_exit_syscalls);
351 sys_prof_refcount_exit++;
352 }
353 mutex_unlock(&syscall_trace_lock);
354 return ret;
355}
356
357void unreg_prof_syscall_exit(char *name)
358{
359 int num;
360
361 num = syscall_name_to_nr(name);
362 if (num < 0 || num >= FTRACE_SYSCALL_MAX)
363 return;
364
365 mutex_lock(&syscall_trace_lock);
366 sys_prof_refcount_exit--;
367 clear_bit(num, enabled_prof_exit_syscalls);
368 if (!sys_prof_refcount_exit)
369 unregister_trace_syscall_exit(prof_syscall_exit);
370 mutex_unlock(&syscall_trace_lock);
371}
372
373#endif
374
375