xref: /openbmc/linux/kernel/trace/trace_syscalls.c (revision b627b4ed)
1 #include <trace/syscall.h>
2 #include <linux/kernel.h>
3 #include <asm/syscall.h>
4 
5 #include "trace_output.h"
6 #include "trace.h"
7 
8 /* Keep a counter of the syscall tracing users */
9 static int refcount;
10 
11 /* Prevent from races on thread flags toggling */
12 static DEFINE_MUTEX(syscall_trace_lock);
13 
14 /* Option to display the parameters types */
15 enum {
16 	TRACE_SYSCALLS_OPT_TYPES = 0x1,
17 };
18 
19 static struct tracer_opt syscalls_opts[] = {
20 	{ TRACER_OPT(syscall_arg_type, TRACE_SYSCALLS_OPT_TYPES) },
21 	{ }
22 };
23 
24 static struct tracer_flags syscalls_flags = {
25 	.val = 0, /* By default: no parameters types */
26 	.opts = syscalls_opts
27 };
28 
29 enum print_line_t
30 print_syscall_enter(struct trace_iterator *iter, int flags)
31 {
32 	struct trace_seq *s = &iter->seq;
33 	struct trace_entry *ent = iter->ent;
34 	struct syscall_trace_enter *trace;
35 	struct syscall_metadata *entry;
36 	int i, ret, syscall;
37 
38 	trace_assign_type(trace, ent);
39 
40 	syscall = trace->nr;
41 
42 	entry = syscall_nr_to_meta(syscall);
43 	if (!entry)
44 		goto end;
45 
46 	ret = trace_seq_printf(s, "%s(", entry->name);
47 	if (!ret)
48 		return TRACE_TYPE_PARTIAL_LINE;
49 
50 	for (i = 0; i < entry->nb_args; i++) {
51 		/* parameter types */
52 		if (syscalls_flags.val & TRACE_SYSCALLS_OPT_TYPES) {
53 			ret = trace_seq_printf(s, "%s ", entry->types[i]);
54 			if (!ret)
55 				return TRACE_TYPE_PARTIAL_LINE;
56 		}
57 		/* parameter values */
58 		ret = trace_seq_printf(s, "%s: %lx%s ", entry->args[i],
59 				       trace->args[i],
60 				       i == entry->nb_args - 1 ? ")" : ",");
61 		if (!ret)
62 			return TRACE_TYPE_PARTIAL_LINE;
63 	}
64 
65 end:
66 	trace_seq_printf(s, "\n");
67 	return TRACE_TYPE_HANDLED;
68 }
69 
70 enum print_line_t
71 print_syscall_exit(struct trace_iterator *iter, int flags)
72 {
73 	struct trace_seq *s = &iter->seq;
74 	struct trace_entry *ent = iter->ent;
75 	struct syscall_trace_exit *trace;
76 	int syscall;
77 	struct syscall_metadata *entry;
78 	int ret;
79 
80 	trace_assign_type(trace, ent);
81 
82 	syscall = trace->nr;
83 
84 	entry = syscall_nr_to_meta(syscall);
85 	if (!entry) {
86 		trace_seq_printf(s, "\n");
87 		return TRACE_TYPE_HANDLED;
88 	}
89 
90 	ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
91 				trace->ret);
92 	if (!ret)
93 		return TRACE_TYPE_PARTIAL_LINE;
94 
95 	return TRACE_TYPE_HANDLED;
96 }
97 
98 void start_ftrace_syscalls(void)
99 {
100 	unsigned long flags;
101 	struct task_struct *g, *t;
102 
103 	mutex_lock(&syscall_trace_lock);
104 
105 	/* Don't enable the flag on the tasks twice */
106 	if (++refcount != 1)
107 		goto unlock;
108 
109 	arch_init_ftrace_syscalls();
110 	read_lock_irqsave(&tasklist_lock, flags);
111 
112 	do_each_thread(g, t) {
113 		set_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
114 	} while_each_thread(g, t);
115 
116 	read_unlock_irqrestore(&tasklist_lock, flags);
117 
118 unlock:
119 	mutex_unlock(&syscall_trace_lock);
120 }
121 
122 void stop_ftrace_syscalls(void)
123 {
124 	unsigned long flags;
125 	struct task_struct *g, *t;
126 
127 	mutex_lock(&syscall_trace_lock);
128 
129 	/* There are perhaps still some users */
130 	if (--refcount)
131 		goto unlock;
132 
133 	read_lock_irqsave(&tasklist_lock, flags);
134 
135 	do_each_thread(g, t) {
136 		clear_tsk_thread_flag(t, TIF_SYSCALL_FTRACE);
137 	} while_each_thread(g, t);
138 
139 	read_unlock_irqrestore(&tasklist_lock, flags);
140 
141 unlock:
142 	mutex_unlock(&syscall_trace_lock);
143 }
144 
145 void ftrace_syscall_enter(struct pt_regs *regs)
146 {
147 	struct syscall_trace_enter *entry;
148 	struct syscall_metadata *sys_data;
149 	struct ring_buffer_event *event;
150 	int size;
151 	int syscall_nr;
152 
153 	syscall_nr = syscall_get_nr(current, regs);
154 
155 	sys_data = syscall_nr_to_meta(syscall_nr);
156 	if (!sys_data)
157 		return;
158 
159 	size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
160 
161 	event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_ENTER, size,
162 							0, 0);
163 	if (!event)
164 		return;
165 
166 	entry = ring_buffer_event_data(event);
167 	entry->nr = syscall_nr;
168 	syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
169 
170 	trace_current_buffer_unlock_commit(event, 0, 0);
171 	trace_wake_up();
172 }
173 
174 void ftrace_syscall_exit(struct pt_regs *regs)
175 {
176 	struct syscall_trace_exit *entry;
177 	struct syscall_metadata *sys_data;
178 	struct ring_buffer_event *event;
179 	int syscall_nr;
180 
181 	syscall_nr = syscall_get_nr(current, regs);
182 
183 	sys_data = syscall_nr_to_meta(syscall_nr);
184 	if (!sys_data)
185 		return;
186 
187 	event = trace_current_buffer_lock_reserve(TRACE_SYSCALL_EXIT,
188 				sizeof(*entry), 0, 0);
189 	if (!event)
190 		return;
191 
192 	entry = ring_buffer_event_data(event);
193 	entry->nr = syscall_nr;
194 	entry->ret = syscall_get_return_value(current, regs);
195 
196 	trace_current_buffer_unlock_commit(event, 0, 0);
197 	trace_wake_up();
198 }
199 
200 static int init_syscall_tracer(struct trace_array *tr)
201 {
202 	start_ftrace_syscalls();
203 
204 	return 0;
205 }
206 
207 static void reset_syscall_tracer(struct trace_array *tr)
208 {
209 	stop_ftrace_syscalls();
210 	tracing_reset_online_cpus(tr);
211 }
212 
213 static struct trace_event syscall_enter_event = {
214 	.type	 	= TRACE_SYSCALL_ENTER,
215 	.trace		= print_syscall_enter,
216 };
217 
218 static struct trace_event syscall_exit_event = {
219 	.type	 	= TRACE_SYSCALL_EXIT,
220 	.trace		= print_syscall_exit,
221 };
222 
223 static struct tracer syscall_tracer __read_mostly = {
224 	.name	     	= "syscall",
225 	.init		= init_syscall_tracer,
226 	.reset		= reset_syscall_tracer,
227 	.flags		= &syscalls_flags,
228 };
229 
230 __init int register_ftrace_syscalls(void)
231 {
232 	int ret;
233 
234 	ret = register_ftrace_event(&syscall_enter_event);
235 	if (!ret) {
236 		printk(KERN_WARNING "event %d failed to register\n",
237 		       syscall_enter_event.type);
238 		WARN_ON_ONCE(1);
239 	}
240 
241 	ret = register_ftrace_event(&syscall_exit_event);
242 	if (!ret) {
243 		printk(KERN_WARNING "event %d failed to register\n",
244 		       syscall_exit_event.type);
245 		WARN_ON_ONCE(1);
246 	}
247 
248 	return register_tracer(&syscall_tracer);
249 }
250 device_initcall(register_ftrace_syscalls);
251