| #include "builtin.h" |
| |
| #include "util/util.h" |
| #include "util/cache.h" |
| #include "util/symbol.h" |
| #include "util/thread.h" |
| #include "util/header.h" |
| #include "util/exec_cmd.h" |
| #include "util/trace-event.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()); |
| |
| setup_perl_scripting(); |
| |
| scripting_ops = &default_scripting_ops; |
| } |
| |
| static int cleanup_scripting(void) |
| { |
| return scripting_ops->stop_script(); |
| } |
| |
| #include "util/parse-options.h" |
| |
| #include "perf.h" |
| #include "util/debug.h" |
| |
| #include "util/trace-event.h" |
| #include "util/data_map.h" |
| #include "util/exec_cmd.h" |
| |
| static char const *input_name = "perf.data"; |
| |
| static struct perf_header *header; |
| static u64 sample_type; |
| |
| static int process_sample_event(event_t *event) |
| { |
| u64 ip = event->ip.ip; |
| u64 timestamp = -1; |
| u32 cpu = -1; |
| u64 period = 1; |
| void *more_data = event->ip.__more_data; |
| struct thread *thread = threads__findnew(event->ip.pid); |
| |
| if (sample_type & PERF_SAMPLE_TIME) { |
| timestamp = *(u64 *)more_data; |
| more_data += sizeof(u64); |
| } |
| |
| if (sample_type & PERF_SAMPLE_CPU) { |
| cpu = *(u32 *)more_data; |
| more_data += sizeof(u32); |
| more_data += sizeof(u32); /* reserved */ |
| } |
| |
| if (sample_type & PERF_SAMPLE_PERIOD) { |
| period = *(u64 *)more_data; |
| more_data += sizeof(u64); |
| } |
| |
| dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
| event->header.misc, |
| event->ip.pid, event->ip.tid, |
| (void *)(long)ip, |
| (long long)period); |
| |
| if (thread == NULL) { |
| pr_debug("problem processing %d event, skipping it.\n", |
| event->header.type); |
| return -1; |
| } |
| |
| dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
| |
| if (sample_type & PERF_SAMPLE_RAW) { |
| struct { |
| u32 size; |
| char data[0]; |
| } *raw = more_data; |
| |
| /* |
| * FIXME: better resolve from pid from the struct trace_entry |
| * field, although it should be the same than this perf |
| * event pid |
| */ |
| scripting_ops->process_event(cpu, raw->data, raw->size, |
| timestamp, thread->comm); |
| } |
| event__stats.total += period; |
| |
| return 0; |
| } |
| |
| static int sample_type_check(u64 type) |
| { |
| sample_type = type; |
| |
| if (!(sample_type & PERF_SAMPLE_RAW)) { |
| fprintf(stderr, |
| "No trace sample to read. Did you call perf record " |
| "without -R?"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static struct perf_file_handler file_handler = { |
| .process_sample_event = process_sample_event, |
| .process_comm_event = event__process_comm, |
| .sample_type_check = sample_type_check, |
| }; |
| |
| static int __cmd_trace(void) |
| { |
| register_idle_thread(); |
| register_perf_file_handler(&file_handler); |
| |
| return mmap_dispatch_perf_file(&header, input_name, |
| 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 [<options>] <command>", |
| NULL |
| }; |
| |
| static const struct option options[] = { |
| OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| "dump raw trace in ASCII"), |
| OPT_BOOLEAN('v', "verbose", &verbose, |
| "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) { |
| /* |
| * Special case: if there's an argument left then assume tha |
| * it's a symbol filter: |
| */ |
| if (argc > 1) |
| usage_with_options(annotate_usage, options); |
| } |
| |
| setup_pager(); |
| |
| 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; |
| } |