diff options
-rw-r--r-- | include/asm-generic/vmlinux.lds.h | 11 | ||||
-rw-r--r-- | include/linux/ftrace.h | 14 | ||||
-rw-r--r-- | include/linux/syscalls.h | 60 | ||||
-rw-r--r-- | kernel/trace/trace.h | 17 | ||||
-rw-r--r-- | kernel/trace/trace_syscalls.c | 146 |
5 files changed, 234 insertions, 14 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 0e0f39be6c8b..d3bc3c86df6a 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
@@ -77,6 +77,14 @@ | |||
77 | #define TRACE_PRINTKS() | 77 | #define TRACE_PRINTKS() |
78 | #endif | 78 | #endif |
79 | 79 | ||
80 | #ifdef CONFIG_FTRACE_SYSCALLS | ||
81 | #define TRACE_SYSCALLS() VMLINUX_SYMBOL(__start_syscalls_metadata) = .; \ | ||
82 | *(__syscalls_metadata) \ | ||
83 | VMLINUX_SYMBOL(__stop_syscalls_metadata) = .; | ||
84 | #else | ||
85 | #define TRACE_SYSCALLS() | ||
86 | #endif | ||
87 | |||
80 | /* .data section */ | 88 | /* .data section */ |
81 | #define DATA_DATA \ | 89 | #define DATA_DATA \ |
82 | *(.data) \ | 90 | *(.data) \ |
@@ -99,7 +107,8 @@ | |||
99 | LIKELY_PROFILE() \ | 107 | LIKELY_PROFILE() \ |
100 | BRANCH_PROFILE() \ | 108 | BRANCH_PROFILE() \ |
101 | TRACE_PRINTKS() \ | 109 | TRACE_PRINTKS() \ |
102 | FTRACE_EVENTS() | 110 | FTRACE_EVENTS() \ |
111 | TRACE_SYSCALLS() | ||
103 | 112 | ||
104 | #define RO_DATA(align) \ | 113 | #define RO_DATA(align) \ |
105 | . = ALIGN((align)); \ | 114 | . = ALIGN((align)); \ |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index c146c1021a29..6dc1c652447e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -506,13 +506,21 @@ static inline void trace_hw_branch_oops(void) {} | |||
506 | /* | 506 | /* |
507 | * A syscall entry in the ftrace syscalls array. | 507 | * A syscall entry in the ftrace syscalls array. |
508 | * | 508 | * |
509 | * @syscall_nr: syscall number | 509 | * @name: name of the syscall |
510 | * @nb_args: number of parameters it takes | ||
511 | * @types: list of types as strings | ||
512 | * @args: list of args as strings (args[i] matches types[i]) | ||
510 | */ | 513 | */ |
511 | struct syscall_trace_entry { | 514 | struct syscall_metadata { |
512 | int syscall_nr; | 515 | const char *name; |
516 | int nb_args; | ||
517 | const char **types; | ||
518 | const char **args; | ||
513 | }; | 519 | }; |
514 | 520 | ||
515 | #ifdef CONFIG_FTRACE_SYSCALLS | 521 | #ifdef CONFIG_FTRACE_SYSCALLS |
522 | extern void arch_init_ftrace_syscalls(void); | ||
523 | extern struct syscall_metadata *syscall_nr_to_meta(int nr); | ||
516 | extern void start_ftrace_syscalls(void); | 524 | extern void start_ftrace_syscalls(void); |
517 | extern void stop_ftrace_syscalls(void); | 525 | extern void stop_ftrace_syscalls(void); |
518 | extern void ftrace_syscall_enter(struct pt_regs *regs); | 526 | extern void ftrace_syscall_enter(struct pt_regs *regs); |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f9f900cfd066..0cff9bb80b02 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
@@ -65,6 +65,7 @@ struct old_linux_dirent; | |||
65 | #include <asm/signal.h> | 65 | #include <asm/signal.h> |
66 | #include <linux/quota.h> | 66 | #include <linux/quota.h> |
67 | #include <linux/key.h> | 67 | #include <linux/key.h> |
68 | #include <linux/ftrace.h> | ||
68 | 69 | ||
69 | #define __SC_DECL1(t1, a1) t1 a1 | 70 | #define __SC_DECL1(t1, a1) t1 a1 |
70 | #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__) | 71 | #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__) |
@@ -95,7 +96,46 @@ struct old_linux_dirent; | |||
95 | #define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__) | 96 | #define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__) |
96 | #define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__) | 97 | #define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__) |
97 | 98 | ||
99 | #ifdef CONFIG_FTRACE_SYSCALLS | ||
100 | #define __SC_STR_ADECL1(t, a) #a | ||
101 | #define __SC_STR_ADECL2(t, a, ...) #a, __SC_STR_ADECL1(__VA_ARGS__) | ||
102 | #define __SC_STR_ADECL3(t, a, ...) #a, __SC_STR_ADECL2(__VA_ARGS__) | ||
103 | #define __SC_STR_ADECL4(t, a, ...) #a, __SC_STR_ADECL3(__VA_ARGS__) | ||
104 | #define __SC_STR_ADECL5(t, a, ...) #a, __SC_STR_ADECL4(__VA_ARGS__) | ||
105 | #define __SC_STR_ADECL6(t, a, ...) #a, __SC_STR_ADECL5(__VA_ARGS__) | ||
106 | |||
107 | #define __SC_STR_TDECL1(t, a) #t | ||
108 | #define __SC_STR_TDECL2(t, a, ...) #t, __SC_STR_TDECL1(__VA_ARGS__) | ||
109 | #define __SC_STR_TDECL3(t, a, ...) #t, __SC_STR_TDECL2(__VA_ARGS__) | ||
110 | #define __SC_STR_TDECL4(t, a, ...) #t, __SC_STR_TDECL3(__VA_ARGS__) | ||
111 | #define __SC_STR_TDECL5(t, a, ...) #t, __SC_STR_TDECL4(__VA_ARGS__) | ||
112 | #define __SC_STR_TDECL6(t, a, ...) #t, __SC_STR_TDECL5(__VA_ARGS__) | ||
113 | |||
114 | #define SYSCALL_METADATA(sname, nb) \ | ||
115 | static const struct syscall_metadata __used \ | ||
116 | __attribute__((__aligned__(4))) \ | ||
117 | __attribute__((section("__syscalls_metadata"))) \ | ||
118 | __syscall_meta_##sname = { \ | ||
119 | .name = "sys"#sname, \ | ||
120 | .nb_args = nb, \ | ||
121 | .types = types_##sname, \ | ||
122 | .args = args_##sname, \ | ||
123 | } | ||
124 | |||
125 | #define SYSCALL_DEFINE0(sname) \ | ||
126 | static const struct syscall_metadata __used \ | ||
127 | __attribute__((__aligned__(4))) \ | ||
128 | __attribute__((section("__syscalls_metadata"))) \ | ||
129 | __syscall_meta_##sname = { \ | ||
130 | .name = "sys_"#sname, \ | ||
131 | .nb_args = 0, \ | ||
132 | }; \ | ||
133 | asmlinkage long sys_##sname(void) | ||
134 | |||
135 | #else | ||
98 | #define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(void) | 136 | #define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(void) |
137 | #endif | ||
138 | |||
99 | #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) | 139 | #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) |
100 | #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) | 140 | #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) |
101 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) | 141 | #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) |
@@ -117,10 +157,26 @@ struct old_linux_dirent; | |||
117 | #endif | 157 | #endif |
118 | #endif | 158 | #endif |
119 | 159 | ||
160 | #ifdef CONFIG_FTRACE_SYSCALLS | ||
161 | #define SYSCALL_DEFINEx(x, sname, ...) \ | ||
162 | static const char *types_##sname[] = { \ | ||
163 | __SC_STR_TDECL##x(__VA_ARGS__) \ | ||
164 | }; \ | ||
165 | static const char *args_##sname[] = { \ | ||
166 | __SC_STR_ADECL##x(__VA_ARGS__) \ | ||
167 | }; \ | ||
168 | SYSCALL_METADATA(sname, x); \ | ||
169 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) | ||
170 | #else | ||
171 | #define SYSCALL_DEFINEx(x, sname, ...) \ | ||
172 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) | ||
173 | #endif | ||
174 | |||
120 | #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS | 175 | #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS |
121 | 176 | ||
122 | #define SYSCALL_DEFINE(name) static inline long SYSC_##name | 177 | #define SYSCALL_DEFINE(name) static inline long SYSC_##name |
123 | #define SYSCALL_DEFINEx(x, name, ...) \ | 178 | |
179 | #define __SYSCALL_DEFINEx(x, name, ...) \ | ||
124 | asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \ | 180 | asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \ |
125 | static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ | 181 | static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ |
126 | asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \ | 182 | asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \ |
@@ -134,7 +190,7 @@ struct old_linux_dirent; | |||
134 | #else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ | 190 | #else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ |
135 | 191 | ||
136 | #define SYSCALL_DEFINE(name) asmlinkage long sys_##name | 192 | #define SYSCALL_DEFINE(name) asmlinkage long sys_##name |
137 | #define SYSCALL_DEFINEx(x, name, ...) \ | 193 | #define __SYSCALL_DEFINEx(x, name, ...) \ |
138 | asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) | 194 | asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) |
139 | 195 | ||
140 | #endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ | 196 | #endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 3d49daae47dc..d80ca0d464d9 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -194,6 +194,19 @@ struct kmemtrace_free_entry { | |||
194 | const void *ptr; | 194 | const void *ptr; |
195 | }; | 195 | }; |
196 | 196 | ||
197 | struct syscall_trace_enter { | ||
198 | struct trace_entry ent; | ||
199 | int nr; | ||
200 | unsigned long args[]; | ||
201 | }; | ||
202 | |||
203 | struct syscall_trace_exit { | ||
204 | struct trace_entry ent; | ||
205 | int nr; | ||
206 | unsigned long ret; | ||
207 | }; | ||
208 | |||
209 | |||
197 | /* | 210 | /* |
198 | * trace_flag_type is an enumeration that holds different | 211 | * trace_flag_type is an enumeration that holds different |
199 | * states when a trace occurs. These are: | 212 | * states when a trace occurs. These are: |
@@ -306,6 +319,10 @@ extern void __ftrace_bad_type(void); | |||
306 | TRACE_KMEM_ALLOC); \ | 319 | TRACE_KMEM_ALLOC); \ |
307 | IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ | 320 | IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ |
308 | TRACE_KMEM_FREE); \ | 321 | TRACE_KMEM_FREE); \ |
322 | IF_ASSIGN(var, ent, struct syscall_trace_enter, \ | ||
323 | TRACE_SYSCALL_ENTER); \ | ||
324 | IF_ASSIGN(var, ent, struct syscall_trace_exit, \ | ||
325 | TRACE_SYSCALL_EXIT); \ | ||
309 | __ftrace_bad_type(); \ | 326 | __ftrace_bad_type(); \ |
310 | } while (0) | 327 | } while (0) |
311 | 328 | ||
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 66cf97449af3..c72e599230ff 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include <linux/ftrace.h> | ||
2 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
3 | 2 | #include <linux/ftrace.h> | |
4 | #include <asm/syscall.h> | 3 | #include <asm/syscall.h> |
5 | 4 | ||
6 | #include "trace_output.h" | 5 | #include "trace_output.h" |
@@ -8,6 +7,90 @@ | |||
8 | 7 | ||
9 | static atomic_t refcount; | 8 | static atomic_t refcount; |
10 | 9 | ||
10 | /* Our two options */ | ||
11 | enum { | ||
12 | TRACE_SYSCALLS_OPT_TYPES = 0x1, | ||
13 | }; | ||
14 | |||
15 | static struct tracer_opt syscalls_opts[] = { | ||
16 | { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) }, | ||
17 | { } | ||
18 | }; | ||
19 | |||
20 | static struct tracer_flags syscalls_flags = { | ||
21 | .val = 0, /* By default: no args types */ | ||
22 | .opts = syscalls_opts | ||
23 | }; | ||
24 | |||
25 | enum print_line_t | ||
26 | print_syscall_enter(struct trace_iterator *iter, int flags) | ||
27 | { | ||
28 | struct trace_seq *s = &iter->seq; | ||
29 | struct trace_entry *ent = iter->ent; | ||
30 | struct syscall_trace_enter *trace; | ||
31 | struct syscall_metadata *entry; | ||
32 | int i, ret, syscall; | ||
33 | |||
34 | trace_assign_type(trace, ent); | ||
35 | |||
36 | syscall = trace->nr; | ||
37 | |||
38 | entry = syscall_nr_to_meta(syscall); | ||
39 | if (!entry) | ||
40 | goto end; | ||
41 | |||
42 | ret = trace_seq_printf(s, "%s(", entry->name); | ||
43 | if (!ret) | ||
44 | return TRACE_TYPE_PARTIAL_LINE; | ||
45 | |||
46 | for (i = 0; i < entry->nb_args; i++) { | ||
47 | /* parameter types */ | ||
48 | if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) { | ||
49 | ret = trace_seq_printf(s, "%s ", entry->types[i]); | ||
50 | if (!ret) | ||
51 | return TRACE_TYPE_PARTIAL_LINE; | ||
52 | } | ||
53 | /* parameter values */ | ||
54 | ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i], | ||
55 | trace->args[i], | ||
56 | i == entry->nb_args - 1 ? ")" : ","); | ||
57 | if (!ret) | ||
58 | return TRACE_TYPE_PARTIAL_LINE; | ||
59 | } | ||
60 | |||
61 | end: | ||
62 | trace_seq_printf(s, "\n"); | ||
63 | return TRACE_TYPE_HANDLED; | ||
64 | } | ||
65 | |||
66 | enum print_line_t | ||
67 | print_syscall_exit(struct trace_iterator *iter, int flags) | ||
68 | { | ||
69 | struct trace_seq *s = &iter->seq; | ||
70 | struct trace_entry *ent = iter->ent; | ||
71 | struct syscall_trace_exit *trace; | ||
72 | int syscall; | ||
73 | struct syscall_metadata *entry; | ||
74 | int ret; | ||
75 | |||
76 | trace_assign_type(trace, ent); | ||
77 | |||
78 | syscall = trace->nr; | ||
79 | |||
80 | entry = syscall_nr_to_meta(syscall); | ||
81 | if (!entry) { | ||
82 | trace_seq_printf(s, "\n"); | ||
83 | return TRACE_TYPE_HANDLED; | ||
84 | } | ||
85 | |||
86 | ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, | ||
87 | trace->ret); | ||
88 | if (!ret) | ||
89 | return TRACE_TYPE_PARTIAL_LINE; | ||
90 | |||
91 | return TRACE_TYPE_HANDLED; | ||
92 | } | ||
93 | |||
11 | void start_ftrace_syscalls(void) | 94 | void start_ftrace_syscalls(void) |
12 | { | 95 | { |
13 | unsigned long flags; | 96 | unsigned long flags; |
@@ -16,6 +99,7 @@ void start_ftrace_syscalls(void) | |||
16 | if (atomic_inc_return(&refcount) != 1) | 99 | if (atomic_inc_return(&refcount) != 1) |
17 | goto out; | 100 | goto out; |
18 | 101 | ||
102 | arch_init_ftrace_syscalls(); | ||
19 | read_lock_irqsave(&tasklist_lock, flags); | 103 | read_lock_irqsave(&tasklist_lock, flags); |
20 | 104 | ||
21 | do_each_thread(g, t) { | 105 | do_each_thread(g, t) { |
@@ -48,20 +132,63 @@ out: | |||
48 | 132 | ||
49 | void ftrace_syscall_enter(struct pt_regs *regs) | 133 | void ftrace_syscall_enter(struct pt_regs *regs) |
50 | { | 134 | { |
135 | struct syscall_trace_enter *entry; | ||
136 | struct syscall_metadata *sys_data; | ||
137 | struct ring_buffer_event *event; | ||
138 | int size; | ||
51 | int syscall_nr; | 139 | int syscall_nr; |
140 | int cpu; | ||
52 | 141 | ||
53 | syscall_nr = syscall_get_nr(current, regs); | 142 | syscall_nr = syscall_get_nr(current, regs); |
54 | 143 | ||
55 | trace_printk("syscall %d enter\n", syscall_nr); | 144 | cpu = raw_smp_processor_id(); |
145 | |||
146 | sys_data = syscall_nr_to_meta(syscall_nr); | ||
147 | if (!sys_data) | ||
148 | return; | ||
149 | |||
150 | size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; | ||
151 | |||
152 | event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size, | ||
153 | 0, 0); | ||
154 | if (!event) | ||
155 | return; | ||
156 | |||
157 | entry = ring_buffer_event_data(event); | ||
158 | entry->nr = syscall_nr; | ||
159 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); | ||
160 | |||
161 | trace_current_buffer_unlock_commit(event, 0, 0); | ||
162 | trace_wake_up(); | ||
56 | } | 163 | } |
57 | 164 | ||
58 | void ftrace_syscall_exit(struct pt_regs *regs) | 165 | void ftrace_syscall_exit(struct pt_regs *regs) |
59 | { | 166 | { |
167 | struct syscall_trace_exit *entry; | ||
168 | struct syscall_metadata *sys_data; | ||
169 | struct ring_buffer_event *event; | ||
60 | int syscall_nr; | 170 | int syscall_nr; |
171 | int cpu; | ||
61 | 172 | ||
62 | syscall_nr = syscall_get_nr(current, regs); | 173 | syscall_nr = syscall_get_nr(current, regs); |
63 | 174 | ||
64 | trace_printk("syscall %d exit\n", syscall_nr); | 175 | cpu = raw_smp_processor_id(); |
176 | |||
177 | sys_data = syscall_nr_to_meta(syscall_nr); | ||
178 | if (!sys_data) | ||
179 | return; | ||
180 | |||
181 | event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT, | ||
182 | sizeof(*entry), 0, 0); | ||
183 | if (!event) | ||
184 | return; | ||
185 | |||
186 | entry = ring_buffer_event_data(event); | ||
187 | entry->nr = syscall_nr; | ||
188 | entry->ret = syscall_get_return_value(current, regs); | ||
189 | |||
190 | trace_current_buffer_unlock_commit(event, 0, 0); | ||
191 | trace_wake_up(); | ||
65 | } | 192 | } |
66 | 193 | ||
67 | static int init_syscall_tracer(struct trace_array *tr) | 194 | static int init_syscall_tracer(struct trace_array *tr) |
@@ -77,17 +204,20 @@ static void reset_syscall_tracer(struct trace_array *tr) | |||
77 | } | 204 | } |
78 | 205 | ||
79 | static struct trace_event syscall_enter_event = { | 206 | static struct trace_event syscall_enter_event = { |
80 | .type = TRACE_SYSCALL_ENTER, | 207 | .type = TRACE_SYSCALL_ENTER, |
208 | .trace = print_syscall_enter, | ||
81 | }; | 209 | }; |
82 | 210 | ||
83 | static struct trace_event syscall_exit_event = { | 211 | static struct trace_event syscall_exit_event = { |
84 | .type = TRACE_SYSCALL_EXIT, | 212 | .type = TRACE_SYSCALL_EXIT, |
213 | .trace = print_syscall_exit, | ||
85 | }; | 214 | }; |
86 | 215 | ||
87 | static struct tracer syscall_tracer __read_mostly = { | 216 | static struct tracer syscall_tracer __read_mostly = { |
88 | .name = "syscall", | 217 | .name = "syscall", |
89 | .init = init_syscall_tracer, | 218 | .init = init_syscall_tracer, |
90 | .reset = reset_syscall_tracer | 219 | .reset = reset_syscall_tracer, |
220 | .flags = &syscalls_flags, | ||
91 | }; | 221 | }; |
92 | 222 | ||
93 | __init int register_ftrace_syscalls(void) | 223 | __init int register_ftrace_syscalls(void) |