diff --git a/[refs] b/[refs] index 38c465610da8..54cc3c2aade8 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: e859cf8656043f158b4004ccc8cbbf1ba4f97177 +refs/heads/master: 956ffd027bedc4106b901eb6a50f0a6c6de4113d diff --git a/trunk/arch/x86/include/asm/ptrace.h b/trunk/arch/x86/include/asm/ptrace.h index 3d11fd0f44c5..a3d49dd7d26e 100644 --- a/trunk/arch/x86/include/asm/ptrace.h +++ b/trunk/arch/x86/include/asm/ptrace.h @@ -227,8 +227,8 @@ extern const char *regs_query_register_name(unsigned int offset); * @regs: pt_regs from which register value is gotten. * @offset: offset number of the register. * - * regs_get_register returns the value of a register. The @offset is the - * offset of the register in struct pt_regs address which specified by @regs. + * regs_get_register returns the value of a register whose offset from @regs + * is @offset. The @offset is the offset of the register in struct pt_regs. * If @offset is bigger than MAX_REG_OFFSET, this returns 0. */ static inline unsigned long regs_get_register(struct pt_regs *regs, @@ -244,7 +244,7 @@ static inline unsigned long regs_get_register(struct pt_regs *regs, * @regs: pt_regs which contains kernel stack pointer. * @addr: address which is checked. * - * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). + * regs_within_kenel_stack() checks @addr is within the kernel stack page(s). * If @addr is within the kernel stack, it returns true. If not, returns false. */ static inline int regs_within_kernel_stack(struct pt_regs *regs, @@ -260,7 +260,7 @@ static inline int regs_within_kernel_stack(struct pt_regs *regs, * @n: stack entry number. * * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which - * is specified by @regs. If the @n th entry is NOT in the kernel stack, + * is specifined by @regs. If the @n th entry is NOT in the kernel stack, * this returns 0. */ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, diff --git a/trunk/arch/x86/kernel/ptrace.c b/trunk/arch/x86/kernel/ptrace.c index 04d182a7cfdb..2941b32ea666 100644 --- a/trunk/arch/x86/kernel/ptrace.c +++ b/trunk/arch/x86/kernel/ptrace.c @@ -595,7 +595,7 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[]) static struct perf_event * ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, - struct task_struct *tsk, int disabled) + struct task_struct *tsk) { int err; int gen_len, gen_type; @@ -616,7 +616,7 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, attr = bp->attr; attr.bp_len = gen_len; attr.bp_type = gen_type; - attr.disabled = disabled; + attr.disabled = 0; return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk); } @@ -655,21 +655,13 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) */ if (!second_pass) continue; - thread->ptrace_bps[i] = NULL; - bp = ptrace_modify_breakpoint(bp, len, type, - tsk, 1); - if (IS_ERR(bp)) { - rc = PTR_ERR(bp); - thread->ptrace_bps[i] = NULL; - break; - } - thread->ptrace_bps[i] = bp; + unregister_hw_breakpoint(bp); } continue; } - bp = ptrace_modify_breakpoint(bp, len, type, tsk, 0); + bp = ptrace_modify_breakpoint(bp, len, type, tsk); /* Incorrect bp, or we have a bug in bp API */ if (IS_ERR(bp)) { diff --git a/trunk/include/linux/syscalls.h b/trunk/include/linux/syscalls.h index e79e2f3ccc51..b50974a93af0 100644 --- a/trunk/include/linux/syscalls.h +++ b/trunk/include/linux/syscalls.h @@ -99,16 +99,37 @@ struct perf_event_attr; #define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__) #ifdef CONFIG_EVENT_PROFILE +#define TRACE_SYS_ENTER_PROFILE(sname) \ +static int prof_sysenter_enable_##sname(struct ftrace_event_call *unused) \ +{ \ + return reg_prof_syscall_enter("sys"#sname); \ +} \ + \ +static void prof_sysenter_disable_##sname(struct ftrace_event_call *unused) \ +{ \ + unreg_prof_syscall_enter("sys"#sname); \ +} + +#define TRACE_SYS_EXIT_PROFILE(sname) \ +static int prof_sysexit_enable_##sname(struct ftrace_event_call *unused) \ +{ \ + return reg_prof_syscall_exit("sys"#sname); \ +} \ + \ +static void prof_sysexit_disable_##sname(struct ftrace_event_call *unused) \ +{ \ + unreg_prof_syscall_exit("sys"#sname); \ +} #define TRACE_SYS_ENTER_PROFILE_INIT(sname) \ .profile_count = ATOMIC_INIT(-1), \ - .profile_enable = prof_sysenter_enable, \ - .profile_disable = prof_sysenter_disable, + .profile_enable = prof_sysenter_enable_##sname, \ + .profile_disable = prof_sysenter_disable_##sname, #define TRACE_SYS_EXIT_PROFILE_INIT(sname) \ .profile_count = ATOMIC_INIT(-1), \ - .profile_enable = prof_sysexit_enable, \ - .profile_disable = prof_sysexit_disable, + .profile_enable = prof_sysexit_enable_##sname, \ + .profile_disable = prof_sysexit_disable_##sname, #else #define TRACE_SYS_ENTER_PROFILE(sname) #define TRACE_SYS_ENTER_PROFILE_INIT(sname) @@ -132,46 +153,74 @@ struct perf_event_attr; #define __SC_STR_TDECL6(t, a, ...) #t, __SC_STR_TDECL5(__VA_ARGS__) #define SYSCALL_TRACE_ENTER_EVENT(sname) \ - static const struct syscall_metadata __syscall_meta_##sname; \ static struct ftrace_event_call event_enter_##sname; \ - static struct trace_event enter_syscall_print_##sname = { \ + struct trace_event enter_syscall_print_##sname = { \ .trace = print_syscall_enter, \ }; \ + static int init_enter_##sname(struct ftrace_event_call *call) \ + { \ + int num, id; \ + num = syscall_name_to_nr("sys"#sname); \ + if (num < 0) \ + return -ENOSYS; \ + id = register_ftrace_event(&enter_syscall_print_##sname);\ + if (!id) \ + return -ENODEV; \ + event_enter_##sname.id = id; \ + set_syscall_enter_id(num, id); \ + INIT_LIST_HEAD(&event_enter_##sname.fields); \ + return 0; \ + } \ + TRACE_SYS_ENTER_PROFILE(sname); \ static struct ftrace_event_call __used \ __attribute__((__aligned__(4))) \ __attribute__((section("_ftrace_events"))) \ event_enter_##sname = { \ .name = "sys_enter"#sname, \ .system = "syscalls", \ - .event = &enter_syscall_print_##sname, \ - .raw_init = init_syscall_trace, \ + .event = &event_syscall_enter, \ + .raw_init = init_enter_##sname, \ .show_format = syscall_enter_format, \ .define_fields = syscall_enter_define_fields, \ .regfunc = reg_event_syscall_enter, \ .unregfunc = unreg_event_syscall_enter, \ - .data = (void *)&__syscall_meta_##sname,\ + .data = "sys"#sname, \ TRACE_SYS_ENTER_PROFILE_INIT(sname) \ } #define SYSCALL_TRACE_EXIT_EVENT(sname) \ - static const struct syscall_metadata __syscall_meta_##sname; \ static struct ftrace_event_call event_exit_##sname; \ - static struct trace_event exit_syscall_print_##sname = { \ + struct trace_event exit_syscall_print_##sname = { \ .trace = print_syscall_exit, \ }; \ + static int init_exit_##sname(struct ftrace_event_call *call) \ + { \ + int num, id; \ + num = syscall_name_to_nr("sys"#sname); \ + if (num < 0) \ + return -ENOSYS; \ + id = register_ftrace_event(&exit_syscall_print_##sname);\ + if (!id) \ + return -ENODEV; \ + event_exit_##sname.id = id; \ + set_syscall_exit_id(num, id); \ + INIT_LIST_HEAD(&event_exit_##sname.fields); \ + return 0; \ + } \ + TRACE_SYS_EXIT_PROFILE(sname); \ static struct ftrace_event_call __used \ __attribute__((__aligned__(4))) \ __attribute__((section("_ftrace_events"))) \ event_exit_##sname = { \ .name = "sys_exit"#sname, \ .system = "syscalls", \ - .event = &exit_syscall_print_##sname, \ - .raw_init = init_syscall_trace, \ + .event = &event_syscall_exit, \ + .raw_init = init_exit_##sname, \ .show_format = syscall_exit_format, \ .define_fields = syscall_exit_define_fields, \ .regfunc = reg_event_syscall_exit, \ .unregfunc = unreg_event_syscall_exit, \ - .data = (void *)&__syscall_meta_##sname,\ + .data = "sys"#sname, \ TRACE_SYS_EXIT_PROFILE_INIT(sname) \ } diff --git a/trunk/include/trace/syscall.h b/trunk/include/trace/syscall.h index 961fda3556bb..51ee17d3632a 100644 --- a/trunk/include/trace/syscall.h +++ b/trunk/include/trace/syscall.h @@ -12,19 +12,21 @@ * A syscall entry in the ftrace syscalls array. * * @name: name of the syscall - * @syscall_nr: number of the syscall * @nb_args: number of parameters it takes * @types: list of types as strings * @args: list of args as strings (args[i] matches types[i]) + * @enter_id: associated ftrace enter event id + * @exit_id: associated ftrace exit event id * @enter_event: associated syscall_enter trace event * @exit_event: associated syscall_exit trace event */ struct syscall_metadata { const char *name; - int syscall_nr; int nb_args; const char **types; const char **args; + int enter_id; + int exit_id; struct ftrace_event_call *enter_event; struct ftrace_event_call *exit_event; @@ -32,7 +34,11 @@ struct syscall_metadata { #ifdef CONFIG_FTRACE_SYSCALLS extern unsigned long arch_syscall_addr(int nr); -extern int init_syscall_trace(struct ftrace_event_call *call); +extern int syscall_name_to_nr(char *name); +void set_syscall_enter_id(int num, int id); +void set_syscall_exit_id(int num, int id); +extern struct trace_event event_syscall_enter; +extern struct trace_event event_syscall_exit; extern int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s); @@ -50,10 +56,10 @@ enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags); enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags); #endif #ifdef CONFIG_EVENT_PROFILE -int prof_sysenter_enable(struct ftrace_event_call *call); -void prof_sysenter_disable(struct ftrace_event_call *call); -int prof_sysexit_enable(struct ftrace_event_call *call); -void prof_sysexit_disable(struct ftrace_event_call *call); +int reg_prof_syscall_enter(char *name); +void unreg_prof_syscall_enter(char *name); +int reg_prof_syscall_exit(char *name); +void unreg_prof_syscall_exit(char *name); #endif diff --git a/trunk/kernel/perf_event.c b/trunk/kernel/perf_event.c index 6b7ddba1dd64..0b9ca2d834dd 100644 --- a/trunk/kernel/perf_event.c +++ b/trunk/kernel/perf_event.c @@ -2210,7 +2210,6 @@ static void perf_mmap_data_free(struct perf_mmap_data *data) perf_mmap_free_page((unsigned long)data->user_page); for (i = 0; i < data->nr_pages; i++) perf_mmap_free_page((unsigned long)data->data_pages[i]); - kfree(data); } #else @@ -2251,7 +2250,6 @@ static void perf_mmap_data_free_work(struct work_struct *work) perf_mmap_unmark_page(base + (i * PAGE_SIZE)); vfree(base); - kfree(data); } static void perf_mmap_data_free(struct perf_mmap_data *data) @@ -2357,6 +2355,7 @@ static void perf_mmap_data_free_rcu(struct rcu_head *rcu_head) data = container_of(rcu_head, struct perf_mmap_data, rcu_head); perf_mmap_data_free(data); + kfree(data); } static void perf_mmap_data_release(struct perf_event *event) @@ -4011,7 +4010,6 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) event->pmu->read(event); data.addr = 0; - data.period = event->hw.last_period; regs = get_irq_regs(); /* * In case we exclude kernel IPs or are somehow not in interrupt diff --git a/trunk/kernel/trace/trace_kprobe.c b/trunk/kernel/trace/trace_kprobe.c index aff5f80b59b8..72d0c65c8676 100644 --- a/trunk/kernel/trace/trace_kprobe.c +++ b/trunk/kernel/trace/trace_kprobe.c @@ -483,8 +483,7 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return) return ret; } -/* Recursive argument parser */ -static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) +static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) { int ret = 0; unsigned long param; @@ -544,7 +543,7 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) if (!id) return -ENOMEM; id->offset = offset; - ret = __parse_probe_arg(arg, &id->orig, is_return); + ret = parse_probe_arg(arg, &id->orig, is_return); if (ret) kfree(id); else { @@ -561,16 +560,6 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) return ret; } -/* String length checking wrapper */ -static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) -{ - if (strlen(arg) > MAX_ARGSTR_LEN) { - pr_info("Argument is too long.: %s\n", arg); - return -ENOSPC; - } - return __parse_probe_arg(arg, ff, is_return); -} - /* Return 1 if name is reserved or already used by another argument */ static int conflict_field_name(const char *name, struct probe_arg *args, int narg) @@ -709,14 +698,13 @@ static int create_trace_probe(int argc, char **argv) } tp->args[i].name = kstrdup(argv[i], GFP_KERNEL); - if (!tp->args[i].name) { - pr_info("Failed to allocate argument%d name '%s'.\n", - i, argv[i]); - ret = -ENOMEM; - goto error; - } /* Parse fetch argument */ + if (strlen(arg) > MAX_ARGSTR_LEN) { + pr_info("Argument%d(%s) is too long.\n", i, arg); + ret = -ENOSPC; + goto error; + } ret = parse_probe_arg(arg, &tp->args[i].fetch, is_return); if (ret) { pr_info("Parse error at argument%d. (%d)\n", i, ret); diff --git a/trunk/kernel/trace/trace_syscalls.c b/trunk/kernel/trace/trace_syscalls.c index 57501d90096a..63aa8070365d 100644 --- a/trunk/kernel/trace/trace_syscalls.c +++ b/trunk/kernel/trace/trace_syscalls.c @@ -51,6 +51,32 @@ static struct syscall_metadata *syscall_nr_to_meta(int nr) return syscalls_metadata[nr]; } +int syscall_name_to_nr(char *name) +{ + int i; + + if (!syscalls_metadata) + return -1; + + for (i = 0; i < NR_syscalls; i++) { + if (syscalls_metadata[i]) { + if (!strcmp(syscalls_metadata[i]->name, name)) + return i; + } + } + return -1; +} + +void set_syscall_enter_id(int num, int id) +{ + syscalls_metadata[num]->enter_id = id; +} + +void set_syscall_exit_id(int num, int id) +{ + syscalls_metadata[num]->exit_id = id; +} + enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags) { @@ -67,7 +93,7 @@ print_syscall_enter(struct trace_iterator *iter, int flags) if (!entry) goto end; - if (entry->enter_event->id != ent->type) { + if (entry->enter_id != ent->type) { WARN_ON_ONCE(1); goto end; } @@ -122,7 +148,7 @@ print_syscall_exit(struct trace_iterator *iter, int flags) return TRACE_TYPE_HANDLED; } - if (entry->exit_event->id != ent->type) { + if (entry->exit_id != ent->type) { WARN_ON_ONCE(1); return TRACE_TYPE_UNHANDLED; } @@ -146,11 +172,18 @@ extern char *__bad_type_size(void); int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s) { int i; + int nr; int ret; - struct syscall_metadata *entry = call->data; + 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;" "\tsigned:%u;\n", SYSCALL_FIELD(int, nr)); @@ -212,11 +245,18 @@ int syscall_exit_format(struct ftrace_event_call *call, struct trace_seq *s) int syscall_enter_define_fields(struct ftrace_event_call *call) { struct syscall_trace_enter trace; - struct syscall_metadata *meta = call->data; + 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; @@ -276,8 +316,8 @@ void ftrace_syscall_enter(struct pt_regs *regs, long id) size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; - event = trace_current_buffer_lock_reserve(&buffer, - sys_data->enter_event->id, size, 0, 0); + event = trace_current_buffer_lock_reserve(&buffer, sys_data->enter_id, + size, 0, 0); if (!event) return; @@ -308,8 +348,8 @@ void ftrace_syscall_exit(struct pt_regs *regs, long ret) if (!sys_data) return; - event = trace_current_buffer_lock_reserve(&buffer, - sys_data->exit_event->id, sizeof(*entry), 0, 0); + event = trace_current_buffer_lock_reserve(&buffer, sys_data->exit_id, + sizeof(*entry), 0, 0); if (!event) return; @@ -326,8 +366,10 @@ int reg_event_syscall_enter(struct ftrace_event_call *call) { int ret = 0; int num; + char *name; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + name = (char *)call->data; + num = syscall_name_to_nr(name); if (num < 0 || num >= NR_syscalls) return -ENOSYS; mutex_lock(&syscall_trace_lock); @@ -347,8 +389,10 @@ int reg_event_syscall_enter(struct ftrace_event_call *call) void unreg_event_syscall_enter(struct ftrace_event_call *call) { int num; + char *name; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + name = (char *)call->data; + num = syscall_name_to_nr(name); if (num < 0 || num >= NR_syscalls) return; mutex_lock(&syscall_trace_lock); @@ -363,8 +407,10 @@ int reg_event_syscall_exit(struct ftrace_event_call *call) { int ret = 0; int num; + char *name; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + name = call->data; + num = syscall_name_to_nr(name); if (num < 0 || num >= NR_syscalls) return -ENOSYS; mutex_lock(&syscall_trace_lock); @@ -384,8 +430,10 @@ int reg_event_syscall_exit(struct ftrace_event_call *call) void unreg_event_syscall_exit(struct ftrace_event_call *call) { int num; + char *name; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + name = call->data; + num = syscall_name_to_nr(name); if (num < 0 || num >= NR_syscalls) return; mutex_lock(&syscall_trace_lock); @@ -396,17 +444,13 @@ void unreg_event_syscall_exit(struct ftrace_event_call *call) mutex_unlock(&syscall_trace_lock); } -int init_syscall_trace(struct ftrace_event_call *call) -{ - int id; +struct trace_event event_syscall_enter = { + .trace = print_syscall_enter, +}; - id = register_ftrace_event(call->event); - if (!id) - return -ENODEV; - call->id = id; - INIT_LIST_HEAD(&call->fields); - return 0; -} +struct trace_event event_syscall_exit = { + .trace = print_syscall_exit, +}; int __init init_ftrace_syscalls(void) { @@ -424,10 +468,6 @@ int __init init_ftrace_syscalls(void) for (i = 0; i < NR_syscalls; i++) { addr = arch_syscall_addr(i); meta = find_syscall_meta(addr); - if (!meta) - continue; - - meta->syscall_nr = i; syscalls_metadata[i] = meta; } @@ -492,11 +532,11 @@ static void prof_syscall_enter(struct pt_regs *regs, long id) rec = (struct syscall_trace_enter *) raw_data; tracing_generic_entry_update(&rec->ent, 0, 0); - rec->ent.type = sys_data->enter_event->id; + 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_event->id, 0, 1, rec, size); + perf_tp_event(sys_data->enter_id, 0, 1, rec, size); end: perf_swevent_put_recursion_context(rctx); @@ -504,12 +544,14 @@ static void prof_syscall_enter(struct pt_regs *regs, long id) local_irq_restore(flags); } -int prof_sysenter_enable(struct ftrace_event_call *call) +int reg_prof_syscall_enter(char *name) { int ret = 0; int num; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + num = syscall_name_to_nr(name); + if (num < 0 || num >= NR_syscalls) + return -ENOSYS; mutex_lock(&syscall_trace_lock); if (!sys_prof_refcount_enter) @@ -525,11 +567,13 @@ int prof_sysenter_enable(struct ftrace_event_call *call) return ret; } -void prof_sysenter_disable(struct ftrace_event_call *call) +void unreg_prof_syscall_enter(char *name) { int num; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + num = syscall_name_to_nr(name); + if (num < 0 || num >= NR_syscalls) + return; mutex_lock(&syscall_trace_lock); sys_prof_refcount_enter--; @@ -593,11 +637,11 @@ static void prof_syscall_exit(struct pt_regs *regs, long ret) rec = (struct syscall_trace_exit *)raw_data; tracing_generic_entry_update(&rec->ent, 0, 0); - rec->ent.type = sys_data->exit_event->id; + 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_event->id, 0, 1, rec, size); + perf_tp_event(sys_data->exit_id, 0, 1, rec, size); end: perf_swevent_put_recursion_context(rctx); @@ -605,12 +649,14 @@ static void prof_syscall_exit(struct pt_regs *regs, long ret) local_irq_restore(flags); } -int prof_sysexit_enable(struct ftrace_event_call *call) +int reg_prof_syscall_exit(char *name) { int ret = 0; int num; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + num = syscall_name_to_nr(name); + if (num < 0 || num >= NR_syscalls) + return -ENOSYS; mutex_lock(&syscall_trace_lock); if (!sys_prof_refcount_exit) @@ -626,11 +672,13 @@ int prof_sysexit_enable(struct ftrace_event_call *call) return ret; } -void prof_sysexit_disable(struct ftrace_event_call *call) +void unreg_prof_syscall_exit(char *name) { int num; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + num = syscall_name_to_nr(name); + if (num < 0 || num >= NR_syscalls) + return; mutex_lock(&syscall_trace_lock); sys_prof_refcount_exit--; diff --git a/trunk/scripts/kernel-doc b/trunk/scripts/kernel-doc index 241310e59cd6..ea9f8a58678f 100755 --- a/trunk/scripts/kernel-doc +++ b/trunk/scripts/kernel-doc @@ -1852,17 +1852,10 @@ sub tracepoint_munge($) { my $tracepointname = 0; my $tracepointargs = 0; - if ($prototype =~ m/TRACE_EVENT\((.*?),/) { + if($prototype =~ m/TRACE_EVENT\((.*?),/) { $tracepointname = $1; } - if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { - $tracepointname = $2; - } - $tracepointname =~ s/^\s+//; #strip leading whitespace - if ($prototype =~ m/TP_PROTO\((.*?)\)/) { + if($prototype =~ m/TP_PROTO\((.*?)\)/) { $tracepointargs = $1; } if (($tracepointname eq 0) || ($tracepointargs eq 0)) { @@ -1927,9 +1920,7 @@ sub process_state3_function($$) { if ($prototype =~ /SYSCALL_DEFINE/) { syscall_munge(); } - if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || - $prototype =~ /DEFINE_SINGLE_EVENT/) - { + if ($prototype =~ /TRACE_EVENT/) { tracepoint_munge($file); } dump_function($prototype, $file); diff --git a/trunk/tools/perf/Makefile b/trunk/tools/perf/Makefile index f8537cf812c9..f1537a94a05f 100644 --- a/trunk/tools/perf/Makefile +++ b/trunk/tools/perf/Makefile @@ -369,8 +369,6 @@ LIB_H += util/sort.h LIB_H += util/hist.h LIB_H += util/thread.h LIB_H += util/data_map.h -LIB_H += util/probe-finder.h -LIB_H += util/probe-event.h LIB_OBJS += util/abspath.o LIB_OBJS += util/alias.o @@ -413,7 +411,6 @@ LIB_OBJS += util/svghelper.o LIB_OBJS += util/sort.o LIB_OBJS += util/hist.o LIB_OBJS += util/data_map.o -LIB_OBJS += util/probe-event.o BUILTIN_OBJS += builtin-annotate.o @@ -488,6 +485,7 @@ ifneq ($(shell sh -c "(echo '\#include '; echo '\#include header.type) { + case PERF_RECORD_SAMPLE: + return process_sample_event(self); + + case PERF_RECORD_MMAP: + return event__process_mmap(self); + + case PERF_RECORD_COMM: + return event__process_comm(self); + + case PERF_RECORD_FORK: + return event__process_task(self); + /* + * We dont process them right now but they are fine: + */ + + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: + return 0; + + default: + return -1; + } + + return 0; +} + static int parse_line(FILE *file, struct hist_entry *he, u64 len) { struct symbol *sym = he->sym; @@ -453,26 +485,99 @@ static void find_annotations(void) } } -static struct perf_file_handler file_handler = { - .process_sample_event = process_sample_event, - .process_mmap_event = event__process_mmap, - .process_comm_event = event__process_comm, - .process_fork_event = event__process_task, -}; - static int __cmd_annotate(void) { - struct perf_header *header; - struct thread *idle; - int ret; + int ret, rc = EXIT_FAILURE; + unsigned long offset = 0; + unsigned long head = 0; + struct stat input_stat; + event_t *event; + uint32_t size; + char *buf; + + register_idle_thread(); + + input = open(input_name, O_RDONLY); + if (input < 0) { + perror("failed to open file"); + exit(-1); + } + + ret = fstat(input, &input_stat); + if (ret < 0) { + perror("failed to stat file"); + exit(-1); + } + + if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { + fprintf(stderr, "file: %s not owned by current user or root\n", input_name); + exit(-1); + } - idle = register_idle_thread(); - register_perf_file_handler(&file_handler); + if (!input_stat.st_size) { + fprintf(stderr, "zero-sized file, nothing to do!\n"); + exit(0); + } + +remap: + buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, + MAP_SHARED, input, offset); + if (buf == MAP_FAILED) { + perror("failed to mmap file"); + exit(-1); + } + +more: + event = (event_t *)(buf + head); + + size = event->header.size; + if (!size) + size = 8; + + if (head + event->header.size >= page_size * mmap_window) { + unsigned long shift = page_size * (head / page_size); + int munmap_ret; + + munmap_ret = munmap(buf, page_size * mmap_window); + assert(munmap_ret == 0); + + offset += shift; + head -= shift; + goto remap; + } + + size = event->header.size; + + dump_printf("%p [%p]: event: %d\n", + (void *)(offset + head), + (void *)(long)event->header.size, + event->header.type); + + if (!size || event__process(event) < 0) { + + dump_printf("%p [%p]: skipping unknown header type: %d\n", + (void *)(offset + head), + (void *)(long)(event->header.size), + event->header.type); + /* + * assume we lost track of the stream, check alignment, and + * increment a single u64 in the hope to catch on again 'soon'. + */ + + if (unlikely(head & 7)) + head &= ~7ULL; + + size = 8; + } + + head += size; + + if (offset + head < (unsigned long)input_stat.st_size) + goto more; + + rc = EXIT_SUCCESS; + close(input); - ret = mmap_dispatch_perf_file(&header, input_name, 0, 0, - &event__cwdlen, &event__cwd); - if (ret) - return ret; if (dump_trace) { event__print_totals(); @@ -490,7 +595,7 @@ static int __cmd_annotate(void) find_annotations(); - return ret; + return rc; } static const char * const annotate_usage[] = { @@ -539,6 +644,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) if (symbol__init(&symbol_conf) < 0) return -1; + page_size = getpagesize(); + argc = parse_options(argc, argv, options, annotate_usage, 0); setup_sorting(); diff --git a/trunk/tools/perf/builtin-probe.c b/trunk/tools/perf/builtin-probe.c index a58e11b7ea80..a2f6daf01ecb 100644 --- a/trunk/tools/perf/builtin-probe.c +++ b/trunk/tools/perf/builtin-probe.c @@ -40,7 +40,6 @@ #include "util/parse-options.h" #include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" -#include "util/probe-event.h" /* Default vmlinux search paths */ #define NR_SEARCH_PATH 3 @@ -52,6 +51,8 @@ const char *default_search_path[NR_SEARCH_PATH] = { #define MAX_PATH_LEN 256 #define MAX_PROBES 128 +#define MAX_PROBE_ARGS 128 +#define PERFPROBE_GROUP "probe" /* Session management structure */ static struct { @@ -62,19 +63,152 @@ static struct { struct probe_point probes[MAX_PROBES]; } session; -static bool listing; +#define semantic_error(msg ...) die("Semantic error :" msg) + +/* Parse probe point. Return 1 if return probe */ +static void parse_probe_point(char *arg, struct probe_point *pp) +{ + char *ptr, *tmp; + char c, nc = 0; + /* + * + * perf probe SRC:LN + * perf probe FUNC[+OFFS|%return][@SRC] + */ + + ptr = strpbrk(arg, ":+@%"); + if (ptr) { + nc = *ptr; + *ptr++ = '\0'; + } + + /* Check arg is function or file and copy it */ + if (strchr(arg, '.')) /* File */ + pp->file = strdup(arg); + else /* Function */ + pp->function = strdup(arg); + DIE_IF(pp->file == NULL && pp->function == NULL); + + /* Parse other options */ + while (ptr) { + arg = ptr; + c = nc; + ptr = strpbrk(arg, ":+@%"); + if (ptr) { + nc = *ptr; + *ptr++ = '\0'; + } + switch (c) { + case ':': /* Line number */ + pp->line = strtoul(arg, &tmp, 0); + if (*tmp != '\0') + semantic_error("There is non-digit charactor" + " in line number."); + break; + case '+': /* Byte offset from a symbol */ + pp->offset = strtoul(arg, &tmp, 0); + if (*tmp != '\0') + semantic_error("There is non-digit charactor" + " in offset."); + break; + case '@': /* File name */ + if (pp->file) + semantic_error("SRC@SRC is not allowed."); + pp->file = strdup(arg); + DIE_IF(pp->file == NULL); + if (ptr) + semantic_error("@SRC must be the last " + "option."); + break; + case '%': /* Probe places */ + if (strcmp(arg, "return") == 0) { + pp->retprobe = 1; + } else /* Others not supported yet */ + semantic_error("%%%s is not supported.", arg); + break; + default: + DIE_IF("Program has a bug."); + break; + } + } + + /* Exclusion check */ + if (pp->line && pp->offset) + semantic_error("Offset can't be used with line number."); + if (!pp->line && pp->file && !pp->function) + semantic_error("File always requires line number."); + if (pp->offset && !pp->function) + semantic_error("Offset requires an entry function."); + if (pp->retprobe && !pp->function) + semantic_error("Return probe requires an entry function."); + if ((pp->offset || pp->line) && pp->retprobe) + semantic_error("Offset/Line can't be used with return probe."); + + pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", + pp->function, pp->file, pp->line, pp->offset, pp->retprobe); +} /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) { + char *argv[MAX_PROBE_ARGS + 2]; /* Event + probe + args */ + int argc, i; struct probe_point *pp = &session.probes[session.nr_probe]; pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); if (++session.nr_probe == MAX_PROBES) - die("Too many probes (> %d) are specified.", MAX_PROBES); + semantic_error("Too many probes"); + + /* Separate arguments, similar to argv_split */ + argc = 0; + do { + /* Skip separators */ + while (isspace(*str)) + str++; + + /* Add an argument */ + if (*str != '\0') { + const char *s = str; + + /* Skip the argument */ + while (!isspace(*str) && *str != '\0') + str++; + + /* Duplicate the argument */ + argv[argc] = strndup(s, str - s); + if (argv[argc] == NULL) + die("strndup"); + if (++argc == MAX_PROBE_ARGS) + semantic_error("Too many arguments"); + pr_debug("argv[%d]=%s\n", argc, argv[argc - 1]); + } + } while (*str != '\0'); + if (!argc) + semantic_error("An empty argument."); + + /* Parse probe point */ + parse_probe_point(argv[0], pp); + free(argv[0]); + if (pp->file || pp->line) + session.need_dwarf = 1; + + /* Copy arguments */ + pp->nr_args = argc - 1; + if (pp->nr_args > 0) { + pp->args = (char **)malloc(sizeof(char *) * pp->nr_args); + if (!pp->args) + die("malloc"); + memcpy(pp->args, &argv[1], sizeof(char *) * pp->nr_args); + } - /* Parse perf-probe event into probe_point */ - session.need_dwarf = parse_perf_probe_event(str, pp); + /* Ensure return probe has no C argument */ + for (i = 0; i < pp->nr_args; i++) + if (is_c_varname(pp->args[i])) { + if (pp->retprobe) + semantic_error("You can't specify local" + " variable for kretprobe"); + session.need_dwarf = 1; + } pr_debug("%d arguments\n", pp->nr_args); } @@ -121,7 +255,6 @@ static int open_default_vmlinux(void) static const char * const probe_usage[] = { "perf probe [] 'PROBEDEF' ['PROBEDEF' ...]", "perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", - "perf probe --list", NULL }; @@ -132,7 +265,6 @@ static const struct option options[] = { OPT_STRING('k', "vmlinux", &session.vmlinux, "file", "vmlinux/module pathname"), #endif - OPT_BOOLEAN('l', "list", &listing, "list up current probes"), OPT_CALLBACK('a', "add", NULL, #ifdef NO_LIBDWARF "FUNC[+OFFS|%return] [ARG ...]", @@ -153,38 +285,73 @@ static const struct option options[] = { "\t\tALN:\tAbsolute line number in file.\n" "\t\tARG:\tProbe argument (local variable name or\n" #endif - "\t\t\tkprobe-tracer argument format.)\n", + "\t\t\tkprobe-tracer argument format is supported.)\n", opt_add_probe_event), OPT_END() }; +static int write_new_event(int fd, const char *buf) +{ + int ret; + + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to create event."); + else + printf("Added new event: %s\n", buf); + + return ret; +} + +#define MAX_CMDLEN 256 + +static int synthesize_probe_event(struct probe_point *pp) +{ + char *buf; + int i, len, ret; + pp->probes[0] = buf = zalloc(MAX_CMDLEN); + if (!buf) + die("Failed to allocate memory by zalloc."); + ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); + if (ret <= 0 || ret >= MAX_CMDLEN) + goto error; + len = ret; + + for (i = 0; i < pp->nr_args; i++) { + ret = snprintf(&buf[len], MAX_CMDLEN - len, " %s", + pp->args[i]); + if (ret <= 0 || ret >= MAX_CMDLEN - len) + goto error; + len += ret; + } + pp->found = 1; + return pp->found; +error: + free(pp->probes[0]); + if (ret > 0) + ret = -E2BIG; + return ret; +} + int cmd_probe(int argc, const char **argv, const char *prefix __used) { - int i, j, ret; -#ifndef NO_LIBDWARF - int fd; -#endif + int i, j, fd, ret; struct probe_point *pp; + char buf[MAX_CMDLEN]; argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); for (i = 0; i < argc; i++) parse_probe_event(argv[i]); - if ((session.nr_probe == 0 && !listing) || - (session.nr_probe != 0 && listing)) + if (session.nr_probe == 0) usage_with_options(probe_usage, options); - if (listing) { - show_perf_probe_events(); - return 0; - } - if (session.need_dwarf) #ifdef NO_LIBDWARF - die("Debuginfo-analysis is not supported"); + semantic_error("Debuginfo-analysis is not supported"); #else /* !NO_LIBDWARF */ - pr_debug("Some probes require debuginfo.\n"); + pr_info("Some probes require debuginfo.\n"); if (session.vmlinux) fd = open(session.vmlinux, O_RDONLY); @@ -228,15 +395,41 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (pp->found) /* This probe is already found. */ continue; - ret = synthesize_trace_kprobe_event(pp); + ret = synthesize_probe_event(pp); if (ret == -E2BIG) - die("probe point definition becomes too long."); + semantic_error("probe point is too long."); else if (ret < 0) die("Failed to synthesize a probe point."); } /* Settng up probe points */ - add_trace_kprobe_events(session.probes, session.nr_probe); + snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path); + fd = open(buf, O_WRONLY, O_APPEND); + if (fd < 0) { + if (errno == ENOENT) + die("kprobe_events file does not exist - please rebuild with CONFIG_KPROBE_TRACER."); + else + die("Could not open kprobe_events file: %s", + strerror(errno)); + } + for (j = 0; j < session.nr_probe; j++) { + pp = &session.probes[j]; + if (pp->found == 1) { + snprintf(buf, MAX_CMDLEN, "%c:%s/%s_%x %s\n", + pp->retprobe ? 'r' : 'p', PERFPROBE_GROUP, + pp->function, pp->offset, pp->probes[0]); + write_new_event(fd, buf); + } else + for (i = 0; i < pp->found; i++) { + snprintf(buf, MAX_CMDLEN, "%c:%s/%s_%x_%d %s\n", + pp->retprobe ? 'r' : 'p', + PERFPROBE_GROUP, + pp->function, pp->offset, i, + pp->probes[0]); + write_new_event(fd, buf); + } + } + close(fd); return 0; } diff --git a/trunk/tools/perf/builtin-timechart.c b/trunk/tools/perf/builtin-timechart.c index cb58b6605fcc..dd4d82ac7aa4 100644 --- a/trunk/tools/perf/builtin-timechart.c +++ b/trunk/tools/perf/builtin-timechart.c @@ -29,14 +29,14 @@ #include "util/header.h" #include "util/parse-options.h" #include "util/parse-events.h" -#include "util/event.h" -#include "util/data_map.h" #include "util/svghelper.h" static char const *input_name = "perf.data"; static char const *output_name = "output.svg"; +static unsigned long page_size; +static unsigned long mmap_window = 32; static u64 sample_type; static unsigned int numcpus; @@ -49,6 +49,8 @@ static u64 first_time, last_time; static int power_only; +static struct perf_header *header; + struct per_pid; struct per_pidcomm; @@ -154,9 +156,9 @@ struct sample_wrapper *all_samples; struct process_filter; struct process_filter { - char *name; - int pid; - struct process_filter *next; + char *name; + int pid; + struct process_filter *next; }; static struct process_filter *process_filter; @@ -1043,6 +1045,36 @@ static void write_svg_file(const char *filename) svg_close(); } +static int +process_event(event_t *event) +{ + + switch (event->header.type) { + + case PERF_RECORD_COMM: + return process_comm_event(event); + case PERF_RECORD_FORK: + return process_fork_event(event); + case PERF_RECORD_EXIT: + return process_exit_event(event); + case PERF_RECORD_SAMPLE: + return queue_sample_event(event); + + /* + * We dont process them right now but they are fine: + */ + case PERF_RECORD_MMAP: + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: + return 0; + + default: + return -1; + } + + return 0; +} + static void process_samples(void) { struct sample_wrapper *cursor; @@ -1058,38 +1090,114 @@ static void process_samples(void) } } -static int sample_type_check(u64 type) + +static int __cmd_timechart(void) { - sample_type = type; + int err, rc = EXIT_FAILURE; + unsigned long offset = 0; + unsigned long head, shift; + struct stat statbuf; + event_t *event; + uint32_t size; + char *buf; + int input; + + input = open(input_name, O_RDONLY); + if (input < 0) { + fprintf(stderr, " failed to open file: %s", input_name); + if (!strcmp(input_name, "perf.data")) + fprintf(stderr, " (try 'perf record' first)"); + fprintf(stderr, "\n"); + exit(-1); + } - if (!(sample_type & PERF_SAMPLE_RAW)) { - fprintf(stderr, "No trace samples found in the file.\n" - "Have you used 'perf timechart record' to record it?\n"); - return -1; + err = fstat(input, &statbuf); + if (err < 0) { + perror("failed to stat file"); + exit(-1); } - return 0; -} + if (!statbuf.st_size) { + fprintf(stderr, "zero-sized file, nothing to do!\n"); + exit(0); + } -static struct perf_file_handler file_handler = { - .process_comm_event = process_comm_event, - .process_fork_event = process_fork_event, - .process_exit_event = process_exit_event, - .process_sample_event = queue_sample_event, - .sample_type_check = sample_type_check, -}; + header = perf_header__new(); + if (header == NULL) + return -ENOMEM; -static int __cmd_timechart(void) -{ - struct perf_header *header; - int ret; + err = perf_header__read(header, input); + if (err < 0) { + perf_header__delete(header); + return err; + } - register_perf_file_handler(&file_handler); + head = header->data_offset; + + sample_type = perf_header__sample_type(header); + + shift = page_size * (head / page_size); + offset += shift; + head -= shift; + +remap: + buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, + MAP_SHARED, input, offset); + if (buf == MAP_FAILED) { + perror("failed to mmap file"); + exit(-1); + } + +more: + event = (event_t *)(buf + head); + + size = event->header.size; + if (!size) + size = 8; + + if (head + event->header.size >= page_size * mmap_window) { + int ret2; + + shift = page_size * (head / page_size); + + ret2 = munmap(buf, page_size * mmap_window); + assert(ret2 == 0); + + offset += shift; + head -= shift; + goto remap; + } + + size = event->header.size; + + if (!size || process_event(event) < 0) { + pr_warning("%p [%p]: skipping unknown header type: %d\n", + (void *)(offset + head), + (void *)(long)(event->header.size), + event->header.type); + /* + * assume we lost track of the stream, check alignment, and + * increment a single u64 in the hope to catch on again 'soon'. + */ + + if (unlikely(head & 7)) + head &= ~7ULL; + + size = 8; + } + + head += size; + + if (offset + head >= header->data_offset + header->data_size) + goto done; + + if (offset + head < (unsigned long)statbuf.st_size) + goto more; + +done: + rc = EXIT_SUCCESS; + close(input); - ret = mmap_dispatch_perf_file(&header, input_name, 0, 0, - &event__cwdlen, &event__cwd); - if (ret) - return EXIT_FAILURE; process_samples(); @@ -1102,7 +1210,7 @@ static int __cmd_timechart(void) pr_info("Written %2.1f seconds of trace to %s.\n", (last_time - first_time) / 1000000000.0, output_name); - return EXIT_SUCCESS; + return rc; } static const char * const timechart_usage[] = { @@ -1169,6 +1277,8 @@ int cmd_timechart(int argc, const char **argv, const char *prefix __used) { symbol__init(0); + page_size = getpagesize(); + argc = parse_options(argc, argv, options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); diff --git a/trunk/tools/perf/builtin-trace.c b/trunk/tools/perf/builtin-trace.c index a7750256c408..e96bb534b948 100644 --- a/trunk/tools/perf/builtin-trace.c +++ b/trunk/tools/perf/builtin-trace.c @@ -6,6 +6,46 @@ #include "util/thread.h" #include "util/header.h" +static char const *script_name; +static char const *generate_script_lang; + +static int default_start_script(const char *script __attribute((unused))) +{ + return 0; +} + +static int default_stop_script(void) +{ + return 0; +} + +static int default_generate_script(const char *outfile __attribute ((unused))) +{ + return 0; +} + +static struct scripting_ops default_scripting_ops = { + .start_script = default_start_script, + .stop_script = default_stop_script, + .process_event = print_event, + .generate_script = default_generate_script, +}; + +static struct scripting_ops *scripting_ops; + +static void setup_scripting(void) +{ + /* make sure PERF_EXEC_PATH is set for scripts */ + perf_set_argv_exec_path(perf_exec_path()); + + scripting_ops = &default_scripting_ops; +} + +static int cleanup_scripting(void) +{ + return scripting_ops->stop_script(); +} + #include "util/parse-options.h" #include "perf.h" @@ -13,11 +53,12 @@ #include "util/trace-event.h" #include "util/data_map.h" +#include "util/exec_cmd.h" -static char const *input_name = "perf.data"; +static char const *input_name = "perf.data"; -static struct perf_header *header; -static u64 sample_type; +static struct perf_header *header; +static u64 sample_type; static int process_sample_event(event_t *event) { @@ -69,7 +110,8 @@ static int process_sample_event(event_t *event) * field, although it should be the same than this perf * event pid */ - print_event(cpu, raw->data, raw->size, timestamp, thread->comm); + scripting_ops->process_event(cpu, raw->data, raw->size, + timestamp, thread->comm); } event__stats.total += period; @@ -105,6 +147,154 @@ static int __cmd_trace(void) 0, 0, &event__cwdlen, &event__cwd); } +struct script_spec { + struct list_head node; + struct scripting_ops *ops; + char spec[0]; +}; + +LIST_HEAD(script_specs); + +static struct script_spec *script_spec__new(const char *spec, + struct scripting_ops *ops) +{ + struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1); + + if (s != NULL) { + strcpy(s->spec, spec); + s->ops = ops; + } + + return s; +} + +static void script_spec__delete(struct script_spec *s) +{ + free(s->spec); + free(s); +} + +static void script_spec__add(struct script_spec *s) +{ + list_add_tail(&s->node, &script_specs); +} + +static struct script_spec *script_spec__find(const char *spec) +{ + struct script_spec *s; + + list_for_each_entry(s, &script_specs, node) + if (strcasecmp(s->spec, spec) == 0) + return s; + return NULL; +} + +static struct script_spec *script_spec__findnew(const char *spec, + struct scripting_ops *ops) +{ + struct script_spec *s = script_spec__find(spec); + + if (s) + return s; + + s = script_spec__new(spec, ops); + if (!s) + goto out_delete_spec; + + script_spec__add(s); + + return s; + +out_delete_spec: + script_spec__delete(s); + + return NULL; +} + +int script_spec_register(const char *spec, struct scripting_ops *ops) +{ + struct script_spec *s; + + s = script_spec__find(spec); + if (s) + return -1; + + s = script_spec__findnew(spec, ops); + if (!s) + return -1; + + return 0; +} + +static struct scripting_ops *script_spec__lookup(const char *spec) +{ + struct script_spec *s = script_spec__find(spec); + if (!s) + return NULL; + + return s->ops; +} + +static void list_available_languages(void) +{ + struct script_spec *s; + + fprintf(stderr, "\n"); + fprintf(stderr, "Scripting language extensions (used in " + "perf trace -s [spec:]script.[spec]):\n\n"); + + list_for_each_entry(s, &script_specs, node) + fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); + + fprintf(stderr, "\n"); +} + +static int parse_scriptname(const struct option *opt __used, + const char *str, int unset __used) +{ + char spec[PATH_MAX]; + const char *script, *ext; + int len; + + if (strcmp(str, "list") == 0) { + list_available_languages(); + return 0; + } + + script = strchr(str, ':'); + if (script) { + len = script - str; + if (len >= PATH_MAX) { + fprintf(stderr, "invalid language specifier"); + return -1; + } + strncpy(spec, str, len); + spec[len] = '\0'; + scripting_ops = script_spec__lookup(spec); + if (!scripting_ops) { + fprintf(stderr, "invalid language specifier"); + return -1; + } + script++; + } else { + script = str; + ext = strchr(script, '.'); + if (!ext) { + fprintf(stderr, "invalid script extension"); + return -1; + } + scripting_ops = script_spec__lookup(++ext); + if (!scripting_ops) { + fprintf(stderr, "invalid script extension"); + return -1; + } + } + + script_name = strdup(script); + + return 0; +} + static const char * const annotate_usage[] = { "perf trace [] ", NULL @@ -117,13 +307,23 @@ static const struct option options[] = { "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('l', "latency", &latency_format, "show latency attributes (irqs/preemption disabled, etc)"), + OPT_CALLBACK('s', "script", NULL, "name", + "script file name (lang:script name, script name, or *)", + parse_scriptname), + OPT_STRING('g', "gen-script", &generate_script_lang, "lang", + "generate perf-trace.xx script in specified language"), + OPT_END() }; int cmd_trace(int argc, const char **argv, const char *prefix __used) { + int err; + symbol__init(0); + setup_scripting(); + argc = parse_options(argc, argv, options, annotate_usage, 0); if (argc) { /* @@ -136,5 +336,50 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) setup_pager(); - return __cmd_trace(); + if (generate_script_lang) { + struct stat perf_stat; + + int input = open(input_name, O_RDONLY); + if (input < 0) { + perror("failed to open file"); + exit(-1); + } + + err = fstat(input, &perf_stat); + if (err < 0) { + perror("failed to stat file"); + exit(-1); + } + + if (!perf_stat.st_size) { + fprintf(stderr, "zero-sized file, nothing to do!\n"); + exit(0); + } + + scripting_ops = script_spec__lookup(generate_script_lang); + if (!scripting_ops) { + fprintf(stderr, "invalid language specifier"); + return -1; + } + + header = perf_header__new(); + if (header == NULL) + return -1; + + perf_header__read(header, input); + err = scripting_ops->generate_script("perf-trace"); + goto out; + } + + if (script_name) { + err = scripting_ops->start_script(script_name); + if (err) + goto out; + } + + err = __cmd_trace(); + + cleanup_scripting(); +out: + return err; } diff --git a/trunk/tools/perf/util/event.c b/trunk/tools/perf/util/event.c index 414b89d1bde9..233d7ad9bd7f 100644 --- a/trunk/tools/perf/util/event.c +++ b/trunk/tools/perf/util/event.c @@ -186,7 +186,8 @@ int event__process_comm(event_t *self) { struct thread *thread = threads__findnew(self->comm.pid); - dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); + dump_printf("PERF_RECORD_COMM: %s:%d\n", + self->comm.comm, self->comm.pid); if (thread == NULL || thread__set_comm(thread, self->comm.comm)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); diff --git a/trunk/tools/perf/util/probe-event.c b/trunk/tools/perf/util/probe-event.c deleted file mode 100644 index cd7fbda5e2a5..000000000000 --- a/trunk/tools/perf/util/probe-event.c +++ /dev/null @@ -1,484 +0,0 @@ -/* - * probe-event.c : perf-probe definition to kprobe_events format converter - * - * Written by Masami Hiramatsu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef _GNU_SOURCE -#include "event.h" -#include "string.h" -#include "strlist.h" -#include "debug.h" -#include "parse-events.h" /* For debugfs_path */ -#include "probe-event.h" - -#define MAX_CMDLEN 256 -#define MAX_PROBE_ARGS 128 -#define PERFPROBE_GROUP "probe" - -#define semantic_error(msg ...) die("Semantic error :" msg) - -/* If there is no space to write, returns -E2BIG. */ -static int e_snprintf(char *str, size_t size, const char *format, ...) -{ - int ret; - va_list ap; - va_start(ap, format); - ret = vsnprintf(str, size, format, ap); - va_end(ap); - if (ret >= (int)size) - ret = -E2BIG; - return ret; -} - -/* Parse probepoint definition. */ -static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) -{ - char *ptr, *tmp; - char c, nc = 0; - /* - * - * perf probe SRC:LN - * perf probe FUNC[+OFFS|%return][@SRC] - */ - - ptr = strpbrk(arg, ":+@%"); - if (ptr) { - nc = *ptr; - *ptr++ = '\0'; - } - - /* Check arg is function or file and copy it */ - if (strchr(arg, '.')) /* File */ - pp->file = strdup(arg); - else /* Function */ - pp->function = strdup(arg); - DIE_IF(pp->file == NULL && pp->function == NULL); - - /* Parse other options */ - while (ptr) { - arg = ptr; - c = nc; - ptr = strpbrk(arg, ":+@%"); - if (ptr) { - nc = *ptr; - *ptr++ = '\0'; - } - switch (c) { - case ':': /* Line number */ - pp->line = strtoul(arg, &tmp, 0); - if (*tmp != '\0') - semantic_error("There is non-digit charactor" - " in line number."); - break; - case '+': /* Byte offset from a symbol */ - pp->offset = strtoul(arg, &tmp, 0); - if (*tmp != '\0') - semantic_error("There is non-digit charactor" - " in offset."); - break; - case '@': /* File name */ - if (pp->file) - semantic_error("SRC@SRC is not allowed."); - pp->file = strdup(arg); - DIE_IF(pp->file == NULL); - if (ptr) - semantic_error("@SRC must be the last " - "option."); - break; - case '%': /* Probe places */ - if (strcmp(arg, "return") == 0) { - pp->retprobe = 1; - } else /* Others not supported yet */ - semantic_error("%%%s is not supported.", arg); - break; - default: - DIE_IF("Program has a bug."); - break; - } - } - - /* Exclusion check */ - if (pp->line && pp->offset) - semantic_error("Offset can't be used with line number."); - - if (!pp->line && pp->file && !pp->function) - semantic_error("File always requires line number."); - - if (pp->offset && !pp->function) - semantic_error("Offset requires an entry function."); - - if (pp->retprobe && !pp->function) - semantic_error("Return probe requires an entry function."); - - if ((pp->offset || pp->line) && pp->retprobe) - semantic_error("Offset/Line can't be used with return probe."); - - pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", - pp->function, pp->file, pp->line, pp->offset, pp->retprobe); -} - -/* Parse perf-probe event definition */ -int parse_perf_probe_event(const char *str, struct probe_point *pp) -{ - char **argv; - int argc, i, need_dwarf = 0; - - argv = argv_split(str, &argc); - if (!argv) - die("argv_split failed."); - if (argc > MAX_PROBE_ARGS + 1) - semantic_error("Too many arguments"); - - /* Parse probe point */ - parse_perf_probe_probepoint(argv[0], pp); - if (pp->file || pp->line) - need_dwarf = 1; - - /* Copy arguments and ensure return probe has no C argument */ - pp->nr_args = argc - 1; - pp->args = zalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { - pp->args[i] = strdup(argv[i + 1]); - if (!pp->args[i]) - die("Failed to copy argument."); - if (is_c_varname(pp->args[i])) { - if (pp->retprobe) - semantic_error("You can't specify local" - " variable for kretprobe"); - need_dwarf = 1; - } - } - - argv_free(argv); - return need_dwarf; -} - -/* Parse kprobe_events event into struct probe_point */ -void parse_trace_kprobe_event(const char *str, char **group, char **event, - struct probe_point *pp) -{ - char pr; - char *p; - int ret, i, argc; - char **argv; - - pr_debug("Parsing kprobe_events: %s\n", str); - argv = argv_split(str, &argc); - if (!argv) - die("argv_split failed."); - if (argc < 2) - semantic_error("Too less arguments."); - - /* Scan event and group name. */ - ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)group, (float *)(void *)event); - if (ret != 3) - semantic_error("Failed to parse event name: %s", argv[0]); - pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr); - - if (!pp) - goto end; - - pp->retprobe = (pr == 'r'); - - /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset); - if (ret == 1) - pp->offset = 0; - - /* kprobe_events doesn't have this information */ - pp->line = 0; - pp->file = NULL; - - pp->nr_args = argc - 2; - pp->args = zalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { - p = strchr(argv[i + 2], '='); - if (p) /* We don't need which register is assigned. */ - *p = '\0'; - pp->args[i] = strdup(argv[i + 2]); - if (!pp->args[i]) - die("Failed to copy argument."); - } - -end: - argv_free(argv); -} - -int synthesize_perf_probe_event(struct probe_point *pp) -{ - char *buf; - char offs[64] = "", line[64] = ""; - int i, len, ret; - - pp->probes[0] = buf = zalloc(MAX_CMDLEN); - if (!buf) - die("Failed to allocate memory by zalloc."); - if (pp->offset) { - ret = e_snprintf(offs, 64, "+%d", pp->offset); - if (ret <= 0) - goto error; - } - if (pp->line) { - ret = e_snprintf(line, 64, ":%d", pp->line); - if (ret <= 0) - goto error; - } - - if (pp->function) - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, - offs, pp->retprobe ? "%return" : "", line); - else - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line); - if (ret <= 0) - goto error; - len = ret; - - for (i = 0; i < pp->nr_args; i++) { - ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); - if (ret <= 0) - goto error; - len += ret; - } - pp->found = 1; - - return pp->found; -error: - free(pp->probes[0]); - - return ret; -} - -int synthesize_trace_kprobe_event(struct probe_point *pp) -{ - char *buf; - int i, len, ret; - - pp->probes[0] = buf = zalloc(MAX_CMDLEN); - if (!buf) - die("Failed to allocate memory by zalloc."); - ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); - if (ret <= 0) - goto error; - len = ret; - - for (i = 0; i < pp->nr_args; i++) { - ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); - if (ret <= 0) - goto error; - len += ret; - } - pp->found = 1; - - return pp->found; -error: - free(pp->probes[0]); - - return ret; -} - -static int open_kprobe_events(int flags, int mode) -{ - char buf[PATH_MAX]; - int ret; - - ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); - if (ret < 0) - die("Failed to make kprobe_events path."); - - ret = open(buf, flags, mode); - if (ret < 0) { - if (errno == ENOENT) - die("kprobe_events file does not exist -" - " please rebuild with CONFIG_KPROBE_TRACER."); - else - die("Could not open kprobe_events file: %s", - strerror(errno)); - } - return ret; -} - -/* Get raw string list of current kprobe_events */ -static struct strlist *get_trace_kprobe_event_rawlist(int fd) -{ - int ret, idx; - FILE *fp; - char buf[MAX_CMDLEN]; - char *p; - struct strlist *sl; - - sl = strlist__new(true, NULL); - - fp = fdopen(dup(fd), "r"); - while (!feof(fp)) { - p = fgets(buf, MAX_CMDLEN, fp); - if (!p) - break; - - idx = strlen(p) - 1; - if (p[idx] == '\n') - p[idx] = '\0'; - ret = strlist__add(sl, buf); - if (ret < 0) - die("strlist__add failed: %s", strerror(-ret)); - } - fclose(fp); - - return sl; -} - -/* Free and zero clear probe_point */ -static void clear_probe_point(struct probe_point *pp) -{ - int i; - - if (pp->function) - free(pp->function); - if (pp->file) - free(pp->file); - for (i = 0; i < pp->nr_args; i++) - free(pp->args[i]); - if (pp->args) - free(pp->args); - for (i = 0; i < pp->found; i++) - free(pp->probes[i]); - memset(pp, 0, sizeof(pp)); -} - -/* List up current perf-probe events */ -void show_perf_probe_events(void) -{ - unsigned int i; - int fd; - char *group, *event; - struct probe_point pp; - struct strlist *rawlist; - struct str_node *ent; - - fd = open_kprobe_events(O_RDONLY, 0); - rawlist = get_trace_kprobe_event_rawlist(fd); - close(fd); - - for (i = 0; i < strlist__nr_entries(rawlist); i++) { - ent = strlist__entry(rawlist, i); - parse_trace_kprobe_event(ent->s, &group, &event, &pp); - synthesize_perf_probe_event(&pp); - printf("[%s:%s]\t%s\n", group, event, pp.probes[0]); - free(group); - free(event); - clear_probe_point(&pp); - } - - strlist__delete(rawlist); -} - -/* Get current perf-probe event names */ -static struct strlist *get_perf_event_names(int fd) -{ - unsigned int i; - char *group, *event; - struct strlist *sl, *rawlist; - struct str_node *ent; - - rawlist = get_trace_kprobe_event_rawlist(fd); - - sl = strlist__new(false, NULL); - for (i = 0; i < strlist__nr_entries(rawlist); i++) { - ent = strlist__entry(rawlist, i); - parse_trace_kprobe_event(ent->s, &group, &event, NULL); - strlist__add(sl, event); - free(group); - } - - strlist__delete(rawlist); - - return sl; -} - -static int write_trace_kprobe_event(int fd, const char *buf) -{ - int ret; - - ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to create event."); - else - printf("Added new event: %s\n", buf); - - return ret; -} - -static void get_new_event_name(char *buf, size_t len, const char *base, - struct strlist *namelist) -{ - int i, ret; - for (i = 0; i < MAX_EVENT_INDEX; i++) { - ret = e_snprintf(buf, len, "%s_%d", base, i); - if (ret < 0) - die("snprintf() failed: %s", strerror(-ret)); - if (!strlist__has_entry(namelist, buf)) - break; - } - if (i == MAX_EVENT_INDEX) - die("Too many events are on the same function."); -} - -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) -{ - int i, j, fd; - struct probe_point *pp; - char buf[MAX_CMDLEN]; - char event[64]; - struct strlist *namelist; - - fd = open_kprobe_events(O_RDWR, O_APPEND); - /* Get current event names */ - namelist = get_perf_event_names(fd); - - for (j = 0; j < nr_probes; j++) { - pp = probes + j; - for (i = 0; i < pp->found; i++) { - /* Get an unused new event name */ - get_new_event_name(event, 64, pp->function, namelist); - snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", - pp->retprobe ? 'r' : 'p', - PERFPROBE_GROUP, event, - pp->probes[i]); - write_trace_kprobe_event(fd, buf); - /* Add added event name to namelist */ - strlist__add(namelist, event); - } - } - close(fd); -} diff --git a/trunk/tools/perf/util/probe-event.h b/trunk/tools/perf/util/probe-event.h deleted file mode 100644 index 0c6fe56fe38a..000000000000 --- a/trunk/tools/perf/util/probe-event.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _PROBE_EVENT_H -#define _PROBE_EVENT_H - -#include "probe-finder.h" -#include "strlist.h" - -extern int parse_perf_probe_event(const char *str, struct probe_point *pp); -extern int synthesize_perf_probe_event(struct probe_point *pp); -extern void parse_trace_kprobe_event(const char *str, char **group, - char **event, struct probe_point *pp); -extern int synthesize_trace_kprobe_event(struct probe_point *pp); -extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); -extern void show_perf_probe_events(void); - -/* Maximum index number of event-name postfix */ -#define MAX_EVENT_INDEX 1024 - -#endif /*_PROBE_EVENT_H */ diff --git a/trunk/tools/perf/util/string.c b/trunk/tools/perf/util/string.c index f24a8cc933d5..227043577e06 100644 --- a/trunk/tools/perf/util/string.c +++ b/trunk/tools/perf/util/string.c @@ -1,3 +1,5 @@ +#include +#include #include "string.h" #include "util.h" @@ -125,104 +127,3 @@ s64 perf_atoll(const char *str) out: return length; } - -/* - * Helper function for splitting a string into an argv-like array. - * originaly copied from lib/argv_split.c - */ -static const char *skip_sep(const char *cp) -{ - while (*cp && isspace(*cp)) - cp++; - - return cp; -} - -static const char *skip_arg(const char *cp) -{ - while (*cp && !isspace(*cp)) - cp++; - - return cp; -} - -static int count_argc(const char *str) -{ - int count = 0; - - while (*str) { - str = skip_sep(str); - if (*str) { - count++; - str = skip_arg(str); - } - } - - return count; -} - -/** - * argv_free - free an argv - * @argv - the argument vector to be freed - * - * Frees an argv and the strings it points to. - */ -void argv_free(char **argv) -{ - char **p; - for (p = argv; *p; p++) - free(*p); - - free(argv); -} - -/** - * argv_split - split a string at whitespace, returning an argv - * @str: the string to be split - * @argcp: returned argument count - * - * Returns an array of pointers to strings which are split out from - * @str. This is performed by strictly splitting on white-space; no - * quote processing is performed. Multiple whitespace characters are - * considered to be a single argument separator. The returned array - * is always NULL-terminated. Returns NULL on memory allocation - * failure. - */ -char **argv_split(const char *str, int *argcp) -{ - int argc = count_argc(str); - char **argv = zalloc(sizeof(*argv) * (argc+1)); - char **argvp; - - if (argv == NULL) - goto out; - - if (argcp) - *argcp = argc; - - argvp = argv; - - while (*str) { - str = skip_sep(str); - - if (*str) { - const char *p = str; - char *t; - - str = skip_arg(str); - - t = strndup(p, str-p); - if (t == NULL) - goto fail; - *argvp++ = t; - } - } - *argvp = NULL; - -out: - return argv; - -fail: - argv_free(argv); - return NULL; -} diff --git a/trunk/tools/perf/util/string.h b/trunk/tools/perf/util/string.h index bfecec265a1a..e50b07f80827 100644 --- a/trunk/tools/perf/util/string.h +++ b/trunk/tools/perf/util/string.h @@ -6,8 +6,6 @@ int hex2u64(const char *ptr, u64 *val); char *strxfrchar(char *s, char from, char to); s64 perf_atoll(const char *str); -char **argv_split(const char *str, int *argcp); -void argv_free(char **argv); #define _STR(x) #x #define STR(x) _STR(x) diff --git a/trunk/tools/perf/util/trace-event.h b/trunk/tools/perf/util/trace-event.h index dd51c6872a15..e7aaf002e667 100644 --- a/trunk/tools/perf/util/trace-event.h +++ b/trunk/tools/perf/util/trace-event.h @@ -259,4 +259,15 @@ enum trace_flag_type { TRACE_FLAG_SOFTIRQ = 0x10, }; +struct scripting_ops { + const char *name; + int (*start_script) (const char *); + int (*stop_script) (void); + void (*process_event) (int cpu, void *data, int size, + unsigned long long nsecs, char *comm); + int (*generate_script) (const char *outfile); +}; + +int script_spec_register(const char *spec, struct scripting_ops *ops); + #endif /* __PERF_TRACE_EVENTS_H */