diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
-rw-r--r-- | tools/perf/builtin-stat.c | 399 |
1 files changed, 210 insertions, 189 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 35f79b48e8dc..661832756a24 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -164,6 +164,35 @@ static struct perf_stat_config stat_config = { .iostat_run = false, }; +/* Options set from the command line. */ +struct opt_aggr_mode { + bool node, socket, die, cluster, cache, core, thread, no_aggr; +}; + +/* Turn command line option into most generic aggregation mode setting. */ +static enum aggr_mode opt_aggr_mode_to_aggr_mode(struct opt_aggr_mode *opt_mode) +{ + enum aggr_mode mode = AGGR_GLOBAL; + + if (opt_mode->node) + mode = AGGR_NODE; + if (opt_mode->socket) + mode = AGGR_SOCKET; + if (opt_mode->die) + mode = AGGR_DIE; + if (opt_mode->cluster) + mode = AGGR_CLUSTER; + if (opt_mode->cache) + mode = AGGR_CACHE; + if (opt_mode->core) + mode = AGGR_CORE; + if (opt_mode->thread) + mode = AGGR_THREAD; + if (opt_mode->no_aggr) + mode = AGGR_NONE; + return mode; +} + static void evlist__check_cpu_maps(struct evlist *evlist) { struct evsel *evsel, *warned_leader = NULL; @@ -255,45 +284,38 @@ static int evsel__write_stat_event(struct evsel *counter, int cpu_map_idx, u32 t process_synthesized_event, NULL); } -static int read_single_counter(struct evsel *counter, int cpu_map_idx, - int thread, struct timespec *rs) +static int read_single_counter(struct evsel *counter, int cpu_map_idx, int thread) { - switch(counter->tool_event) { - case PERF_TOOL_DURATION_TIME: { - u64 val = rs->tv_nsec + rs->tv_sec*1000000000ULL; - struct perf_counts_values *count = - perf_counts(counter->counts, cpu_map_idx, thread); - count->ena = count->run = val; - count->val = val; - return 0; - } - case PERF_TOOL_USER_TIME: - case PERF_TOOL_SYSTEM_TIME: { - u64 val; - struct perf_counts_values *count = - perf_counts(counter->counts, cpu_map_idx, thread); - if (counter->tool_event == PERF_TOOL_USER_TIME) - val = ru_stats.ru_utime_usec_stat.mean; - else - val = ru_stats.ru_stime_usec_stat.mean; - count->ena = count->run = val; - count->val = val; - return 0; - } - default: - case PERF_TOOL_NONE: - return evsel__read_counter(counter, cpu_map_idx, thread); - case PERF_TOOL_MAX: - /* This should never be reached */ - return 0; + int err = evsel__read_counter(counter, cpu_map_idx, thread); + + /* + * Reading user and system time will fail when the process + * terminates. Use the wait4 values in that case. + */ + if (err && cpu_map_idx == 0 && + (counter->tool_event == PERF_TOOL_USER_TIME || + counter->tool_event == PERF_TOOL_SYSTEM_TIME)) { + u64 val, *start_time; + struct perf_counts_values *count = + perf_counts(counter->counts, cpu_map_idx, thread); + + start_time = xyarray__entry(counter->start_times, cpu_map_idx, thread); + if (counter->tool_event == PERF_TOOL_USER_TIME) + val = ru_stats.ru_utime_usec_stat.mean; + else + val = ru_stats.ru_stime_usec_stat.mean; + count->ena = count->run = *start_time + val; + count->val = val; + return 0; } + return err; } /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode */ -static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu_map_idx) +static int read_counter_cpu(struct evsel *counter, int cpu_map_idx) { int nthreads = perf_thread_map__nr(evsel_list->core.threads); int thread; @@ -311,7 +333,7 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu_ * (via evsel__read_counter()) and sets their count->loaded. */ if (!perf_counts__is_loaded(counter->counts, cpu_map_idx, thread) && - read_single_counter(counter, cpu_map_idx, thread, rs)) { + read_single_counter(counter, cpu_map_idx, thread)) { counter->counts->scaled = -1; perf_counts(counter->counts, cpu_map_idx, thread)->ena = 0; perf_counts(counter->counts, cpu_map_idx, thread)->run = 0; @@ -340,7 +362,7 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu_ return 0; } -static int read_affinity_counters(struct timespec *rs) +static int read_affinity_counters(void) { struct evlist_cpu_iterator evlist_cpu_itr; struct affinity saved_affinity, *affinity; @@ -361,10 +383,8 @@ static int read_affinity_counters(struct timespec *rs) if (evsel__is_bpf(counter)) continue; - if (!counter->err) { - counter->err = read_counter_cpu(counter, rs, - evlist_cpu_itr.cpu_map_idx); - } + if (!counter->err) + counter->err = read_counter_cpu(counter, evlist_cpu_itr.cpu_map_idx); } if (affinity) affinity__cleanup(&saved_affinity); @@ -388,11 +408,11 @@ static int read_bpf_map_counters(void) return 0; } -static int read_counters(struct timespec *rs) +static int read_counters(void) { if (!stat_config.stop_read_counter) { if (read_bpf_map_counters() || - read_affinity_counters(rs)) + read_affinity_counters()) return -1; } return 0; @@ -423,7 +443,7 @@ static void process_interval(void) evlist__reset_aggr_stats(evsel_list); - if (read_counters(&rs) == 0) + if (read_counters() == 0) process_counters(); if (STAT_RECORD) { @@ -911,7 +931,7 @@ try_again_reset: * avoid arbitrary skew, we must read all counters before closing any * group leaders. */ - if (read_counters(&(struct timespec) { .tv_nsec = t1-t0 }) == 0) + if (read_counters() == 0) process_counters(); /* @@ -1096,7 +1116,7 @@ static int parse_cache_level(const struct option *opt, int unset __maybe_unused) { int level; - u32 *aggr_mode = (u32 *)opt->value; + struct opt_aggr_mode *opt_aggr_mode = (struct opt_aggr_mode *)opt->value; u32 *aggr_level = (u32 *)opt->data; /* @@ -1135,155 +1155,11 @@ static int parse_cache_level(const struct option *opt, return -EINVAL; } out: - *aggr_mode = AGGR_CACHE; + opt_aggr_mode->cache = true; *aggr_level = level; return 0; } -static struct option stat_options[] = { - OPT_BOOLEAN('T', "transaction", &transaction_run, - "hardware transaction statistics"), - OPT_CALLBACK('e', "event", &parse_events_option_args, "event", - "event selector. use 'perf list' to list available events", - parse_events_option), - OPT_CALLBACK(0, "filter", &evsel_list, "filter", - "event filter", parse_filter), - OPT_BOOLEAN('i', "no-inherit", &stat_config.no_inherit, - "child tasks do not inherit counters"), - OPT_STRING('p', "pid", &target.pid, "pid", - "stat events on existing process id"), - OPT_STRING('t', "tid", &target.tid, "tid", - "stat events on existing thread id"), -#ifdef HAVE_BPF_SKEL - OPT_STRING('b', "bpf-prog", &target.bpf_str, "bpf-prog-id", - "stat events on existing bpf program id"), - OPT_BOOLEAN(0, "bpf-counters", &target.use_bpf, - "use bpf program to count events"), - OPT_STRING(0, "bpf-attr-map", &target.attr_map, "attr-map-path", - "path to perf_event_attr map"), -#endif - OPT_BOOLEAN('a', "all-cpus", &target.system_wide, - "system-wide collection from all CPUs"), - OPT_BOOLEAN(0, "scale", &stat_config.scale, - "Use --no-scale to disable counter scaling for multiplexing"), - OPT_INCR('v', "verbose", &verbose, - "be more verbose (show counter open errors, etc)"), - OPT_INTEGER('r', "repeat", &stat_config.run_count, - "repeat command and print average + stddev (max: 100, forever: 0)"), - OPT_BOOLEAN(0, "table", &stat_config.walltime_run_table, - "display details about each run (only with -r option)"), - OPT_BOOLEAN('n', "null", &stat_config.null_run, - "null run - dont start any counters"), - OPT_INCR('d', "detailed", &detailed_run, - "detailed run - start a lot of events"), - OPT_BOOLEAN('S', "sync", &sync_run, - "call sync() before starting a run"), - OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, - "print large numbers with thousands\' separators", - stat__set_big_num), - OPT_STRING('C', "cpu", &target.cpu_list, "cpu", - "list of cpus to monitor in system-wide"), - OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, - "disable aggregation across CPUs or PMUs", AGGR_NONE), - OPT_SET_UINT(0, "no-merge", &stat_config.aggr_mode, - "disable aggregation the same as -A or -no-aggr", AGGR_NONE), - OPT_BOOLEAN(0, "hybrid-merge", &stat_config.hybrid_merge, - "Merge identical named hybrid events"), - OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", - "print counts with custom separator"), - OPT_BOOLEAN('j', "json-output", &stat_config.json_output, - "print counts in JSON format"), - OPT_CALLBACK('G', "cgroup", &evsel_list, "name", - "monitor event in cgroup name only", parse_stat_cgroups), - OPT_STRING(0, "for-each-cgroup", &stat_config.cgroup_list, "name", - "expand events for each cgroup"), - OPT_STRING('o', "output", &output_name, "file", "output file name"), - OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), - OPT_INTEGER(0, "log-fd", &output_fd, - "log output to fd, instead of stderr"), - OPT_STRING(0, "pre", &pre_cmd, "command", - "command to run prior to the measured command"), - OPT_STRING(0, "post", &post_cmd, "command", - "command to run after to the measured command"), - OPT_UINTEGER('I', "interval-print", &stat_config.interval, - "print counts at regular interval in ms " - "(overhead is possible for values <= 100ms)"), - OPT_INTEGER(0, "interval-count", &stat_config.times, - "print counts for fixed number of times"), - OPT_BOOLEAN(0, "interval-clear", &stat_config.interval_clear, - "clear screen in between new interval"), - OPT_UINTEGER(0, "timeout", &stat_config.timeout, - "stop workload and print counts after a timeout period in ms (>= 10ms)"), - OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, - "aggregate counts per processor socket", AGGR_SOCKET), - OPT_SET_UINT(0, "per-die", &stat_config.aggr_mode, - "aggregate counts per processor die", AGGR_DIE), - OPT_SET_UINT(0, "per-cluster", &stat_config.aggr_mode, - "aggregate counts per processor cluster", AGGR_CLUSTER), - OPT_CALLBACK_OPTARG(0, "per-cache", &stat_config.aggr_mode, &stat_config.aggr_level, - "cache level", "aggregate count at this cache level (Default: LLC)", - parse_cache_level), - OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode, - "aggregate counts per physical processor core", AGGR_CORE), - OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, - "aggregate counts per thread", AGGR_THREAD), - OPT_SET_UINT(0, "per-node", &stat_config.aggr_mode, - "aggregate counts per numa node", AGGR_NODE), - OPT_INTEGER('D', "delay", &target.initial_delay, - "ms to wait before starting measurement after program start (-1: start with events disabled)"), - OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL, - "Only print computed metrics. No raw values", enable_metric_only), - OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group, - "don't group metric events, impacts multiplexing"), - OPT_BOOLEAN(0, "metric-no-merge", &stat_config.metric_no_merge, - "don't try to share events between metrics in a group"), - OPT_BOOLEAN(0, "metric-no-threshold", &stat_config.metric_no_threshold, - "disable adding events for the metric threshold calculation"), - OPT_BOOLEAN(0, "topdown", &topdown_run, - "measure top-down statistics"), - OPT_UINTEGER(0, "td-level", &stat_config.topdown_level, - "Set the metrics level for the top-down statistics (0: max level)"), - OPT_BOOLEAN(0, "smi-cost", &smi_cost, - "measure SMI cost"), - OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", - "monitor specified metrics or metric groups (separated by ,)", - append_metric_groups), - OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel, - "Configure all used events to run in kernel space.", - PARSE_OPT_EXCLUSIVE), - OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user, - "Configure all used events to run in user space.", - PARSE_OPT_EXCLUSIVE), - OPT_BOOLEAN(0, "percore-show-thread", &stat_config.percore_show_thread, - "Use with 'percore' event qualifier to show the event " - "counts of one hardware thread by sum up total hardware " - "threads of same physical core"), - OPT_BOOLEAN(0, "summary", &stat_config.summary, - "print summary for interval mode"), - OPT_BOOLEAN(0, "no-csv-summary", &stat_config.no_csv_summary, - "don't print 'summary' for CSV summary output"), - OPT_BOOLEAN(0, "quiet", &quiet, - "don't print any output, messages or warnings (useful with record)"), - OPT_CALLBACK(0, "cputype", &evsel_list, "hybrid cpu type", - "Only enable events on applying cpu with this type " - "for hybrid platform (e.g. core or atom)", - parse_cputype), -#ifdef HAVE_LIBPFM - OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", - "libpfm4 event selector. use 'perf list' to list available events", - parse_libpfm_events_option), -#endif - OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd] or fifo:ctl-fifo[,ack-fifo]", - "Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n" - "\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n" - "\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.", - parse_control_option), - OPT_CALLBACK_OPTARG(0, "iostat", &evsel_list, &stat_config, "default", - "measure I/O performance metrics provided by arch/platform", - iostat_parse), - OPT_END() -}; - /** * Calculate the cache instance ID from the map in * /sys/devices/system/cpu/cpuX/cache/indexY/shared_cpu_list @@ -2245,13 +2121,15 @@ static void init_features(struct perf_session *session) perf_header__clear_feat(&session->header, HEADER_AUXTRACE); } -static int __cmd_record(int argc, const char **argv) +static int __cmd_record(const struct option stat_options[], struct opt_aggr_mode *opt_mode, + int argc, const char **argv) { struct perf_session *session; struct perf_data *data = &perf_stat.data; argc = parse_options(argc, argv, stat_options, stat_record_usage, PARSE_OPT_STOP_AT_NON_OPTION); + stat_config.aggr_mode = opt_aggr_mode_to_aggr_mode(opt_mode); if (output_name) data->path = output_name; @@ -2494,6 +2372,147 @@ static void setup_system_wide(int forks) int cmd_stat(int argc, const char **argv) { + struct opt_aggr_mode opt_mode = {}; + struct option stat_options[] = { + OPT_BOOLEAN('T', "transaction", &transaction_run, + "hardware transaction statistics"), + OPT_CALLBACK('e', "event", &parse_events_option_args, "event", + "event selector. use 'perf list' to list available events", + parse_events_option), + OPT_CALLBACK(0, "filter", &evsel_list, "filter", + "event filter", parse_filter), + OPT_BOOLEAN('i', "no-inherit", &stat_config.no_inherit, + "child tasks do not inherit counters"), + OPT_STRING('p', "pid", &target.pid, "pid", + "stat events on existing process id"), + OPT_STRING('t', "tid", &target.tid, "tid", + "stat events on existing thread id"), +#ifdef HAVE_BPF_SKEL + OPT_STRING('b', "bpf-prog", &target.bpf_str, "bpf-prog-id", + "stat events on existing bpf program id"), + OPT_BOOLEAN(0, "bpf-counters", &target.use_bpf, + "use bpf program to count events"), + OPT_STRING(0, "bpf-attr-map", &target.attr_map, "attr-map-path", + "path to perf_event_attr map"), +#endif + OPT_BOOLEAN('a', "all-cpus", &target.system_wide, + "system-wide collection from all CPUs"), + OPT_BOOLEAN(0, "scale", &stat_config.scale, + "Use --no-scale to disable counter scaling for multiplexing"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose (show counter open errors, etc)"), + OPT_INTEGER('r', "repeat", &stat_config.run_count, + "repeat command and print average + stddev (max: 100, forever: 0)"), + OPT_BOOLEAN(0, "table", &stat_config.walltime_run_table, + "display details about each run (only with -r option)"), + OPT_BOOLEAN('n', "null", &stat_config.null_run, + "null run - dont start any counters"), + OPT_INCR('d', "detailed", &detailed_run, + "detailed run - start a lot of events"), + OPT_BOOLEAN('S', "sync", &sync_run, + "call sync() before starting a run"), + OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, + "print large numbers with thousands\' separators", + stat__set_big_num), + OPT_STRING('C', "cpu", &target.cpu_list, "cpu", + "list of cpus to monitor in system-wide"), + OPT_BOOLEAN('A', "no-aggr", &opt_mode.no_aggr, + "disable aggregation across CPUs or PMUs"), + OPT_BOOLEAN(0, "no-merge", &opt_mode.no_aggr, + "disable aggregation the same as -A or -no-aggr"), + OPT_BOOLEAN(0, "hybrid-merge", &stat_config.hybrid_merge, + "Merge identical named hybrid events"), + OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", + "print counts with custom separator"), + OPT_BOOLEAN('j', "json-output", &stat_config.json_output, + "print counts in JSON format"), + OPT_CALLBACK('G', "cgroup", &evsel_list, "name", + "monitor event in cgroup name only", parse_stat_cgroups), + OPT_STRING(0, "for-each-cgroup", &stat_config.cgroup_list, "name", + "expand events for each cgroup"), + OPT_STRING('o', "output", &output_name, "file", "output file name"), + OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), + OPT_INTEGER(0, "log-fd", &output_fd, + "log output to fd, instead of stderr"), + OPT_STRING(0, "pre", &pre_cmd, "command", + "command to run prior to the measured command"), + OPT_STRING(0, "post", &post_cmd, "command", + "command to run after to the measured command"), + OPT_UINTEGER('I', "interval-print", &stat_config.interval, + "print counts at regular interval in ms " + "(overhead is possible for values <= 100ms)"), + OPT_INTEGER(0, "interval-count", &stat_config.times, + "print counts for fixed number of times"), + OPT_BOOLEAN(0, "interval-clear", &stat_config.interval_clear, + "clear screen in between new interval"), + OPT_UINTEGER(0, "timeout", &stat_config.timeout, + "stop workload and print counts after a timeout period in ms (>= 10ms)"), + OPT_BOOLEAN(0, "per-socket", &opt_mode.socket, + "aggregate counts per processor socket"), + OPT_BOOLEAN(0, "per-die", &opt_mode.die, "aggregate counts per processor die"), + OPT_BOOLEAN(0, "per-cluster", &opt_mode.cluster, + "aggregate counts per processor cluster"), + OPT_CALLBACK_OPTARG(0, "per-cache", &opt_mode, &stat_config.aggr_level, + "cache level", "aggregate count at this cache level (Default: LLC)", + parse_cache_level), + OPT_BOOLEAN(0, "per-core", &opt_mode.core, + "aggregate counts per physical processor core"), + OPT_BOOLEAN(0, "per-thread", &opt_mode.thread, "aggregate counts per thread"), + OPT_BOOLEAN(0, "per-node", &opt_mode.node, "aggregate counts per numa node"), + OPT_INTEGER('D', "delay", &target.initial_delay, + "ms to wait before starting measurement after program start (-1: start with events disabled)"), + OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL, + "Only print computed metrics. No raw values", enable_metric_only), + OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group, + "don't group metric events, impacts multiplexing"), + OPT_BOOLEAN(0, "metric-no-merge", &stat_config.metric_no_merge, + "don't try to share events between metrics in a group"), + OPT_BOOLEAN(0, "metric-no-threshold", &stat_config.metric_no_threshold, + "disable adding events for the metric threshold calculation"), + OPT_BOOLEAN(0, "topdown", &topdown_run, + "measure top-down statistics"), + OPT_UINTEGER(0, "td-level", &stat_config.topdown_level, + "Set the metrics level for the top-down statistics (0: max level)"), + OPT_BOOLEAN(0, "smi-cost", &smi_cost, + "measure SMI cost"), + OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", + "monitor specified metrics or metric groups (separated by ,)", + append_metric_groups), + OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel, + "Configure all used events to run in kernel space.", + PARSE_OPT_EXCLUSIVE), + OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user, + "Configure all used events to run in user space.", + PARSE_OPT_EXCLUSIVE), + OPT_BOOLEAN(0, "percore-show-thread", &stat_config.percore_show_thread, + "Use with 'percore' event qualifier to show the event " + "counts of one hardware thread by sum up total hardware " + "threads of same physical core"), + OPT_BOOLEAN(0, "summary", &stat_config.summary, + "print summary for interval mode"), + OPT_BOOLEAN(0, "no-csv-summary", &stat_config.no_csv_summary, + "don't print 'summary' for CSV summary output"), + OPT_BOOLEAN(0, "quiet", &quiet, + "don't print any output, messages or warnings (useful with record)"), + OPT_CALLBACK(0, "cputype", &evsel_list, "hybrid cpu type", + "Only enable events on applying cpu with this type " + "for hybrid platform (e.g. core or atom)", + parse_cputype), +#ifdef HAVE_LIBPFM + OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", + "libpfm4 event selector. use 'perf list' to list available events", + parse_libpfm_events_option), +#endif + OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd] or fifo:ctl-fifo[,ack-fifo]", + "Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n" + "\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n" + "\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.", + parse_control_option), + OPT_CALLBACK_OPTARG(0, "iostat", &evsel_list, &stat_config, "default", + "measure I/O performance metrics provided by arch/platform", + iostat_parse), + OPT_END() + }; const char * const stat_usage[] = { "perf stat [<options>] [<command>]", NULL @@ -2522,6 +2541,8 @@ int cmd_stat(int argc, const char **argv) (const char **) stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); + stat_config.aggr_mode = opt_aggr_mode_to_aggr_mode(&opt_mode); + if (stat_config.csv_sep) { stat_config.csv_output = true; if (!strcmp(stat_config.csv_sep, "\\t")) @@ -2530,7 +2551,7 @@ int cmd_stat(int argc, const char **argv) stat_config.csv_sep = DEFAULT_SEPARATOR; if (argc && strlen(argv[0]) > 2 && strstarts("record", argv[0])) { - argc = __cmd_record(argc, argv); + argc = __cmd_record(stat_options, &opt_mode, argc, argv); if (argc < 0) return -1; } else if (argc && strlen(argv[0]) > 2 && strstarts("report", argv[0])) |