Blame view
kernel/trace/trace_syscalls.c
16.5 KB
47788c58e
|
1 |
#include <trace/syscall.h> |
1c569f026
|
2 |
#include <trace/events/syscalls.h> |
5a0e3ad6a
|
3 |
#include <linux/slab.h> |
ee08c6ecc
|
4 |
#include <linux/kernel.h> |
fb34a08c3
|
5 |
#include <linux/ftrace.h> |
cdd6c482c
|
6 |
#include <linux/perf_event.h> |
ee08c6ecc
|
7 8 9 10 |
#include <asm/syscall.h> #include "trace_output.h" #include "trace.h" |
5be71b61f
|
11 |
static DEFINE_MUTEX(syscall_trace_lock); |
fb34a08c3
|
12 13 |
static int sys_refcount_enter; static int sys_refcount_exit; |
57421dbbd
|
14 15 |
static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls); static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls); |
ee08c6ecc
|
16 |
|
2239291ae
|
17 18 19 20 |
static int syscall_enter_register(struct ftrace_event_call *event, enum trace_reg type); static int syscall_exit_register(struct ftrace_event_call *event, enum trace_reg type); |
2e33af029
|
21 22 23 24 25 26 27 28 29 30 |
static int syscall_enter_define_fields(struct ftrace_event_call *call); static int syscall_exit_define_fields(struct ftrace_event_call *call); static struct list_head * syscall_get_enter_fields(struct ftrace_event_call *call) { struct syscall_metadata *entry = call->data; return &entry->enter_fields; } |
80decc70a
|
31 |
struct trace_event_functions enter_syscall_print_funcs = { |
7f85803a2
|
32 |
.trace = print_syscall_enter, |
80decc70a
|
33 34 35 |
}; struct trace_event_functions exit_syscall_print_funcs = { |
7f85803a2
|
36 |
.trace = print_syscall_exit, |
80decc70a
|
37 |
}; |
2239291ae
|
38 |
struct ftrace_event_class event_class_syscall_enter = { |
7f85803a2
|
39 40 41 42 43 |
.system = "syscalls", .reg = syscall_enter_register, .define_fields = syscall_enter_define_fields, .get_fields = syscall_get_enter_fields, .raw_init = init_syscall_trace, |
2239291ae
|
44 45 46 |
}; struct ftrace_event_class event_class_syscall_exit = { |
7f85803a2
|
47 48 49 50 51 |
.system = "syscalls", .reg = syscall_exit_register, .define_fields = syscall_exit_define_fields, .fields = LIST_HEAD_INIT(event_class_syscall_exit.fields), .raw_init = init_syscall_trace, |
8f0820183
|
52 |
}; |
3d56e331b
|
53 54 |
extern struct syscall_metadata *__start_syscalls_metadata[]; extern struct syscall_metadata *__stop_syscalls_metadata[]; |
c44fc7708
|
55 56 |
static struct syscall_metadata **syscalls_metadata; |
b2d554968
|
57 58 59 60 61 62 63 64 65 66 67 68 |
#ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME static inline bool arch_syscall_match_sym_name(const char *sym, const char *name) { /* * Only compare after the "sys" prefix. Archs that use * syscall wrappers may have syscalls symbols aliases prefixed * with "SyS" instead of "sys", leading to an unwanted * mismatch. */ return !strcmp(sym + 3, name + 3); } #endif |
3d56e331b
|
69 70 |
static __init struct syscall_metadata * find_syscall_meta(unsigned long syscall) |
c44fc7708
|
71 |
{ |
3d56e331b
|
72 73 |
struct syscall_metadata **start; struct syscall_metadata **stop; |
c44fc7708
|
74 |
char str[KSYM_SYMBOL_LEN]; |
3d56e331b
|
75 76 |
start = __start_syscalls_metadata; stop = __stop_syscalls_metadata; |
c44fc7708
|
77 |
kallsyms_lookup(syscall, NULL, NULL, NULL, str); |
ae07f551c
|
78 79 |
if (arch_syscall_match_sym_name(str, "sys_ni_syscall")) return NULL; |
c44fc7708
|
80 |
for ( ; start < stop; start++) { |
b2d554968
|
81 |
if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name)) |
3d56e331b
|
82 |
return *start; |
c44fc7708
|
83 84 85 86 87 88 89 90 91 92 93 |
} return NULL; } static struct syscall_metadata *syscall_nr_to_meta(int nr) { if (!syscalls_metadata || nr >= NR_syscalls || nr < 0) return NULL; return syscalls_metadata[nr]; } |
bed1ffca0
|
94 |
enum print_line_t |
a9a577638
|
95 96 |
print_syscall_enter(struct trace_iterator *iter, int flags, struct trace_event *event) |
bed1ffca0
|
97 98 99 100 101 102 |
{ struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_enter *trace; struct syscall_metadata *entry; int i, ret, syscall; |
64c12e044
|
103 |
trace = (typeof(trace))ent; |
bed1ffca0
|
104 |
syscall = trace->nr; |
bed1ffca0
|
105 |
entry = syscall_nr_to_meta(syscall); |
64c12e044
|
106 |
|
bed1ffca0
|
107 108 |
if (!entry) goto end; |
32c0edaea
|
109 |
if (entry->enter_event->event.type != ent->type) { |
64c12e044
|
110 111 112 |
WARN_ON_ONCE(1); goto end; } |
bed1ffca0
|
113 114 115 116 117 118 |
ret = trace_seq_printf(s, "%s(", entry->name); if (!ret) return TRACE_TYPE_PARTIAL_LINE; for (i = 0; i < entry->nb_args; i++) { /* parameter types */ |
ba8b3a40b
|
119 |
if (trace_flags & TRACE_ITER_VERBOSE) { |
bed1ffca0
|
120 121 122 123 124 |
ret = trace_seq_printf(s, "%s ", entry->types[i]); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } /* parameter values */ |
4539f0770
|
125 |
ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i], |
bed1ffca0
|
126 |
trace->args[i], |
4539f0770
|
127 |
i == entry->nb_args - 1 ? "" : ", "); |
bed1ffca0
|
128 129 130 |
if (!ret) return TRACE_TYPE_PARTIAL_LINE; } |
4539f0770
|
131 132 133 |
ret = trace_seq_putc(s, ')'); if (!ret) return TRACE_TYPE_PARTIAL_LINE; |
bed1ffca0
|
134 |
end: |
4539f0770
|
135 136 137 138 |
ret = trace_seq_putc(s, ' '); if (!ret) return TRACE_TYPE_PARTIAL_LINE; |
bed1ffca0
|
139 140 141 142 |
return TRACE_TYPE_HANDLED; } enum print_line_t |
a9a577638
|
143 144 |
print_syscall_exit(struct trace_iterator *iter, int flags, struct trace_event *event) |
bed1ffca0
|
145 146 147 148 149 150 151 |
{ struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_exit *trace; int syscall; struct syscall_metadata *entry; int ret; |
64c12e044
|
152 |
trace = (typeof(trace))ent; |
bed1ffca0
|
153 |
syscall = trace->nr; |
bed1ffca0
|
154 |
entry = syscall_nr_to_meta(syscall); |
64c12e044
|
155 |
|
bed1ffca0
|
156 157 158 159 160 |
if (!entry) { trace_seq_printf(s, " "); return TRACE_TYPE_HANDLED; } |
32c0edaea
|
161 |
if (entry->exit_event->event.type != ent->type) { |
64c12e044
|
162 163 164 |
WARN_ON_ONCE(1); return TRACE_TYPE_UNHANDLED; } |
bed1ffca0
|
165 166 167 168 169 170 171 172 |
ret = trace_seq_printf(s, "%s -> 0x%lx ", entry->name, trace->ret); if (!ret) return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_HANDLED; } |
e6971969c
|
173 174 175 176 177 |
extern char *__bad_type_size(void); #define SYSCALL_FIELD(type, name) \ sizeof(type) != sizeof(trace.name) ? \ __bad_type_size() : \ |
26a50744b
|
178 179 |
#type, #name, offsetof(typeof(trace), name), \ sizeof(trace.name), is_signed_type(type) |
e6971969c
|
180 |
|
50307a45f
|
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
static int __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) { int i; int pos = 0; /* When len=0, we just calculate the needed length */ #define LEN_OR_ZERO (len ? len - pos : 0) pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); for (i = 0; i < entry->nb_args; i++) { pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s", entry->args[i], sizeof(unsigned long), i == entry->nb_args - 1 ? "" : ", "); } pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); for (i = 0; i < entry->nb_args; i++) { pos += snprintf(buf + pos, LEN_OR_ZERO, ", ((unsigned long)(REC->%s))", entry->args[i]); } #undef LEN_OR_ZERO /* return the length of print_fmt */ return pos; } static int set_syscall_print_fmt(struct ftrace_event_call *call) { char *print_fmt; int len; struct syscall_metadata *entry = call->data; if (entry->enter_event != call) { call->print_fmt = "\"0x%lx\", REC->ret"; return 0; } /* First: called with 0 length to calculate the needed length */ len = __set_enter_print_fmt(entry, NULL, 0); print_fmt = kmalloc(len + 1, GFP_KERNEL); if (!print_fmt) return -ENOMEM; /* Second: actually write the @print_fmt */ __set_enter_print_fmt(entry, print_fmt, len + 1); call->print_fmt = print_fmt; return 0; } static void free_syscall_print_fmt(struct ftrace_event_call *call) { struct syscall_metadata *entry = call->data; if (entry->enter_event == call) kfree(call->print_fmt); } |
2e33af029
|
241 |
static int syscall_enter_define_fields(struct ftrace_event_call *call) |
540b7b8d6
|
242 243 |
{ struct syscall_trace_enter trace; |
31c16b133
|
244 |
struct syscall_metadata *meta = call->data; |
540b7b8d6
|
245 |
int ret; |
540b7b8d6
|
246 247 |
int i; int offset = offsetof(typeof(trace), args); |
0f1ef51d2
|
248 249 250 |
ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER); if (ret) return ret; |
540b7b8d6
|
251 |
for (i = 0; i < meta->nb_args; i++) { |
aeaeae118
|
252 253 |
ret = trace_define_field(call, meta->types[i], meta->args[i], offset, |
43b51ead3
|
254 255 |
sizeof(unsigned long), 0, FILTER_OTHER); |
540b7b8d6
|
256 257 258 259 260 |
offset += sizeof(unsigned long); } return ret; } |
2e33af029
|
261 |
static int syscall_exit_define_fields(struct ftrace_event_call *call) |
540b7b8d6
|
262 263 264 |
{ struct syscall_trace_exit trace; int ret; |
0f1ef51d2
|
265 266 267 |
ret = trace_define_field(call, SYSCALL_FIELD(int, nr), FILTER_OTHER); if (ret) return ret; |
26a50744b
|
268 |
ret = trace_define_field(call, SYSCALL_FIELD(long, ret), |
43b51ead3
|
269 |
FILTER_OTHER); |
540b7b8d6
|
270 271 272 |
return ret; } |
38516ab59
|
273 |
void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id) |
ee08c6ecc
|
274 |
{ |
bed1ffca0
|
275 276 277 |
struct syscall_trace_enter *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; |
e77405ad8
|
278 |
struct ring_buffer *buffer; |
bed1ffca0
|
279 |
int size; |
ee08c6ecc
|
280 281 282 |
int syscall_nr; syscall_nr = syscall_get_nr(current, regs); |
cd0980fc8
|
283 284 |
if (syscall_nr < 0) return; |
fb34a08c3
|
285 286 |
if (!test_bit(syscall_nr, enabled_enter_syscalls)) return; |
ee08c6ecc
|
287 |
|
bed1ffca0
|
288 289 290 291 292 |
sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; |
fcc19438d
|
293 |
event = trace_current_buffer_lock_reserve(&buffer, |
32c0edaea
|
294 |
sys_data->enter_event->event.type, size, 0, 0); |
bed1ffca0
|
295 296 297 298 299 300 |
if (!event) return; entry = ring_buffer_event_data(event); entry->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); |
e77405ad8
|
301 302 303 |
if (!filter_current_check_discard(buffer, sys_data->enter_event, entry, event)) trace_current_buffer_unlock_commit(buffer, event, 0, 0); |
ee08c6ecc
|
304 |
} |
38516ab59
|
305 |
void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret) |
ee08c6ecc
|
306 |
{ |
bed1ffca0
|
307 308 309 |
struct syscall_trace_exit *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; |
e77405ad8
|
310 |
struct ring_buffer *buffer; |
ee08c6ecc
|
311 312 313 |
int syscall_nr; syscall_nr = syscall_get_nr(current, regs); |
cd0980fc8
|
314 315 |
if (syscall_nr < 0) return; |
fb34a08c3
|
316 317 |
if (!test_bit(syscall_nr, enabled_exit_syscalls)) return; |
ee08c6ecc
|
318 |
|
bed1ffca0
|
319 320 321 |
sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; |
fcc19438d
|
322 |
event = trace_current_buffer_lock_reserve(&buffer, |
32c0edaea
|
323 |
sys_data->exit_event->event.type, sizeof(*entry), 0, 0); |
bed1ffca0
|
324 325 326 327 328 329 |
if (!event) return; entry = ring_buffer_event_data(event); entry->nr = syscall_nr; entry->ret = syscall_get_return_value(current, regs); |
e77405ad8
|
330 331 332 |
if (!filter_current_check_discard(buffer, sys_data->exit_event, entry, event)) trace_current_buffer_unlock_commit(buffer, event, 0, 0); |
ee08c6ecc
|
333 |
} |
bd1a5c849
|
334 |
int reg_event_syscall_enter(struct ftrace_event_call *call) |
ee08c6ecc
|
335 |
{ |
fb34a08c3
|
336 337 |
int ret = 0; int num; |
fb34a08c3
|
338 |
|
c252f6579
|
339 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
3773b389b
|
340 |
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls)) |
fb34a08c3
|
341 342 343 |
return -ENOSYS; mutex_lock(&syscall_trace_lock); if (!sys_refcount_enter) |
38516ab59
|
344 |
ret = register_trace_sys_enter(ftrace_syscall_enter, NULL); |
3b8e42738
|
345 |
if (!ret) { |
fb34a08c3
|
346 347 348 349 350 |
set_bit(num, enabled_enter_syscalls); sys_refcount_enter++; } mutex_unlock(&syscall_trace_lock); return ret; |
ee08c6ecc
|
351 |
} |
bd1a5c849
|
352 |
void unreg_event_syscall_enter(struct ftrace_event_call *call) |
ee08c6ecc
|
353 |
{ |
fb34a08c3
|
354 |
int num; |
ee08c6ecc
|
355 |
|
c252f6579
|
356 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
3773b389b
|
357 |
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls)) |
fb34a08c3
|
358 359 360 361 362 |
return; mutex_lock(&syscall_trace_lock); sys_refcount_enter--; clear_bit(num, enabled_enter_syscalls); if (!sys_refcount_enter) |
38516ab59
|
363 |
unregister_trace_sys_enter(ftrace_syscall_enter, NULL); |
fb34a08c3
|
364 365 |
mutex_unlock(&syscall_trace_lock); } |
ee08c6ecc
|
366 |
|
bd1a5c849
|
367 |
int reg_event_syscall_exit(struct ftrace_event_call *call) |
ee08c6ecc
|
368 |
{ |
fb34a08c3
|
369 370 |
int ret = 0; int num; |
fb34a08c3
|
371 |
|
c252f6579
|
372 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
3773b389b
|
373 |
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls)) |
fb34a08c3
|
374 375 376 |
return -ENOSYS; mutex_lock(&syscall_trace_lock); if (!sys_refcount_exit) |
38516ab59
|
377 |
ret = register_trace_sys_exit(ftrace_syscall_exit, NULL); |
3b8e42738
|
378 |
if (!ret) { |
fb34a08c3
|
379 380 |
set_bit(num, enabled_exit_syscalls); sys_refcount_exit++; |
ee08c6ecc
|
381 |
} |
fb34a08c3
|
382 383 384 |
mutex_unlock(&syscall_trace_lock); return ret; } |
ee08c6ecc
|
385 |
|
bd1a5c849
|
386 |
void unreg_event_syscall_exit(struct ftrace_event_call *call) |
fb34a08c3
|
387 388 |
{ int num; |
ee08c6ecc
|
389 |
|
c252f6579
|
390 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
3773b389b
|
391 |
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls)) |
fb34a08c3
|
392 393 394 395 396 |
return; mutex_lock(&syscall_trace_lock); sys_refcount_exit--; clear_bit(num, enabled_exit_syscalls); if (!sys_refcount_exit) |
38516ab59
|
397 |
unregister_trace_sys_exit(ftrace_syscall_exit, NULL); |
fb34a08c3
|
398 |
mutex_unlock(&syscall_trace_lock); |
ee08c6ecc
|
399 |
} |
fb34a08c3
|
400 |
|
a1301da09
|
401 402 403 |
int init_syscall_trace(struct ftrace_event_call *call) { int id; |
ba976970c
|
404 405 406 407 408 409 410 411 412 |
int num; num = ((struct syscall_metadata *)call->data)->syscall_nr; if (num < 0 || num >= NR_syscalls) { pr_debug("syscall %s metadata not mapped, disabling ftrace event ", ((struct syscall_metadata *)call->data)->name); return -ENOSYS; } |
a1301da09
|
413 |
|
50307a45f
|
414 415 |
if (set_syscall_print_fmt(call) < 0) return -ENOMEM; |
c7ef3a900
|
416 417 418 |
id = trace_event_raw_init(call); if (id < 0) { |
50307a45f
|
419 |
free_syscall_print_fmt(call); |
c7ef3a900
|
420 |
return id; |
50307a45f
|
421 |
} |
c7ef3a900
|
422 423 |
return id; |
a1301da09
|
424 |
} |
c763ba06b
|
425 |
unsigned long __init __weak arch_syscall_addr(int nr) |
e7b8e675d
|
426 427 428 |
{ return (unsigned long)sys_call_table[nr]; } |
c44fc7708
|
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
int __init init_ftrace_syscalls(void) { struct syscall_metadata *meta; unsigned long addr; int i; syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) * NR_syscalls, GFP_KERNEL); if (!syscalls_metadata) { WARN_ON(1); return -ENOMEM; } for (i = 0; i < NR_syscalls; i++) { addr = arch_syscall_addr(i); meta = find_syscall_meta(addr); |
c252f6579
|
445 446 447 448 |
if (!meta) continue; meta->syscall_nr = i; |
c44fc7708
|
449 450 451 452 453 454 |
syscalls_metadata[i] = meta; } return 0; } core_initcall(init_ftrace_syscalls); |
07b139c8c
|
455 |
#ifdef CONFIG_PERF_EVENTS |
19007a67a
|
456 |
|
97d5a2200
|
457 458 459 460 |
static DECLARE_BITMAP(enabled_perf_enter_syscalls, NR_syscalls); static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls); static int sys_perf_refcount_enter; static int sys_perf_refcount_exit; |
f4b5ffccc
|
461 |
|
38516ab59
|
462 |
static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) |
f4b5ffccc
|
463 464 |
{ struct syscall_metadata *sys_data; |
20ab4425a
|
465 |
struct syscall_trace_enter *rec; |
1c024eca5
|
466 |
struct hlist_head *head; |
f4b5ffccc
|
467 |
int syscall_nr; |
4ed7c92d6
|
468 |
int rctx; |
19007a67a
|
469 |
int size; |
f4b5ffccc
|
470 471 |
syscall_nr = syscall_get_nr(current, regs); |
97d5a2200
|
472 |
if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) |
f4b5ffccc
|
473 474 475 476 477 |
return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; |
19007a67a
|
478 479 480 481 |
/* get the size after alignment with the u32 buffer size field */ size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec); size = ALIGN(size + sizeof(u32), sizeof(u64)); size -= sizeof(u32); |
97d5a2200
|
482 483 |
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "perf buffer not large enough")) |
20ab4425a
|
484 |
return; |
97d5a2200
|
485 |
rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size, |
ff5f149b6
|
486 |
sys_data->enter_event->event.type, regs, &rctx); |
430ad5a60
|
487 488 |
if (!rec) return; |
20ab4425a
|
489 |
|
20ab4425a
|
490 491 492 |
rec->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); |
1c024eca5
|
493 |
|
3771f0771
|
494 |
head = this_cpu_ptr(sys_data->enter_event->perf_events); |
1c024eca5
|
495 |
perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head); |
f4b5ffccc
|
496 |
} |
97d5a2200
|
497 |
int perf_sysenter_enable(struct ftrace_event_call *call) |
f4b5ffccc
|
498 499 500 |
{ int ret = 0; int num; |
3bbe84e9d
|
501 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
f4b5ffccc
|
502 503 |
mutex_lock(&syscall_trace_lock); |
97d5a2200
|
504 |
if (!sys_perf_refcount_enter) |
38516ab59
|
505 |
ret = register_trace_sys_enter(perf_syscall_enter, NULL); |
f4b5ffccc
|
506 507 508 509 |
if (ret) { pr_info("event trace: Could not activate" "syscall entry trace point"); } else { |
97d5a2200
|
510 511 |
set_bit(num, enabled_perf_enter_syscalls); sys_perf_refcount_enter++; |
f4b5ffccc
|
512 513 514 515 |
} mutex_unlock(&syscall_trace_lock); return ret; } |
97d5a2200
|
516 |
void perf_sysenter_disable(struct ftrace_event_call *call) |
f4b5ffccc
|
517 518 |
{ int num; |
3bbe84e9d
|
519 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
f4b5ffccc
|
520 521 |
mutex_lock(&syscall_trace_lock); |
97d5a2200
|
522 523 524 |
sys_perf_refcount_enter--; clear_bit(num, enabled_perf_enter_syscalls); if (!sys_perf_refcount_enter) |
38516ab59
|
525 |
unregister_trace_sys_enter(perf_syscall_enter, NULL); |
f4b5ffccc
|
526 527 |
mutex_unlock(&syscall_trace_lock); } |
38516ab59
|
528 |
static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) |
f4b5ffccc
|
529 530 |
{ struct syscall_metadata *sys_data; |
20ab4425a
|
531 |
struct syscall_trace_exit *rec; |
1c024eca5
|
532 |
struct hlist_head *head; |
f4b5ffccc
|
533 |
int syscall_nr; |
4ed7c92d6
|
534 |
int rctx; |
20ab4425a
|
535 |
int size; |
f4b5ffccc
|
536 537 |
syscall_nr = syscall_get_nr(current, regs); |
97d5a2200
|
538 |
if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) |
f4b5ffccc
|
539 540 541 542 543 |
return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; |
20ab4425a
|
544 545 546 |
/* We can probably do that at build time */ size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); size -= sizeof(u32); |
19007a67a
|
547 |
|
20ab4425a
|
548 549 550 551 |
/* * Impossible, but be paranoid with the future * How to put this check outside runtime? */ |
97d5a2200
|
552 553 |
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "exit event has grown above perf buffer size")) |
20ab4425a
|
554 |
return; |
97d5a2200
|
555 |
rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size, |
ff5f149b6
|
556 |
sys_data->exit_event->event.type, regs, &rctx); |
430ad5a60
|
557 558 |
if (!rec) return; |
20ab4425a
|
559 |
|
20ab4425a
|
560 561 |
rec->nr = syscall_nr; rec->ret = syscall_get_return_value(current, regs); |
3771f0771
|
562 |
head = this_cpu_ptr(sys_data->exit_event->perf_events); |
1c024eca5
|
563 |
perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head); |
f4b5ffccc
|
564 |
} |
97d5a2200
|
565 |
int perf_sysexit_enable(struct ftrace_event_call *call) |
f4b5ffccc
|
566 567 568 |
{ int ret = 0; int num; |
3bbe84e9d
|
569 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
f4b5ffccc
|
570 571 |
mutex_lock(&syscall_trace_lock); |
97d5a2200
|
572 |
if (!sys_perf_refcount_exit) |
38516ab59
|
573 |
ret = register_trace_sys_exit(perf_syscall_exit, NULL); |
f4b5ffccc
|
574 575 |
if (ret) { pr_info("event trace: Could not activate" |
6574658b3
|
576 |
"syscall exit trace point"); |
f4b5ffccc
|
577 |
} else { |
97d5a2200
|
578 579 |
set_bit(num, enabled_perf_exit_syscalls); sys_perf_refcount_exit++; |
f4b5ffccc
|
580 581 582 583 |
} mutex_unlock(&syscall_trace_lock); return ret; } |
97d5a2200
|
584 |
void perf_sysexit_disable(struct ftrace_event_call *call) |
f4b5ffccc
|
585 586 |
{ int num; |
3bbe84e9d
|
587 |
num = ((struct syscall_metadata *)call->data)->syscall_nr; |
f4b5ffccc
|
588 589 |
mutex_lock(&syscall_trace_lock); |
97d5a2200
|
590 591 592 |
sys_perf_refcount_exit--; clear_bit(num, enabled_perf_exit_syscalls); if (!sys_perf_refcount_exit) |
38516ab59
|
593 |
unregister_trace_sys_exit(perf_syscall_exit, NULL); |
f4b5ffccc
|
594 595 |
mutex_unlock(&syscall_trace_lock); } |
07b139c8c
|
596 |
#endif /* CONFIG_PERF_EVENTS */ |
f4b5ffccc
|
597 |
|
2239291ae
|
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 |
static int syscall_enter_register(struct ftrace_event_call *event, enum trace_reg type) { switch (type) { case TRACE_REG_REGISTER: return reg_event_syscall_enter(event); case TRACE_REG_UNREGISTER: unreg_event_syscall_enter(event); return 0; #ifdef CONFIG_PERF_EVENTS case TRACE_REG_PERF_REGISTER: return perf_sysenter_enable(event); case TRACE_REG_PERF_UNREGISTER: perf_sysenter_disable(event); return 0; #endif } return 0; } static int syscall_exit_register(struct ftrace_event_call *event, enum trace_reg type) { switch (type) { case TRACE_REG_REGISTER: return reg_event_syscall_exit(event); case TRACE_REG_UNREGISTER: unreg_event_syscall_exit(event); return 0; #ifdef CONFIG_PERF_EVENTS case TRACE_REG_PERF_REGISTER: return perf_sysexit_enable(event); case TRACE_REG_PERF_UNREGISTER: perf_sysexit_disable(event); return 0; #endif } return 0; } |