aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-03-13 10:42:11 -0400
committerIngo Molnar <mingo@elte.hu>2009-03-13 11:57:42 -0400
commitbed1ffca022cc876fb83161d26670e9b5d3cf36b (patch)
treea896c79e9ea1af11f992826f1de7e2ece52fbe33
parentf58ba100678f421bdcb000a3c71793f432dfab93 (diff)
tracing/syscalls: core infrastructure for syscalls tracing, enhancements
Impact: new feature This adds the generic support for syscalls tracing. This is currently exploited through a devoted tracer but other tracing engines can use it. (They just have to play with {start,stop}_ftrace_syscalls() and use the display callbacks unless they want to override them.) The syscalls prototypes definitions are abused here to steal some metadata informations: - syscall name, param types, param names, number of params The syscall addr is not directly saved during this definition because we don't know if its prototype is available in the namespace. But we don't really need it. The arch has just to build a function able to resolve the syscall number to its metadata struct. The current tracer prints the syscall names, parameters names and values (and their types optionally). Currently the value is a raw hex but higher level values diplaying is on my TODO list. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <1236955332-10133-2-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-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)