|  | /* Copyright (c) 2015-2016 Google Inc | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * Davide Libenzi <dlibenzi@google.com> | 
|  | * See LICENSE for details. | 
|  | */ | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/wait.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #include <fcntl.h> | 
|  | #include <limits.h> | 
|  | #include <errno.h> | 
|  | #include <argp.h> | 
|  | #include <time.h> | 
|  | #include <parlib/parlib.h> | 
|  | #include <parlib/timing.h> | 
|  | #include <parlib/core_set.h> | 
|  | #include "xlib.h" | 
|  | #include "perfconv.h" | 
|  | #include "perf_core.h" | 
|  |  | 
|  | /* Helpers */ | 
|  | static void run_process_and_wait(int argc, char *argv[], | 
|  | const struct core_set *cores); | 
|  |  | 
|  | /* For communicating with perf_create_context() */ | 
|  | static struct perf_context_config perf_cfg = { | 
|  | .perf_file = "#arch/perf", | 
|  | .kpctl_file = "#kprof/kpctl", | 
|  | .kpdata_file = "#kprof/kpdata", | 
|  | }; | 
|  |  | 
|  | static struct perfconv_context *cctx; | 
|  | static struct perf_context *pctx; | 
|  | extern char **environ;	/* POSIX envp */ | 
|  |  | 
|  | struct perf_opts { | 
|  | FILE			*outfile; | 
|  | const char		*events; | 
|  | char			**cmd_argv; | 
|  | int			cmd_argc; | 
|  | struct core_set		cores; | 
|  | bool			got_cores; | 
|  | bool			verbose; | 
|  | bool			sampling; | 
|  | bool			stat_bignum; | 
|  | bool			record_quiet; | 
|  | unsigned long		record_period; | 
|  | }; | 
|  | static struct perf_opts opts; | 
|  |  | 
|  | struct perf_cmd { | 
|  | char			*name; | 
|  | char			*desc; | 
|  | char			*opts; | 
|  | int (*func)(struct perf_cmd *, int, char **); | 
|  | }; | 
|  |  | 
|  | static int perf_help(struct perf_cmd *cmd, int argc, char *argv[]); | 
|  | static int perf_list(struct perf_cmd *cmd, int argc, char *argv[]); | 
|  | static int perf_record(struct perf_cmd *cmd, int argc, char *argv[]); | 
|  | static int perf_stat(struct perf_cmd *cmd, int argc, char *argv[]); | 
|  | static int perf_pmu_caps(struct perf_cmd *cmd, int argc, char *argv[]); | 
|  |  | 
|  | static struct perf_cmd perf_cmds[] = { | 
|  | { .name = "help", | 
|  | .desc = "Detailed help for commands", | 
|  | .opts = "COMMAND", | 
|  | .func = perf_help, | 
|  | }, | 
|  | { .name = "list", | 
|  | .desc = "Lists all available events", | 
|  | .opts = "[REGEX]", | 
|  | .func = perf_list, | 
|  | }, | 
|  | { .name = "record", | 
|  | .desc = "Samples events during command execution", | 
|  | .opts = 0, | 
|  | .func = perf_record, | 
|  | }, | 
|  | { .name = "stat", | 
|  | .desc = "Counts events during command execution", | 
|  | .opts = 0, | 
|  | .func = perf_stat, | 
|  | }, | 
|  | { .name = "pmu_caps", | 
|  | .desc = "Shows PMU capabilities", | 
|  | .opts = "", | 
|  | .func = perf_pmu_caps, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | /**************************** perf help ****************************/ | 
|  |  | 
|  | static int perf_help(struct perf_cmd *cmd, int argc, char *argv[]) | 
|  | { | 
|  | char *sub_argv[2]; | 
|  |  | 
|  | if (argc < 2) { | 
|  | fprintf(stderr, "perf %s %s\n", cmd->name, cmd->opts); | 
|  | return -1; | 
|  | } | 
|  | for (int i = 0; i < COUNT_OF(perf_cmds); i++) { | 
|  | if (!strcmp(perf_cmds[i].name, argv[1])) { | 
|  | if (perf_cmds[i].opts) { | 
|  | fprintf(stdout, "perf %s %s\n", | 
|  | perf_cmds[i].name, perf_cmds[i].opts); | 
|  | fprintf(stdout, "\t%s\n", perf_cmds[i].desc); | 
|  | } else { | 
|  | /* For argp subcommands, call their help | 
|  | * directly. */ | 
|  | sub_argv[0] = xstrdup(perf_cmds[i].name); | 
|  | sub_argv[1] = xstrdup("--help"); | 
|  | perf_cmds[i].func(&perf_cmds[i], 2, sub_argv); | 
|  | free(sub_argv[0]); | 
|  | free(sub_argv[1]); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | fprintf(stderr, "Unknown perf command %s\n", argv[1]); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /**************************** perf list ****************************/ | 
|  |  | 
|  | static int perf_list(struct perf_cmd *cmd, int argc, char *argv[]) | 
|  | { | 
|  | char *show_regex = NULL; | 
|  |  | 
|  | if (argc > 1) | 
|  | show_regex = argv[1]; | 
|  | perf_show_events(show_regex, stdout); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /**************************** perf pmu_caps ************************/ | 
|  |  | 
|  | static int perf_pmu_caps(struct perf_cmd *cmd, int argc, char *argv[]) | 
|  | { | 
|  | const struct perf_arch_info *pai = perf_context_get_arch_info(pctx); | 
|  |  | 
|  | fprintf(stdout, | 
|  | "PERF.version             = %u\n" | 
|  | "PERF.proc_arch_events    = %u\n" | 
|  | "PERF.bits_x_counter      = %u\n" | 
|  | "PERF.counters_x_proc     = %u\n" | 
|  | "PERF.bits_x_fix_counter  = %u\n" | 
|  | "PERF.fix_counters_x_proc = %u\n", | 
|  | pai->perfmon_version, pai->proc_arch_events, | 
|  | pai->bits_x_counter, pai->counters_x_proc, | 
|  | pai->bits_x_fix_counter, pai->fix_counters_x_proc); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /**************************** Common argp ************************/ | 
|  |  | 
|  | /* Collection argument parsing.  These options are common to any function that | 
|  | * will collect perf events, e.g. perf record and perf stat. */ | 
|  |  | 
|  | static struct argp_option collect_opts[] = { | 
|  | {"event", 'e', "EVENT", 0, "Event string, e.g. cycles:u:k"}, | 
|  | {"cores", 'C', "CORE_LIST", 0, "List of cores, e.g. 0.2.4:8-19"}, | 
|  | {"cpu", 'C', 0, OPTION_ALIAS}, | 
|  | {"all-cpus", 'a', 0, 0, "Collect events on all cores (on by default)"}, | 
|  | {"verbose", 'v', 0, 0, 0}, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | static const char *collect_args_doc = "COMMAND [ARGS]"; | 
|  |  | 
|  | static error_t parse_collect_opt(int key, char *arg, struct argp_state *state) | 
|  | { | 
|  | struct perf_opts *p_opts = state->input; | 
|  |  | 
|  | /* argp doesn't pass input to the child parser(s) by default... */ | 
|  | state->child_inputs[0] = state->input; | 
|  |  | 
|  | switch (key) { | 
|  | case 'a': | 
|  | /* Our default operation is to track all cores; we don't follow | 
|  | * processes yet. */ | 
|  | break; | 
|  | case 'C': | 
|  | parlib_parse_cores(arg, &p_opts->cores); | 
|  | p_opts->got_cores = TRUE; | 
|  | break; | 
|  | case 'e': | 
|  | p_opts->events = arg; | 
|  | break; | 
|  | case 'v': | 
|  | p_opts->verbose = TRUE; | 
|  | break; | 
|  | case ARGP_KEY_ARG: | 
|  | p_opts->cmd_argc = state->argc - state->next + 1; | 
|  | p_opts->cmd_argv = xmalloc(sizeof(char*) * | 
|  | (p_opts->cmd_argc + 1)); | 
|  | p_opts->cmd_argv[0] = arg; | 
|  | memcpy(&p_opts->cmd_argv[1], &state->argv[state->next], | 
|  | sizeof(char*) * (p_opts->cmd_argc - 1)); | 
|  | p_opts->cmd_argv[p_opts->cmd_argc] = NULL; | 
|  | state->next = state->argc; | 
|  | break; | 
|  | case ARGP_KEY_END: | 
|  | if (!p_opts->cmd_argc) | 
|  | argp_usage(state); | 
|  | /* By default, we set all cores (different than linux) */ | 
|  | if (!p_opts->got_cores) | 
|  | parlib_get_all_core_set(&p_opts->cores); | 
|  | break; | 
|  | default: | 
|  | return ARGP_ERR_UNKNOWN; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Helper, parses args using the collect_opts and the child parser for a given | 
|  | * cmd. */ | 
|  | static void collect_argp(struct perf_cmd *cmd, int argc, char *argv[], | 
|  | struct argp_child *children, struct perf_opts *opts) | 
|  | { | 
|  | struct argp collect_opt = {collect_opts, parse_collect_opt, | 
|  | collect_args_doc, cmd->desc, children}; | 
|  | char *cmd_name; | 
|  | const char *fmt = "perf %s"; | 
|  | size_t cmd_sz = strlen(cmd->name) + strlen(fmt) + 1; | 
|  |  | 
|  | /* Rewrite the command name from foo to perf foo for the --help output*/ | 
|  | cmd_name = xmalloc(cmd_sz); | 
|  | snprintf(cmd_name, cmd_sz, fmt, cmd->name); | 
|  | cmd_name[cmd_sz - 1] = '\0'; | 
|  | argv[0] = cmd_name; | 
|  | argp_parse(&collect_opt, argc, argv, ARGP_IN_ORDER, 0, opts); | 
|  | /* It's possible that someone could still be using cmd_name */ | 
|  | } | 
|  |  | 
|  | /* Helper, submits the events in opts to the kernel for monitoring. */ | 
|  | static void submit_events(struct perf_opts *opts) | 
|  | { | 
|  | struct perf_eventsel *sel; | 
|  | char *dup_evts, *tok, *tok_save = 0; | 
|  |  | 
|  | dup_evts = xstrdup(opts->events); | 
|  | for (tok = strtok_r(dup_evts, ",", &tok_save); | 
|  | tok; | 
|  | tok = strtok_r(NULL, ",", &tok_save)) { | 
|  |  | 
|  | sel = perf_parse_event(tok); | 
|  | PMEV_SET_INTEN(sel->ev.event, opts->sampling); | 
|  | sel->ev.trigger_count = opts->record_period; | 
|  | perf_context_event_submit(pctx, &opts->cores, sel); | 
|  | } | 
|  | free(dup_evts); | 
|  | } | 
|  |  | 
|  | /**************************** perf record ************************/ | 
|  |  | 
|  | static struct argp_option record_opts[] = { | 
|  | {"count", 'c', "PERIOD", 0, "Sampling period"}, | 
|  | {"output", 'o', "FILE", 0, "Output file name (default perf.data)"}, | 
|  | {"freq", 'F', "FREQUENCY", 0, "Sampling frequency (assumes cycles)"}, | 
|  | {"call-graph", 'g', 0, 0, "Backtrace recording (always on!)"}, | 
|  | {"quiet", 'q', 0, 0, "No printing to stdio"}, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | /* In lieu of adaptively changing the period to maintain a set freq, we | 
|  | * just assume they want cycles and that the TSC is close to that. | 
|  | * | 
|  | * (cycles/sec) / (samples/sec) = cycles / sample = period. | 
|  | * | 
|  | * TODO: this also assumes we're running the core at full speed. */ | 
|  | static unsigned long freq_to_period(unsigned long freq) | 
|  | { | 
|  | return get_tsc_freq() / freq; | 
|  | } | 
|  |  | 
|  | static error_t parse_record_opt(int key, char *arg, struct argp_state *state) | 
|  | { | 
|  | struct perf_opts *p_opts = state->input; | 
|  |  | 
|  | switch (key) { | 
|  | case 'c': | 
|  | if (p_opts->record_period) | 
|  | argp_error(state, | 
|  | "Period set.  Only use at most one of -c -F"); | 
|  | p_opts->record_period = atol(arg); | 
|  | break; | 
|  | case 'F': | 
|  | if (p_opts->record_period) | 
|  | argp_error(state, | 
|  | "Period set.  Only use at most one of -c -F"); | 
|  | /* TODO: when we properly support freq, multiple events will | 
|  | * have the same freq but different, dynamic, periods. */ | 
|  | p_opts->record_period = freq_to_period(atol(arg)); | 
|  | break; | 
|  | case 'g': | 
|  | /* Our default operation is to record backtraces. */ | 
|  | break; | 
|  | case 'o': | 
|  | p_opts->outfile = xfopen(arg, "wb"); | 
|  | break; | 
|  | case 'q': | 
|  | p_opts->record_quiet = TRUE; | 
|  | break; | 
|  | case ARGP_KEY_END: | 
|  | if (!p_opts->events) | 
|  | p_opts->events = "cycles"; | 
|  | if (!p_opts->outfile) | 
|  | p_opts->outfile = xfopen("perf.data", "wb"); | 
|  | if (!p_opts->record_period) | 
|  | p_opts->record_period = freq_to_period(1000); | 
|  | break; | 
|  | default: | 
|  | return ARGP_ERR_UNKNOWN; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int perf_record(struct perf_cmd *cmd, int argc, char *argv[]) | 
|  | { | 
|  | struct argp argp_record = {record_opts, parse_record_opt}; | 
|  | struct argp_child children[] = { {&argp_record, 0, 0, 0}, {0} }; | 
|  |  | 
|  | collect_argp(cmd, argc, argv, children, &opts); | 
|  | opts.sampling = TRUE; | 
|  |  | 
|  | /* Once a perf event is submitted, it'll start counting and firing the | 
|  | * IRQ.  However, we can control whether or not the samples are | 
|  | * collected. */ | 
|  | submit_events(&opts); | 
|  | perf_start_sampling(pctx); | 
|  | run_process_and_wait(opts.cmd_argc, opts.cmd_argv, | 
|  | opts.got_cores ? &opts.cores : NULL); | 
|  | perf_stop_sampling(pctx); | 
|  | if (opts.verbose) | 
|  | perf_context_show_events(pctx, stdout); | 
|  | /* The events are still counting and firing IRQs.  Let's be nice and | 
|  | * turn them off to minimize our impact. */ | 
|  | perf_stop_events(pctx); | 
|  | /* Generate the Linux perf file format with the traces which have been | 
|  | * created during this operation. */ | 
|  | perf_convert_trace_data(cctx, perf_cfg.kpdata_file, opts.outfile); | 
|  | fclose(opts.outfile); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /**************************** perf stat  ************************/ | 
|  |  | 
|  | static struct argp_option stat_opts[] = { | 
|  | {"big-num", 'B', 0, 0, "Formatting option"}, | 
|  | {"output", 'o', "FILE", 0, "Print output to file (default stdout)"}, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | static error_t parse_stat_opt(int key, char *arg, struct argp_state *state) | 
|  | { | 
|  | struct perf_opts *p_opts = state->input; | 
|  |  | 
|  | switch (key) { | 
|  | case 'B': | 
|  | p_opts->stat_bignum = TRUE; | 
|  | break; | 
|  | case 'o': | 
|  | p_opts->outfile = xfopen(arg, "w"); | 
|  | break; | 
|  | case ARGP_KEY_END: | 
|  | if (!p_opts->events) | 
|  | p_opts->events = "cache-misses,cache-references," | 
|  | "branch-misses,branches,instructions,cycles"; | 
|  | if (!p_opts->outfile) | 
|  | p_opts->outfile = stdout; | 
|  | break; | 
|  | default: | 
|  | return ARGP_ERR_UNKNOWN; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | struct stat_val { | 
|  | char			*name; | 
|  | uint64_t		count; | 
|  | }; | 
|  |  | 
|  | /* Helper, given a name, fetches its value as a float. */ | 
|  | static float get_count_for(char *name, struct stat_val *all_vals, | 
|  | size_t nr_vals) | 
|  | { | 
|  | for (int i = 0; i < nr_vals; i++) { | 
|  | if (!strcmp(name, all_vals[i].name)) | 
|  | return (float)all_vals[i].count; | 
|  | } | 
|  | return 0.0; | 
|  | } | 
|  |  | 
|  | /* Helper, gets the seconds count as a float */ | 
|  | static float get_seconds(struct stat_val *all_vals, size_t nr_vals) | 
|  | { | 
|  | float sec = get_count_for("nsec", all_vals, nr_vals) / 1000000000; | 
|  |  | 
|  | /* We should never have a time of 0, but in case something went wrong, | 
|  | * don't hand back 0 (divide by 0 errors). */ | 
|  | return sec != 0.0 ? sec : 1.0; | 
|  | } | 
|  |  | 
|  | /* Prints "X per second", scaling for K, M, or G. */ | 
|  | static void print_default_rate(FILE *out, struct stat_val *val, | 
|  | struct stat_val *all_vals, size_t nr_vals) | 
|  | { | 
|  | float rate = val->count / get_seconds(all_vals, nr_vals); | 
|  | char scale = ' '; | 
|  |  | 
|  | if (rate > 1000000000) { | 
|  | rate /= 1000000000; | 
|  | scale = 'G'; | 
|  | } else if (rate > 1000000) { | 
|  | rate /= 1000000; | 
|  | scale = 'M'; | 
|  | } else if (rate > 1000) { | 
|  | rate /= 1000; | 
|  | scale = 'K'; | 
|  | } | 
|  | fprintf(out, "%9.3f %c/sec\n", rate, scale); | 
|  | } | 
|  |  | 
|  | /* Prints a line for the given stat val.  We pass all the vals since some stats | 
|  | * will adjust their output based on *other* known values.  e.g. IPC. */ | 
|  | static void stat_print_val(FILE *out, struct stat_val *val, | 
|  | struct stat_val *all_vals, size_t nr_vals) | 
|  | { | 
|  | /* Everyone gets the same front part of the printout */ | 
|  | fprintf(out, "%18llu      %-25s #", val->count, val->name); | 
|  |  | 
|  | /* Based on the particular event and what other events we know, we may | 
|  | * print something different to the summary bit after the #. */ | 
|  | if (!strcmp(val->name, "instructions")) { | 
|  | float cycles = get_count_for("cycles", all_vals, nr_vals); | 
|  |  | 
|  | if (cycles != 0.0) | 
|  | fprintf(out, "%9.3f insns per cycle\n", | 
|  | val->count / cycles); | 
|  | else | 
|  | print_default_rate(out, val, all_vals, nr_vals); | 
|  | } else if (!strcmp(val->name, "cache-misses")) { | 
|  | float cache_ref = get_count_for("cache-references", all_vals, | 
|  | nr_vals); | 
|  |  | 
|  | if (cache_ref != 0.0) | 
|  | fprintf(out, "%8.2f%% of all refs\n", | 
|  | val->count * 100 / cache_ref); | 
|  | else | 
|  | print_default_rate(out, val, all_vals, nr_vals); | 
|  | } else if (!strcmp(val->name, "branch-misses")) { | 
|  | float branches = get_count_for("branches", all_vals, nr_vals); | 
|  |  | 
|  | if (branches != 0.0) | 
|  | fprintf(out, "%8.2f%% of all branches\n", | 
|  | val->count * 100 / branches); | 
|  | else | 
|  | print_default_rate(out, val, all_vals, nr_vals); | 
|  | } else { | 
|  | print_default_rate(out, val, all_vals, nr_vals); | 
|  | } | 
|  | } | 
|  |  | 
|  | static char *cmd_as_str(int argc, char *const argv[]) | 
|  | { | 
|  | size_t len = 0; | 
|  | char *str; | 
|  |  | 
|  | for (int i = 0; i < argc; i++) | 
|  | len += strlen(argv[i]) + 1; | 
|  | str = xzmalloc(len); | 
|  | for (int i = 0; i < argc; i++) { | 
|  | strlcat(str, argv[i], len); | 
|  | if (i != argc - 1) | 
|  | strlcat(str, " ", len); | 
|  | } | 
|  | return str; | 
|  | } | 
|  |  | 
|  | static struct stat_val *collect_stats(struct perf_context *pctx, | 
|  | struct timespec *diff) | 
|  | { | 
|  | struct stat_val *stat_vals; | 
|  |  | 
|  | /* the last stat is time (nsec). */ | 
|  | stat_vals = xzmalloc(sizeof(struct stat_val) * (pctx->event_count + 1)); | 
|  | for (int i = 0; i < pctx->event_count; i++) { | 
|  | stat_vals[i].count = perf_get_event_count(pctx, i); | 
|  | stat_vals[i].name = pctx->events[i].sel.fq_str; | 
|  | } | 
|  | stat_vals[pctx->event_count].name = "nsec"; | 
|  | stat_vals[pctx->event_count].count = diff->tv_sec * 1000000000 + | 
|  | diff->tv_nsec; | 
|  | return stat_vals; | 
|  | } | 
|  |  | 
|  | static int perf_stat(struct perf_cmd *cmd, int argc, char *argv[]) | 
|  | { | 
|  | struct argp argp_stat = {stat_opts, parse_stat_opt}; | 
|  | struct argp_child children[] = { {&argp_stat, 0, 0, 0}, {0} }; | 
|  | FILE *out; | 
|  | struct timespec start, end, diff; | 
|  | struct stat_val *stat_vals; | 
|  | char *cmd_string; | 
|  |  | 
|  | collect_argp(cmd, argc, argv, children, &opts); | 
|  | opts.sampling = FALSE; | 
|  | out = opts.outfile; | 
|  |  | 
|  | /* As soon as we submit one event, that event is being tracked, meaning | 
|  | * that the setup/teardown of perf events is also tracked.  Each event | 
|  | * (including the clock measurement) will roughly account for either the | 
|  | * start or stop of every other event. */ | 
|  | clock_gettime(CLOCK_REALTIME, &start); | 
|  | submit_events(&opts); | 
|  | run_process_and_wait(opts.cmd_argc, opts.cmd_argv, | 
|  | opts.got_cores ? &opts.cores : NULL); | 
|  | clock_gettime(CLOCK_REALTIME, &end); | 
|  | subtract_timespecs(&diff, &end, &start); | 
|  | stat_vals = collect_stats(pctx, &diff); | 
|  | perf_stop_events(pctx); | 
|  | cmd_string = cmd_as_str(opts.cmd_argc, opts.cmd_argv); | 
|  | fprintf(out, "\nPerformance counter stats for '%s':\n\n", cmd_string); | 
|  | free(cmd_string); | 
|  | for (int i = 0; i < pctx->event_count; i++) | 
|  | stat_print_val(out, &stat_vals[i], stat_vals, | 
|  | pctx->event_count + 1); | 
|  | fprintf(out, "\n%8llu.%09llu seconds time elapsed\n\n", diff.tv_sec, | 
|  | diff.tv_nsec); | 
|  | fclose(out); | 
|  | free(stat_vals); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void run_process_and_wait(int argc, char *argv[], | 
|  | const struct core_set *cores) | 
|  | { | 
|  | int pid, status; | 
|  |  | 
|  | pid = create_child_with_stdfds(argv[0], argc, argv, environ); | 
|  | if (pid < 0) { | 
|  | perror("Unable to spawn child"); | 
|  | fflush(stderr); | 
|  | exit(1); | 
|  | } | 
|  | if (cores) { | 
|  | if (provision_core_set(pid, cores)) { | 
|  | fprintf(stderr, | 
|  | "Unable to provision all cores to PID %d: cmd='%s'\n", | 
|  | pid, argv[0]); | 
|  | sys_proc_destroy(pid, -1); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  | sys_proc_run(pid); | 
|  | waitpid(pid, &status, 0); | 
|  | } | 
|  |  | 
|  | static void save_cmdline(int argc, char *argv[]) | 
|  | { | 
|  | size_t len = 0; | 
|  | char *p; | 
|  |  | 
|  | for (int i = 0; i < argc; i++) | 
|  | len += strlen(argv[i]) + 1; | 
|  | cmd_line_save = xmalloc(len); | 
|  | p = cmd_line_save; | 
|  | for (int i = 0; i < argc; i++) { | 
|  | strcpy(p, argv[i]); | 
|  | p += strlen(argv[i]); | 
|  | if (!(i == argc - 1)) { | 
|  | *p = ' ';	/* overwrite \0 with ' ' */ | 
|  | p++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void global_usage(void) | 
|  | { | 
|  | fprintf(stderr, "  Usage: perf COMMAND [ARGS]\n"); | 
|  | fprintf(stderr, "\n  Available commands:\n\n"); | 
|  | for (int i = 0; i < COUNT_OF(perf_cmds); i++) | 
|  | fprintf(stderr, "  \t%s: %s\n", perf_cmds[i].name, | 
|  | perf_cmds[i].desc); | 
|  | exit(-1); | 
|  | } | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | int i, ret = -1; | 
|  |  | 
|  | save_cmdline(argc, argv); | 
|  |  | 
|  | /* Common inits.  Some functions don't need these, but it doesn't hurt. | 
|  | */ | 
|  | perf_initialize(); | 
|  | pctx = perf_create_context(&perf_cfg); | 
|  | cctx = perfconv_create_context(pctx); | 
|  |  | 
|  | if (argc < 2) | 
|  | global_usage(); | 
|  | for (i = 0; i < COUNT_OF(perf_cmds); i++) { | 
|  | if (!strcmp(perf_cmds[i].name, argv[1])) { | 
|  | ret = perf_cmds[i].func(&perf_cmds[i], argc - 1, | 
|  | argv + 1); | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (i == COUNT_OF(perf_cmds)) | 
|  | global_usage(); | 
|  | /* This cleanup is optional - they'll all be dealt with when the program | 
|  | * exits.  This means its safe for us to exit(-1) at any point in the | 
|  | * program. */ | 
|  | perf_free_context(pctx); | 
|  | perfconv_free_context(cctx); | 
|  | perf_finalize(); | 
|  | return ret; | 
|  | } |