diff options
| -rw-r--r-- | include/linux/perf_counter.h | 2 | ||||
| -rw-r--r-- | include/linux/syscalls.h | 52 | ||||
| -rw-r--r-- | include/trace/syscall.h | 7 | ||||
| -rw-r--r-- | kernel/trace/trace_syscalls.c | 121 |
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; | |||
| 734 | extern int sysctl_perf_counter_sample_rate; | 734 | extern int sysctl_perf_counter_sample_rate; |
| 735 | 735 | ||
| 736 | extern void perf_counter_init(void); | 736 | extern void perf_counter_init(void); |
| 737 | extern 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) \ | ||
| 103 | static 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 | \ | ||
| 111 | static 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) \ | ||
| 118 | static 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 | \ | ||
| 126 | static 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); | |||
| 58 | enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags); | 58 | enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags); |
| 59 | enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags); | 59 | enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags); |
| 60 | #endif | 60 | #endif |
| 61 | #ifdef CONFIG_EVENT_PROFILE | ||
| 62 | int reg_prof_syscall_enter(char *name); | ||
| 63 | void unreg_prof_syscall_enter(char *name); | ||
| 64 | int reg_prof_syscall_exit(char *name); | ||
| 65 | void 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 = { | |||
| 252 | struct trace_event event_syscall_exit = { | 253 | struct trace_event event_syscall_exit = { |
| 253 | .trace = print_syscall_exit, | 254 | .trace = print_syscall_exit, |
| 254 | }; | 255 | }; |
| 256 | |||
| 257 | #ifdef CONFIG_EVENT_PROFILE | ||
| 258 | static DECLARE_BITMAP(enabled_prof_enter_syscalls, FTRACE_SYSCALL_MAX); | ||
| 259 | static DECLARE_BITMAP(enabled_prof_exit_syscalls, FTRACE_SYSCALL_MAX); | ||
| 260 | static int sys_prof_refcount_enter; | ||
| 261 | static int sys_prof_refcount_exit; | ||
| 262 | |||
| 263 | static 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 | |||
| 279 | int 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 | |||
| 302 | void 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 | |||
| 318 | static 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 | |||
| 334 | int 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 | |||
| 357 | void 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 | |||
