aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/asm-generic/vmlinux.lds.h11
-rw-r--r--include/linux/ftrace.h14
-rw-r--r--include/linux/syscalls.h60
-rw-r--r--kernel/trace/trace.h17
-rw-r--r--kernel/trace/trace_syscalls.c146
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 */
511struct syscall_trace_entry { 514struct 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
522extern void arch_init_ftrace_syscalls(void);
523extern struct syscall_metadata *syscall_nr_to_meta(int nr);
516extern void start_ftrace_syscalls(void); 524extern void start_ftrace_syscalls(void);
517extern void stop_ftrace_syscalls(void); 525extern void stop_ftrace_syscalls(void);
518extern void ftrace_syscall_enter(struct pt_regs *regs); 526extern 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
197struct syscall_trace_enter {
198 struct trace_entry ent;
199 int nr;
200 unsigned long args[];
201};
202
203struct 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
9static atomic_t refcount; 8static atomic_t refcount;
10 9
10/* Our two options */
11enum {
12 TRACE_SYSCALLS_OPT_TYPES = 0x1,
13};
14
15static struct tracer_opt syscalls_opts[] = {
16 { TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
17 { }
18};
19
20static struct tracer_flags syscalls_flags = {
21 .val = 0, /* By default: no args types */
22 .opts = syscalls_opts
23};
24
25enum print_line_t
26print_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
61end:
62 trace_seq_printf(s, "\n");
63 return TRACE_TYPE_HANDLED;
64}
65
66enum print_line_t
67print_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
11void start_ftrace_syscalls(void) 94void 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
49void ftrace_syscall_enter(struct pt_regs *regs) 133void 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
58void ftrace_syscall_exit(struct pt_regs *regs) 165void 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
67static int init_syscall_tracer(struct trace_array *tr) 194static 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
79static struct trace_event syscall_enter_event = { 206static 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
83static struct trace_event syscall_exit_event = { 211static 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
87static struct tracer syscall_tracer __read_mostly = { 216static 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)