diff options
Diffstat (limited to 'kernel/trace/trace_syscalls.c')
| -rw-r--r-- | kernel/trace/trace_syscalls.c | 189 |
1 files changed, 71 insertions, 118 deletions
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 75289f372dd2..cba47d7935cc 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c | |||
| @@ -143,70 +143,65 @@ extern char *__bad_type_size(void); | |||
| 143 | #type, #name, offsetof(typeof(trace), name), \ | 143 | #type, #name, offsetof(typeof(trace), name), \ |
| 144 | sizeof(trace.name), is_signed_type(type) | 144 | sizeof(trace.name), is_signed_type(type) |
| 145 | 145 | ||
| 146 | int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s) | 146 | static |
| 147 | int __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) | ||
| 147 | { | 148 | { |
| 148 | int i; | 149 | int i; |
| 149 | int ret; | 150 | int pos = 0; |
| 150 | struct syscall_metadata *entry = call->data; | ||
| 151 | struct syscall_trace_enter trace; | ||
| 152 | int offset = offsetof(struct syscall_trace_enter, args); | ||
| 153 | 151 | ||
| 154 | ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;" | 152 | /* When len=0, we just calculate the needed length */ |
| 155 | "\tsigned:%u;\n", | 153 | #define LEN_OR_ZERO (len ? len - pos : 0) |
| 156 | SYSCALL_FIELD(int, nr)); | ||
| 157 | if (!ret) | ||
| 158 | return 0; | ||
| 159 | 154 | ||
| 155 | pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); | ||
| 160 | for (i = 0; i < entry->nb_args; i++) { | 156 | for (i = 0; i < entry->nb_args; i++) { |
| 161 | ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i], | 157 | pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s", |
| 162 | entry->args[i]); | 158 | entry->args[i], sizeof(unsigned long), |
| 163 | if (!ret) | 159 | i == entry->nb_args - 1 ? "" : ", "); |
| 164 | return 0; | ||
| 165 | ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;" | ||
| 166 | "\tsigned:%u;\n", offset, | ||
| 167 | sizeof(unsigned long), | ||
| 168 | is_signed_type(unsigned long)); | ||
| 169 | if (!ret) | ||
| 170 | return 0; | ||
| 171 | offset += sizeof(unsigned long); | ||
| 172 | } | 160 | } |
| 161 | pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); | ||
| 173 | 162 | ||
| 174 | trace_seq_puts(s, "\nprint fmt: \""); | ||
| 175 | for (i = 0; i < entry->nb_args; i++) { | 163 | for (i = 0; i < entry->nb_args; i++) { |
| 176 | ret = trace_seq_printf(s, "%s: 0x%%0%zulx%s", entry->args[i], | 164 | pos += snprintf(buf + pos, LEN_OR_ZERO, |
| 177 | sizeof(unsigned long), | 165 | ", ((unsigned long)(REC->%s))", entry->args[i]); |
| 178 | i == entry->nb_args - 1 ? "" : ", "); | ||
| 179 | if (!ret) | ||
| 180 | return 0; | ||
| 181 | } | 166 | } |
| 182 | trace_seq_putc(s, '"'); | ||
| 183 | 167 | ||
| 184 | for (i = 0; i < entry->nb_args; i++) { | 168 | #undef LEN_OR_ZERO |
| 185 | ret = trace_seq_printf(s, ", ((unsigned long)(REC->%s))", | ||
| 186 | entry->args[i]); | ||
| 187 | if (!ret) | ||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | 169 | ||
| 191 | return trace_seq_putc(s, '\n'); | 170 | /* return the length of print_fmt */ |
| 171 | return pos; | ||
| 192 | } | 172 | } |
| 193 | 173 | ||
| 194 | int syscall_exit_format(struct ftrace_event_call *call, struct trace_seq *s) | 174 | static int set_syscall_print_fmt(struct ftrace_event_call *call) |
| 195 | { | 175 | { |
| 196 | int ret; | 176 | char *print_fmt; |
| 197 | struct syscall_trace_exit trace; | 177 | int len; |
| 178 | struct syscall_metadata *entry = call->data; | ||
| 198 | 179 | ||
| 199 | ret = trace_seq_printf(s, | 180 | if (entry->enter_event != call) { |
| 200 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;" | 181 | call->print_fmt = "\"0x%lx\", REC->ret"; |
| 201 | "\tsigned:%u;\n" | ||
| 202 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;" | ||
| 203 | "\tsigned:%u;\n", | ||
| 204 | SYSCALL_FIELD(int, nr), | ||
| 205 | SYSCALL_FIELD(long, ret)); | ||
| 206 | if (!ret) | ||
| 207 | return 0; | 182 | return 0; |
| 183 | } | ||
| 184 | |||
| 185 | /* First: called with 0 length to calculate the needed length */ | ||
| 186 | len = __set_enter_print_fmt(entry, NULL, 0); | ||
| 187 | |||
| 188 | print_fmt = kmalloc(len + 1, GFP_KERNEL); | ||
| 189 | if (!print_fmt) | ||
| 190 | return -ENOMEM; | ||
| 191 | |||
| 192 | /* Second: actually write the @print_fmt */ | ||
| 193 | __set_enter_print_fmt(entry, print_fmt, len + 1); | ||
| 194 | call->print_fmt = print_fmt; | ||
| 208 | 195 | ||
| 209 | return trace_seq_printf(s, "\nprint fmt: \"0x%%lx\", REC->ret\n"); | 196 | return 0; |
| 197 | } | ||
| 198 | |||
| 199 | static void free_syscall_print_fmt(struct ftrace_event_call *call) | ||
| 200 | { | ||
| 201 | struct syscall_metadata *entry = call->data; | ||
| 202 | |||
| 203 | if (entry->enter_event == call) | ||
| 204 | kfree(call->print_fmt); | ||
| 210 | } | 205 | } |
| 211 | 206 | ||
| 212 | int syscall_enter_define_fields(struct ftrace_event_call *call) | 207 | int syscall_enter_define_fields(struct ftrace_event_call *call) |
| @@ -386,12 +381,22 @@ int init_syscall_trace(struct ftrace_event_call *call) | |||
| 386 | { | 381 | { |
| 387 | int id; | 382 | int id; |
| 388 | 383 | ||
| 389 | id = register_ftrace_event(call->event); | 384 | if (set_syscall_print_fmt(call) < 0) |
| 390 | if (!id) | 385 | return -ENOMEM; |
| 391 | return -ENODEV; | 386 | |
| 392 | call->id = id; | 387 | id = trace_event_raw_init(call); |
| 393 | INIT_LIST_HEAD(&call->fields); | 388 | |
| 394 | return 0; | 389 | if (id < 0) { |
| 390 | free_syscall_print_fmt(call); | ||
| 391 | return id; | ||
| 392 | } | ||
| 393 | |||
| 394 | return id; | ||
| 395 | } | ||
| 396 | |||
| 397 | unsigned long __init arch_syscall_addr(int nr) | ||
| 398 | { | ||
| 399 | return (unsigned long)sys_call_table[nr]; | ||
| 395 | } | 400 | } |
| 396 | 401 | ||
| 397 | int __init init_ftrace_syscalls(void) | 402 | int __init init_ftrace_syscalls(void) |
| @@ -421,7 +426,7 @@ int __init init_ftrace_syscalls(void) | |||
| 421 | } | 426 | } |
| 422 | core_initcall(init_ftrace_syscalls); | 427 | core_initcall(init_ftrace_syscalls); |
| 423 | 428 | ||
| 424 | #ifdef CONFIG_EVENT_PROFILE | 429 | #ifdef CONFIG_PERF_EVENTS |
| 425 | 430 | ||
| 426 | static DECLARE_BITMAP(enabled_prof_enter_syscalls, NR_syscalls); | 431 | static DECLARE_BITMAP(enabled_prof_enter_syscalls, NR_syscalls); |
| 427 | static DECLARE_BITMAP(enabled_prof_exit_syscalls, NR_syscalls); | 432 | static DECLARE_BITMAP(enabled_prof_exit_syscalls, NR_syscalls); |
| @@ -433,12 +438,9 @@ static void prof_syscall_enter(struct pt_regs *regs, long id) | |||
| 433 | struct syscall_metadata *sys_data; | 438 | struct syscall_metadata *sys_data; |
| 434 | struct syscall_trace_enter *rec; | 439 | struct syscall_trace_enter *rec; |
| 435 | unsigned long flags; | 440 | unsigned long flags; |
| 436 | char *trace_buf; | ||
| 437 | char *raw_data; | ||
| 438 | int syscall_nr; | 441 | int syscall_nr; |
| 439 | int rctx; | 442 | int rctx; |
| 440 | int size; | 443 | int size; |
| 441 | int cpu; | ||
| 442 | 444 | ||
| 443 | syscall_nr = syscall_get_nr(current, regs); | 445 | syscall_nr = syscall_get_nr(current, regs); |
| 444 | if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) | 446 | if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) |
| @@ -457,37 +459,15 @@ static void prof_syscall_enter(struct pt_regs *regs, long id) | |||
| 457 | "profile buffer not large enough")) | 459 | "profile buffer not large enough")) |
| 458 | return; | 460 | return; |
| 459 | 461 | ||
| 460 | /* Protect the per cpu buffer, begin the rcu read side */ | 462 | rec = (struct syscall_trace_enter *)ftrace_perf_buf_prepare(size, |
| 461 | local_irq_save(flags); | 463 | sys_data->enter_event->id, &rctx, &flags); |
| 462 | 464 | if (!rec) | |
| 463 | rctx = perf_swevent_get_recursion_context(); | 465 | return; |
| 464 | if (rctx < 0) | ||
| 465 | goto end_recursion; | ||
| 466 | |||
| 467 | cpu = smp_processor_id(); | ||
| 468 | |||
| 469 | trace_buf = rcu_dereference(perf_trace_buf); | ||
| 470 | |||
| 471 | if (!trace_buf) | ||
| 472 | goto end; | ||
| 473 | |||
| 474 | raw_data = per_cpu_ptr(trace_buf, cpu); | ||
| 475 | |||
| 476 | /* zero the dead bytes from align to not leak stack to user */ | ||
| 477 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; | ||
| 478 | 466 | ||
| 479 | rec = (struct syscall_trace_enter *) raw_data; | ||
| 480 | tracing_generic_entry_update(&rec->ent, 0, 0); | ||
| 481 | rec->ent.type = sys_data->enter_event->id; | ||
| 482 | rec->nr = syscall_nr; | 467 | rec->nr = syscall_nr; |
| 483 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, | 468 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, |
| 484 | (unsigned long *)&rec->args); | 469 | (unsigned long *)&rec->args); |
| 485 | perf_tp_event(sys_data->enter_event->id, 0, 1, rec, size); | 470 | ftrace_perf_buf_submit(rec, size, rctx, 0, 1, flags); |
| 486 | |||
| 487 | end: | ||
| 488 | perf_swevent_put_recursion_context(rctx); | ||
| 489 | end_recursion: | ||
| 490 | local_irq_restore(flags); | ||
| 491 | } | 471 | } |
| 492 | 472 | ||
| 493 | int prof_sysenter_enable(struct ftrace_event_call *call) | 473 | int prof_sysenter_enable(struct ftrace_event_call *call) |
| @@ -531,11 +511,8 @@ static void prof_syscall_exit(struct pt_regs *regs, long ret) | |||
| 531 | struct syscall_trace_exit *rec; | 511 | struct syscall_trace_exit *rec; |
| 532 | unsigned long flags; | 512 | unsigned long flags; |
| 533 | int syscall_nr; | 513 | int syscall_nr; |
| 534 | char *trace_buf; | ||
| 535 | char *raw_data; | ||
| 536 | int rctx; | 514 | int rctx; |
| 537 | int size; | 515 | int size; |
| 538 | int cpu; | ||
| 539 | 516 | ||
| 540 | syscall_nr = syscall_get_nr(current, regs); | 517 | syscall_nr = syscall_get_nr(current, regs); |
| 541 | if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) | 518 | if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) |
| @@ -557,38 +534,15 @@ static void prof_syscall_exit(struct pt_regs *regs, long ret) | |||
| 557 | "exit event has grown above profile buffer size")) | 534 | "exit event has grown above profile buffer size")) |
| 558 | return; | 535 | return; |
| 559 | 536 | ||
| 560 | /* Protect the per cpu buffer, begin the rcu read side */ | 537 | rec = (struct syscall_trace_exit *)ftrace_perf_buf_prepare(size, |
| 561 | local_irq_save(flags); | 538 | sys_data->exit_event->id, &rctx, &flags); |
| 562 | 539 | if (!rec) | |
| 563 | rctx = perf_swevent_get_recursion_context(); | 540 | return; |
| 564 | if (rctx < 0) | ||
| 565 | goto end_recursion; | ||
| 566 | |||
| 567 | cpu = smp_processor_id(); | ||
| 568 | |||
| 569 | trace_buf = rcu_dereference(perf_trace_buf); | ||
| 570 | |||
| 571 | if (!trace_buf) | ||
| 572 | goto end; | ||
| 573 | |||
| 574 | raw_data = per_cpu_ptr(trace_buf, cpu); | ||
| 575 | |||
| 576 | /* zero the dead bytes from align to not leak stack to user */ | ||
| 577 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; | ||
| 578 | |||
| 579 | rec = (struct syscall_trace_exit *)raw_data; | ||
| 580 | 541 | ||
| 581 | tracing_generic_entry_update(&rec->ent, 0, 0); | ||
| 582 | rec->ent.type = sys_data->exit_event->id; | ||
| 583 | rec->nr = syscall_nr; | 542 | rec->nr = syscall_nr; |
| 584 | rec->ret = syscall_get_return_value(current, regs); | 543 | rec->ret = syscall_get_return_value(current, regs); |
| 585 | 544 | ||
| 586 | perf_tp_event(sys_data->exit_event->id, 0, 1, rec, size); | 545 | ftrace_perf_buf_submit(rec, size, rctx, 0, 1, flags); |
| 587 | |||
| 588 | end: | ||
| 589 | perf_swevent_put_recursion_context(rctx); | ||
| 590 | end_recursion: | ||
| 591 | local_irq_restore(flags); | ||
| 592 | } | 546 | } |
| 593 | 547 | ||
| 594 | int prof_sysexit_enable(struct ftrace_event_call *call) | 548 | int prof_sysexit_enable(struct ftrace_event_call *call) |
| @@ -603,7 +557,7 @@ int prof_sysexit_enable(struct ftrace_event_call *call) | |||
| 603 | ret = register_trace_sys_exit(prof_syscall_exit); | 557 | ret = register_trace_sys_exit(prof_syscall_exit); |
| 604 | if (ret) { | 558 | if (ret) { |
| 605 | pr_info("event trace: Could not activate" | 559 | pr_info("event trace: Could not activate" |
| 606 | "syscall entry trace point"); | 560 | "syscall exit trace point"); |
| 607 | } else { | 561 | } else { |
| 608 | set_bit(num, enabled_prof_exit_syscalls); | 562 | set_bit(num, enabled_prof_exit_syscalls); |
| 609 | sys_prof_refcount_exit++; | 563 | sys_prof_refcount_exit++; |
| @@ -626,6 +580,5 @@ void prof_sysexit_disable(struct ftrace_event_call *call) | |||
| 626 | mutex_unlock(&syscall_trace_lock); | 580 | mutex_unlock(&syscall_trace_lock); |
| 627 | } | 581 | } |
| 628 | 582 | ||
| 629 | #endif | 583 | #endif /* CONFIG_PERF_EVENTS */ |
| 630 | |||
| 631 | 584 | ||
