static void prof_syscall_enter(struct pt_regs *regs, long id) { struct syscall_trace_enter *rec; struct syscall_metadata *sys_data; int syscall_nr; int size; syscall_nr = syscall_get_nr(current, regs); if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; /* 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); do { char raw_data[size]; /* zero the dead bytes from align to not leak stack to user */ *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; rec = (struct syscall_trace_enter *) raw_data; tracing_generic_entry_update(&rec->ent, 0, 0); rec->ent.type = sys_data->enter_id; rec->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size); } while(0); }
void ftrace_syscall_enter(struct pt_regs *regs, long id) { struct syscall_trace_enter *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; int size; int syscall_nr; syscall_nr = syscall_get_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_enter_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; event = trace_current_buffer_lock_reserve(&buffer, sys_data->enter_id, size, 0, 0); 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); if (!filter_current_check_discard(buffer, sys_data->enter_event, entry, event)) trace_current_buffer_unlock_commit(buffer, event, 0, 0); }
int syscall_enter_define_fields(struct ftrace_event_call *call) { struct syscall_trace_enter trace; struct syscall_metadata *meta; int ret; int nr; int i; int offset = offsetof(typeof(trace), args); nr = syscall_name_to_nr(call->data); meta = syscall_nr_to_meta(nr); if (!meta) return 0; ret = trace_define_common_fields(call); if (ret) return ret; for (i = 0; i < meta->nb_args; i++) { ret = trace_define_field(call, meta->types[i], meta->args[i], offset, sizeof(unsigned long), 0, FILTER_OTHER); offset += sizeof(unsigned long); } return ret; }
void ftrace_syscall_exit(struct pt_regs *regs, long ret) { struct syscall_trace_exit *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; int syscall_nr; syscall_nr = syscall_get_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_exit_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; event = trace_current_buffer_lock_reserve(&buffer, sys_data->exit_id, sizeof(*entry), 0, 0); if (!event) return; entry = ring_buffer_event_data(event); entry->nr = syscall_nr; entry->ret = syscall_get_return_value(current, regs); if (!filter_current_check_discard(buffer, sys_data->exit_event, entry, event)) trace_current_buffer_unlock_commit(buffer, event, 0, 0); }
/* ftrace_syscall_enter_state - build state for filter matching * * @buf: buffer to populate with current task state for matching * @available: size available for use in the buffer. * @entry: optional pointer to the trace_entry member of the state. * * Returns 0 on success and non-zero otherwise. * If @entry is NULL, it will be ignored. */ static int ftrace_syscall_enter_state(u8 *buf, size_t available, struct trace_entry **entry) { struct syscall_trace_enter *sys_enter; struct syscall_metadata *sys_data; int size; int syscall_nr; struct pt_regs *regs = task_pt_regs(current); syscall_nr = syscall_get_nr(current, regs); if (syscall_nr < 0) return -EINVAL; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return -EINVAL; /* Determine the actual size needed. */ size = ftrace_syscall_enter_state_size(sys_data->nb_args); BUG_ON(size > available); sys_enter = (struct syscall_trace_enter *)buf; /* Populating the struct trace_sys_enter is left to the caller, but * a pointer is returned to encourage opacity. */ if (entry) *entry = &sys_enter->ent; sys_enter->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, sys_enter->args); return 0; }
static enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags, struct trace_event *event) { struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_exit *trace; int syscall; struct syscall_metadata *entry; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) { trace_seq_putc(s, '\n'); goto out; } if (entry->exit_event->event.type != ent->type) { WARN_ON_ONCE(1); return TRACE_TYPE_UNHANDLED; } trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, trace->ret); out: return trace_handle_return(s); }
enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags) { 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; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) { trace_seq_printf(s, "\n"); return TRACE_TYPE_HANDLED; } if (entry->exit_id != ent->type) { WARN_ON_ONCE(1); return TRACE_TYPE_UNHANDLED; } ret = trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, trace->ret); if (!ret) return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_HANDLED; }
static void prof_syscall_exit(struct pt_regs *regs, long ret) { struct syscall_metadata *sys_data; struct syscall_trace_exit *rec; unsigned long flags; int syscall_nr; char *raw_data; int size; int cpu; syscall_nr = syscall_get_nr(current, regs); if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; /* We can probably do that at build time */ size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); size -= sizeof(u32); /* * Impossible, but be paranoid with the future * How to put this check outside runtime? */ if (WARN_ONCE(size > FTRACE_MAX_PROFILE_SIZE, "exit event has grown above profile buffer size")) return; /* Protect the per cpu buffer, begin the rcu read side */ local_irq_save(flags); cpu = smp_processor_id(); if (in_nmi()) raw_data = rcu_dereference(trace_profile_buf_nmi); else raw_data = rcu_dereference(trace_profile_buf); if (!raw_data) goto end; raw_data = per_cpu_ptr(raw_data, cpu); /* zero the dead bytes from align to not leak stack to user */ *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; rec = (struct syscall_trace_exit *)raw_data; tracing_generic_entry_update(&rec->ent, 0, 0); rec->ent.type = sys_data->exit_id; rec->nr = syscall_nr; rec->ret = syscall_get_return_value(current, regs); perf_tp_event(sys_data->exit_id, 0, 1, rec, size); end: local_irq_restore(flags); }
static const char *syscall_nr_to_name(int syscall) { const char *syscall_name = "unknown"; struct syscall_metadata *data = syscall_nr_to_meta(syscall); if (data) syscall_name = data->name; return syscall_name; }
static void prof_syscall_enter(struct pt_regs *regs, long id) { struct syscall_metadata *sys_data; struct syscall_trace_enter *rec; unsigned long flags; char *raw_data; int syscall_nr; int size; int cpu; syscall_nr = syscall_get_nr(current, regs); if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; /* 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); if (WARN_ONCE(size > FTRACE_MAX_PROFILE_SIZE, "profile buffer not large enough")) return; /* Protect the per cpu buffer, begin the rcu read side */ local_irq_save(flags); cpu = smp_processor_id(); if (in_nmi()) raw_data = rcu_dereference(trace_profile_buf_nmi); else raw_data = rcu_dereference(trace_profile_buf); if (!raw_data) goto end; raw_data = per_cpu_ptr(raw_data, cpu); /* zero the dead bytes from align to not leak stack to user */ *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; rec = (struct syscall_trace_enter *) raw_data; tracing_generic_entry_update(&rec->ent, 0, 0); rec->ent.type = sys_data->enter_id; rec->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); perf_tp_event(sys_data->enter_id, 0, 1, rec, size); end: local_irq_restore(flags); }
const char *get_syscall_name(int syscall) { struct syscall_metadata *entry; entry = syscall_nr_to_meta(syscall); if (!entry) return NULL; return entry->name; }
enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags, struct trace_event *event) { 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; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) goto end; if (entry->enter_event->event.type != ent->type) { WARN_ON_ONCE(1); goto end; } 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 */ if (trace_flags & TRACE_ITER_VERBOSE) { ret = trace_seq_printf(s, "%s ", entry->types[i]); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } /* parameter values */ ret = trace_seq_printf(s, "%s: %lx%s", entry->args[i], trace->args[i], i == entry->nb_args - 1 ? "" : ", "); if (!ret) return TRACE_TYPE_PARTIAL_LINE; } ret = trace_seq_putc(s, ')'); if (!ret) return TRACE_TYPE_PARTIAL_LINE; end: ret = trace_seq_putc(s, '\n'); if (!ret) return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_HANDLED; }
int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s) { int i; int nr; int ret; struct syscall_metadata *entry; struct syscall_trace_enter trace; int offset = offsetof(struct syscall_trace_enter, args); nr = syscall_name_to_nr(call->data); entry = syscall_nr_to_meta(nr); if (!entry) return 0; ret = trace_seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n", SYSCALL_FIELD(int, nr)); if (!ret) return 0; for (i = 0; i < entry->nb_args; i++) { ret = trace_seq_printf(s, "\tfield:%s %s;", entry->types[i], entry->args[i]); if (!ret) return 0; ret = trace_seq_printf(s, "\toffset:%d;\tsize:%zu;\n", offset, sizeof(unsigned long)); if (!ret) return 0; offset += sizeof(unsigned long); } trace_seq_puts(s, "\nprint fmt: \""); for (i = 0; i < entry->nb_args; i++) { ret = trace_seq_printf(s, "%s: 0x%%0%zulx%s", entry->args[i], sizeof(unsigned long), i == entry->nb_args - 1 ? "" : ", "); if (!ret) return 0; } trace_seq_putc(s, '"'); for (i = 0; i < entry->nb_args; i++) { ret = trace_seq_printf(s, ", ((unsigned long)(REC->%s))", entry->args[i]); if (!ret) return 0; } return trace_seq_putc(s, '\n'); }
static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) { struct trace_array *tr = data; struct ftrace_event_file *ftrace_file; struct syscall_trace_enter *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; unsigned long irq_flags; int pc; int syscall_nr; int size; syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; /* Here we're inside tp handler's rcu_read_lock_sched (__DO_TRACE) */ ftrace_file = rcu_dereference_sched(tr->enter_syscall_files[syscall_nr]); if (!ftrace_file) return; if (ftrace_trigger_soft_disabled(ftrace_file)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; local_save_flags(irq_flags); pc = preempt_count(); buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, sys_data->enter_event->event.type, size, irq_flags, pc); 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); event_trigger_unlock_commit(ftrace_file, buffer, event, entry, irq_flags, pc); }
static enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags, struct trace_event *event) { struct trace_array *tr = iter->tr; struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_enter *trace; struct syscall_metadata *entry; int i, syscall; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) goto end; if (entry->enter_event->event.type != ent->type) { WARN_ON_ONCE(1); goto end; } trace_seq_printf(s, "%s(", entry->name); for (i = 0; i < entry->nb_args; i++) { if (trace_seq_has_overflowed(s)) goto end; /* parameter types */ if (tr->trace_flags & TRACE_ITER_VERBOSE) trace_seq_printf(s, "%s ", entry->types[i]); /* parameter values */ trace_seq_printf(s, "%s: %lx%s", entry->args[i], trace->args[i], i == entry->nb_args - 1 ? "" : ", "); } trace_seq_putc(s, ')'); end: trace_seq_putc(s, '\n'); return trace_handle_return(s); }
/* Encodes translation from sys_enter events to system call numbers. * Returns -ENOSYS when the event doesn't match a system call or if * current is_compat_task(). ftrace has no awareness of CONFIG_COMPAT * yet. */ static int event_to_syscall_nr(int event_id) { int nr, nosys = 1; #ifdef CONFIG_COMPAT if (is_compat_task()) return -ENOSYS; #endif for (nr = 0; nr < NR_syscalls; ++nr) { struct syscall_metadata *data = syscall_nr_to_meta(nr); if (!data) continue; nosys = 0; if (data->enter_event->event.type == event_id) return nr; } if (nosys) return -ENOSYS; return -EINVAL; }
static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) { struct trace_array *tr = data; struct syscall_trace_enter *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; unsigned long irq_flags; int pc; int syscall_nr; int size; syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, tr->enabled_enter_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; local_save_flags(irq_flags); pc = preempt_count(); buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, sys_data->enter_event->event.type, size, irq_flags, pc); 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); if (!filter_current_check_discard(buffer, sys_data->enter_event, entry, event)) trace_current_buffer_unlock_commit(buffer, event, irq_flags, pc); }
static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) { struct syscall_metadata *sys_data; struct syscall_trace_exit *rec; struct hlist_head *head; int syscall_nr; int rctx; int size; syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; /* We can probably do that at build time */ size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); size -= sizeof(u32); /* * Impossible, but be paranoid with the future * How to put this check outside runtime? */ if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "exit event has grown above perf buffer size")) return; rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size, sys_data->exit_event->event.type, regs, &rctx); if (!rec) return; rec->nr = syscall_nr; rec->ret = syscall_get_return_value(current, regs); head = this_cpu_ptr(sys_data->exit_event->perf_events); perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); }
static void prof_syscall_exit(struct pt_regs *regs, long ret) { struct syscall_metadata *sys_data; struct syscall_trace_exit rec; int syscall_nr; syscall_nr = syscall_get_nr(current, regs); if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; tracing_generic_entry_update(&rec.ent, 0, 0); rec.ent.type = sys_data->exit_id; rec.nr = syscall_nr; rec.ret = syscall_get_return_value(current, regs); perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec)); }
static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) { struct syscall_metadata *sys_data; struct syscall_trace_enter *rec; struct hlist_head *head; int syscall_nr; int rctx; int size; syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; /* 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); if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "perf buffer not large enough")) return; rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size, sys_data->enter_event->event.type, regs, &rctx); if (!rec) return; rec->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); head = this_cpu_ptr(sys_data->enter_event->perf_events); perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); }
static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) { struct syscall_metadata *sys_data; struct syscall_trace_enter *rec; struct hlist_head *head; int syscall_nr; int rctx; int size; syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; head = this_cpu_ptr(sys_data->enter_event->perf_events); if (hlist_empty(head)) return; /* 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); rec = perf_trace_buf_alloc(size, NULL, &rctx); if (!rec) return; rec->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); perf_trace_buf_submit(rec, size, rctx, sys_data->enter_event->event.type, 1, regs, head, NULL); }
void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret) { struct syscall_trace_exit *entry; struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; unsigned long irq_flags; int pc; int syscall_nr; syscall_nr = syscall_get_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_exit_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; local_save_flags(irq_flags); pc = preempt_count(); event = trace_current_buffer_lock_reserve(&buffer, sys_data->exit_event->event.type, sizeof(*entry), irq_flags, pc); if (!event) return; entry = ring_buffer_event_data(event); entry->nr = syscall_nr; entry->ret = syscall_get_return_value(current, regs); if (!filter_current_check_discard(buffer, sys_data->exit_event, entry, event)) trace_current_buffer_unlock_commit(buffer, event, irq_flags, pc); }
static struct event_filter *alloc_event_filter(int syscall_nr, char *filter_string) { struct syscall_metadata *data; struct event_filter *filter = NULL; int err; data = syscall_nr_to_meta(syscall_nr); /* Argument-based filtering only works on ftrace-hooked syscalls. */ err = -ENOSYS; if (!data) goto fail; err = create_event_filter(&filter, data->enter_event->event.type, filter_string); if (err) goto fail; return filter; fail: kfree(filter); return ERR_PTR(err); }
static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) { struct syscall_metadata *sys_data; struct syscall_trace_exit *rec; struct hlist_head *head; int syscall_nr; int rctx; int size; syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) return; sys_data = syscall_nr_to_meta(syscall_nr); if (!sys_data) return; head = this_cpu_ptr(sys_data->exit_event->perf_events); if (hlist_empty(head)) return; /* We can probably do that at build time */ size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); size -= sizeof(u32); rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size, sys_data->exit_event->event.type, NULL, &rctx); if (!rec) return; rec->nr = syscall_nr; rec->ret = syscall_get_return_value(current, regs); perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); }