diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 217c82ee3665..900fca01bdd3 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -4099,6 +4099,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event unsigned long long val; struct func_map *func; const char *saveptr; + struct trace_seq p; char *bprint_fmt = NULL; char format[32]; int show_func; @@ -4306,8 +4307,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event format[len] = 0; if (!len_as_arg) len_arg = -1; - print_str_arg(s, data, size, event, + /* Use helper trace_seq */ + trace_seq_init(&p); + print_str_arg(&p, data, size, event, format, len_arg, arg); + trace_seq_terminate(&p); + trace_seq_puts(s, p.buffer); arg = arg->next; break; default: diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 43b42c4f4a91..c407897f0435 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -57,6 +57,8 @@ OPTIONS -t:: --tid=:: Record events on existing thread ID (comma separated list). + This option also disables inheritance by default. Enable it by adding + --inherit. -u:: --uid=:: @@ -201,11 +203,11 @@ abort events and some memory events in precise mode on modern Intel CPUs. --transaction:: Record transaction flags for transaction related events. ---force-per-cpu:: -Force the use of per-cpu mmaps. By default, when tasks are specified (i.e. -p, --t or -u options) per-thread mmaps are created. This option overrides that and -forces per-cpu mmaps. A side-effect of that is that inheritance is -automatically enabled. Add the -i option also to disable inheritance. +--per-thread:: +Use per-thread mmaps. By default per-cpu mmaps are created. This option +overrides that and uses per-thread mmaps. A side-effect of that is that +inheritance is automatically disabled. --per-thread is ignored with a warning +if combined with -a or -C options. SEE ALSO -------- diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index e9cbfcddfa3f..cfdbb1e045b5 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -203,6 +203,12 @@ OPTIONS --show-kernel-path:: Try to resolve the path of [kernel.kallsyms] +--show-task-events + Display task related events (e.g. FORK, COMM, EXIT). + +--show-mmap-events + Display mmap related events (e.g. MMAP, MMAP2). + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script-perl[1], diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 3ff8bd4f0b4d..271dd4ed5b05 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt @@ -8,8 +8,7 @@ perf-timechart - Tool to visualize total system behavior during a workload SYNOPSIS -------- [verse] -'perf timechart' record -'perf timechart' [] +'perf timechart' [] {record} [] DESCRIPTION ----------- @@ -21,8 +20,8 @@ There are two variants of perf timechart: 'perf timechart' to turn a trace into a Scalable Vector Graphics file, that can be viewed with popular SVG viewers such as 'Inkscape'. -OPTIONS -------- +TIMECHART OPTIONS +----------------- -o:: --output=:: Select the output file (default: output.svg) @@ -35,6 +34,9 @@ OPTIONS -P:: --power-only:: Only output the CPU power section of the diagram +-T:: +--tasks-only:: + Don't output processor state transitions -p:: --process:: Select the processes to display, by name or PID @@ -54,6 +56,22 @@ $ perf timechart Written 10.2 seconds of trace to output.svg. +-n:: +--proc-num:: + Print task info for at least given number of tasks. + +RECORD OPTIONS +-------------- +-P:: +--power-only:: + Record only power-related events +-T:: +--tasks-only:: + Record only tasks-related events +-g:: +--callchain:: + Do call-graph (stack chain/backtrace) recording + SEE ALSO -------- linkperf:perf-record[1] diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 7de01dd79688..cdd8d4946dba 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -50,7 +50,6 @@ Default is to monitor all CPUS. --count-filter=:: Only display functions with more events than this. --g:: --group:: Put the counters into a counter group. @@ -143,12 +142,12 @@ Default is to monitor all CPUS. --asm-raw:: Show raw instruction encoding of assembly instructions. --G:: +-g:: Enables call-graph (stack chain/backtrace) recording. --call-graph:: Setup and enable call-graph (stack chain/backtrace) recording, - implies -G. + implies -g. --max-stack:: Set the stack depth limit when parsing the callchain, anything diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 4835618a5608..eefb9fb0c02f 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -60,8 +60,11 @@ endef # # Needed if no target specified: +# (Except for tags and TAGS targets. The reason is that the +# Makefile does not treat tags/TAGS as targets but as files +# and thus won't rebuilt them once they are in place.) # -all: +all tags TAGS: $(print_msg) $(make) @@ -77,3 +80,5 @@ clean: %: $(print_msg) $(make) + +.PHONY: tags TAGS diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 7fc8f179cae7..e416ccc7d831 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -840,9 +840,9 @@ ifndef NO_LIBPYTHON $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \ $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' endif - $(call QUIET_INSTALL, bash_completion-script) \ + $(call QUIET_INSTALL, perf_completion-script) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \ - $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' + $(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' $(call QUIET_INSTALL, tests) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 7c8020a32784..65615a8bc25e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -800,6 +800,7 @@ static struct perf_record record = { .freq = 4000, .target = { .uses_mmap = true, + .default_per_cpu = true, }, }, }; @@ -842,8 +843,9 @@ const struct option record_options[] = { OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), OPT_STRING('o', "output", &record.file.path, "file", "output file name"), - OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, - "child tasks do not inherit counters"), + OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit, + &record.opts.no_inherit_set, + "child tasks do not inherit counters"), OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", "number of mmap data pages", @@ -888,8 +890,8 @@ const struct option record_options[] = { "sample by weight (on special events only)"), OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, "sample transaction flags (special events only)"), - OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu, - "force the use of per-cpu mmaps"), + OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, + "use per-thread mmaps"), OPT_END() }; @@ -938,6 +940,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) goto out_symbol_exit; } + if (rec->opts.target.tid && !rec->opts.no_inherit_set) + rec->opts.no_inherit = true; + err = target__validate(&rec->opts.target); if (err) { target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index baf17989a216..952dce979252 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -280,6 +280,30 @@ static int perf_session__check_output_opt(struct perf_session *session) set_print_ip_opts(&evsel->attr); } + /* + * set default for tracepoints to print symbols only + * if callchains are present + */ + if (symbol_conf.use_callchain && + !output[PERF_TYPE_TRACEPOINT].user_set) { + struct perf_event_attr *attr; + + j = PERF_TYPE_TRACEPOINT; + evsel = perf_session__find_first_evtype(session, j); + if (evsel == NULL) + goto out; + + attr = &evsel->attr; + + if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) { + output[j].fields |= PERF_OUTPUT_IP; + output[j].fields |= PERF_OUTPUT_SYM; + output[j].fields |= PERF_OUTPUT_DSO; + set_print_ip_opts(attr); + } + } + +out: return 0; } @@ -288,7 +312,6 @@ static void print_sample_start(struct perf_sample *sample, struct perf_evsel *evsel) { struct perf_event_attr *attr = &evsel->attr; - const char *evname = NULL; unsigned long secs; unsigned long usecs; unsigned long long nsecs; @@ -323,11 +346,6 @@ static void print_sample_start(struct perf_sample *sample, usecs = nsecs / NSECS_PER_USEC; printf("%5lu.%06lu: ", secs, usecs); } - - if (PRINT_FIELD(EVNAME)) { - evname = perf_evsel__name(evsel); - printf("%s: ", evname ? evname : "[unknown]"); - } } static bool is_bts_event(struct perf_event_attr *attr) @@ -434,6 +452,11 @@ static void process_event(union perf_event *event, struct perf_sample *sample, print_sample_start(sample, thread, evsel); + if (PRINT_FIELD(EVNAME)) { + const char *evname = perf_evsel__name(evsel); + printf("%s: ", evname ? evname : "[unknown]"); + } + if (is_bts_event(attr)) { print_sample_bts(event, sample, evsel, machine, thread); return; @@ -549,6 +572,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, struct perf_script { struct perf_tool tool; struct perf_session *session; + bool show_task_events; + bool show_mmap_events; }; static int process_attr(struct perf_tool *tool, union perf_event *event, @@ -579,6 +604,163 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, return perf_evsel__check_attr(evsel, scr->session); } +static int process_comm_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + int ret = -1; + + thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); + if (thread == NULL) { + pr_debug("problem processing COMM event, skipping it.\n"); + return -1; + } + + if (perf_event__process_comm(tool, event, sample, machine) < 0) + goto out; + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->comm.tid; + sample->pid = event->comm.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + ret = 0; + +out: + return ret; +} + +static int process_fork_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + if (perf_event__process_fork(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); + if (thread == NULL) { + pr_debug("problem processing FORK event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = event->fork.time; + sample->tid = event->fork.tid; + sample->pid = event->fork.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + return 0; +} +static int process_exit_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); + if (thread == NULL) { + pr_debug("problem processing EXIT event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->comm.tid; + sample->pid = event->comm.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + if (perf_event__process_exit(tool, event, sample, machine) < 0) + return -1; + + return 0; +} + +static int process_mmap_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + if (perf_event__process_mmap(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid); + if (thread == NULL) { + pr_debug("problem processing MMAP event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->mmap.tid; + sample->pid = event->mmap.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + return 0; +} + +static int process_mmap2_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__first(session->evlist); + + if (perf_event__process_mmap2(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid); + if (thread == NULL) { + pr_debug("problem processing MMAP2 event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + sample->cpu = 0; + sample->time = 0; + sample->tid = event->mmap2.tid; + sample->pid = event->mmap2.pid; + } + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + + return 0; +} + static void sig_handler(int sig __maybe_unused) { session_done = 1; @@ -590,6 +772,17 @@ static int __cmd_script(struct perf_script *script) signal(SIGINT, sig_handler); + /* override event processing functions */ + if (script->show_task_events) { + script->tool.comm = process_comm_event; + script->tool.fork = process_fork_event; + script->tool.exit = process_exit_event; + } + if (script->show_mmap_events) { + script->tool.mmap = process_mmap_event; + script->tool.mmap2 = process_mmap2_event; + } + ret = perf_session__process_events(script->session, &script->tool); if (debug_mode) @@ -1352,6 +1545,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) "display extended information from perf.data file"), OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, "Show the path of [kernel.kallsyms]"), + OPT_BOOLEAN('\0', "show-task-events", &script.show_task_events, + "Show the fork/comm/exit events"), + OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events, + "Show the mmap events"), OPT_END() }; const char * const script_usage[] = { diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 41c9bde2fb67..680632d7e26a 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -41,6 +41,7 @@ #define SUPPORT_OLD_POWER_EVENTS 1 #define PWR_EVENT_EXIT -1 +static int proc_num = 15; static unsigned int numcpus; static u64 min_freq; /* Lowest CPU frequency seen */ @@ -50,16 +51,12 @@ static u64 turbo_frequency; static u64 first_time, last_time; static bool power_only; +static bool tasks_only; +static bool with_backtrace; -struct per_pid; struct per_pidcomm; - struct cpu_sample; -struct power_event; -struct wake_event; - -struct sample_wrapper; /* * Datastructure layout: @@ -124,6 +121,7 @@ struct cpu_sample { u64 end_time; int type; int cpu; + const char *backtrace; }; static struct per_pid *all_data; @@ -145,12 +143,12 @@ struct wake_event { int waker; int wakee; u64 time; + const char *backtrace; }; static struct power_event *power_events; static struct wake_event *wake_events; -struct process_filter; struct process_filter { char *name; int pid; @@ -229,7 +227,8 @@ static void pid_exit(int pid, u64 timestamp) } static void -pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) +pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end, + const char *backtrace) { struct per_pid *p; struct per_pidcomm *c; @@ -252,6 +251,7 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) sample->type = type; sample->next = c->samples; sample->cpu = cpu; + sample->backtrace = backtrace; c->samples = sample; if (sample->type == TYPE_RUNNING && end > start && start > 0) { @@ -299,50 +299,10 @@ static int process_exit_event(struct perf_tool *tool __maybe_unused, return 0; } -struct trace_entry { - unsigned short type; - unsigned char flags; - unsigned char preempt_count; - int pid; - int lock_depth; -}; - #ifdef SUPPORT_OLD_POWER_EVENTS static int use_old_power_events; -struct power_entry_old { - struct trace_entry te; - u64 type; - u64 value; - u64 cpu_id; -}; #endif -struct power_processor_entry { - struct trace_entry te; - u32 state; - u32 cpu_id; -}; - -#define TASK_COMM_LEN 16 -struct wakeup_entry { - struct trace_entry te; - char comm[TASK_COMM_LEN]; - int pid; - int prio; - int success; -}; - -struct sched_switch { - struct trace_entry te; - char prev_comm[TASK_COMM_LEN]; - int prev_pid; - int prev_prio; - long prev_state; /* Arjan weeps. */ - char next_comm[TASK_COMM_LEN]; - int next_pid; - int next_prio; -}; - static void c_state_start(int cpu, u64 timestamp, int state) { cpus_cstate_start_times[cpu] = timestamp; @@ -402,23 +362,23 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq) turbo_frequency = max_freq; } -static void -sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te) +static void sched_wakeup(int cpu, u64 timestamp, int waker, int wakee, + u8 flags, const char *backtrace) { struct per_pid *p; - struct wakeup_entry *wake = (void *)te; struct wake_event *we = zalloc(sizeof(*we)); if (!we) return; we->time = timestamp; - we->waker = pid; + we->waker = waker; + we->backtrace = backtrace; - if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ)) + if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) we->waker = -1; - we->wakee = wake->pid; + we->wakee = wakee; we->next = wake_events; wake_events = we; p = find_create_pid(we->wakee); @@ -428,27 +388,31 @@ sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te) p->current->state = TYPE_WAITING; } if (p && p->current && p->current->state == TYPE_BLOCKED) { - pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp); + pid_put_sample(p->pid, p->current->state, cpu, + p->current->state_since, timestamp, NULL); p->current->state_since = timestamp; p->current->state = TYPE_WAITING; } } -static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) +static void sched_switch(int cpu, u64 timestamp, int prev_pid, int next_pid, + u64 prev_state, const char *backtrace) { struct per_pid *p = NULL, *prev_p; - struct sched_switch *sw = (void *)te; + prev_p = find_create_pid(prev_pid); - prev_p = find_create_pid(sw->prev_pid); - - p = find_create_pid(sw->next_pid); + p = find_create_pid(next_pid); if (prev_p->current && prev_p->current->state != TYPE_NONE) - pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp); + pid_put_sample(prev_pid, TYPE_RUNNING, cpu, + prev_p->current->state_since, timestamp, + backtrace); if (p && p->current) { if (p->current->state != TYPE_NONE) - pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); + pid_put_sample(next_pid, p->current->state, cpu, + p->current->state_since, timestamp, + backtrace); p->current->state_since = timestamp; p->current->state = TYPE_RUNNING; @@ -457,18 +421,97 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) if (prev_p->current) { prev_p->current->state = TYPE_NONE; prev_p->current->state_since = timestamp; - if (sw->prev_state & 2) + if (prev_state & 2) prev_p->current->state = TYPE_BLOCKED; - if (sw->prev_state == 0) + if (prev_state == 0) prev_p->current->state = TYPE_WAITING; } } +static const char *cat_backtrace(union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct addr_location al; + unsigned int i; + char *p = NULL; + size_t p_len; + u8 cpumode = PERF_RECORD_MISC_USER; + struct addr_location tal; + struct ip_callchain *chain = sample->callchain; + FILE *f = open_memstream(&p, &p_len); + + if (!f) { + perror("open_memstream error"); + return NULL; + } + + if (!chain) + goto exit; + + if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { + fprintf(stderr, "problem processing %d event, skipping it.\n", + event->header.type); + goto exit; + } + + for (i = 0; i < chain->nr; i++) { + u64 ip; + + if (callchain_param.order == ORDER_CALLEE) + ip = chain->ips[i]; + else + ip = chain->ips[chain->nr - i - 1]; + + if (ip >= PERF_CONTEXT_MAX) { + switch (ip) { + case PERF_CONTEXT_HV: + cpumode = PERF_RECORD_MISC_HYPERVISOR; + break; + case PERF_CONTEXT_KERNEL: + cpumode = PERF_RECORD_MISC_KERNEL; + break; + case PERF_CONTEXT_USER: + cpumode = PERF_RECORD_MISC_USER; + break; + default: + pr_debug("invalid callchain context: " + "%"PRId64"\n", (s64) ip); + + /* + * It seems the callchain is corrupted. + * Discard all. + */ + free(p); + p = NULL; + goto exit; + } + continue; + } + + tal.filtered = false; + thread__find_addr_location(al.thread, machine, cpumode, + MAP__FUNCTION, ip, &tal); + + if (tal.sym) + fprintf(f, "..... %016" PRIx64 " %s\n", ip, + tal.sym->name); + else + fprintf(f, "..... %016" PRIx64 "\n", ip); + } + +exit: + fclose(f); + + return p; +} + typedef int (*tracepoint_handler)(struct perf_evsel *evsel, - struct perf_sample *sample); + struct perf_sample *sample, + const char *backtrace); static int process_sample_event(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, + union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine __maybe_unused) @@ -485,81 +528,97 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - return f(evsel, sample); + return f(evsel, sample, cat_backtrace(event, sample, machine)); } return 0; } static int -process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_cpu_idle(struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_processor_entry *ppe = sample->raw_data; + u32 state = perf_evsel__intval(evsel, sample, "state"); + u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); - if (ppe->state == (u32) PWR_EVENT_EXIT) - c_state_end(ppe->cpu_id, sample->time); + if (state == (u32)PWR_EVENT_EXIT) + c_state_end(cpu_id, sample->time); else - c_state_start(ppe->cpu_id, sample->time, ppe->state); + c_state_start(cpu_id, sample->time, state); return 0; } static int -process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_cpu_frequency(struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_processor_entry *ppe = sample->raw_data; + u32 state = perf_evsel__intval(evsel, sample, "state"); + u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); - p_state_change(ppe->cpu_id, sample->time, ppe->state); + p_state_change(cpu_id, sample->time, state); return 0; } static int -process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_sched_wakeup(struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace) { - struct trace_entry *te = sample->raw_data; + u8 flags = perf_evsel__intval(evsel, sample, "common_flags"); + int waker = perf_evsel__intval(evsel, sample, "common_pid"); + int wakee = perf_evsel__intval(evsel, sample, "pid"); - sched_wakeup(sample->cpu, sample->time, sample->pid, te); + sched_wakeup(sample->cpu, sample->time, waker, wakee, flags, backtrace); return 0; } static int -process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_sched_switch(struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace) { - struct trace_entry *te = sample->raw_data; + int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"); + int next_pid = perf_evsel__intval(evsel, sample, "next_pid"); + u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state"); - sched_switch(sample->cpu, sample->time, te); + sched_switch(sample->cpu, sample->time, prev_pid, next_pid, prev_state, + backtrace); return 0; } #ifdef SUPPORT_OLD_POWER_EVENTS static int -process_sample_power_start(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_power_start(struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_entry_old *peo = sample->raw_data; + u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); + u64 value = perf_evsel__intval(evsel, sample, "value"); - c_state_start(peo->cpu_id, sample->time, peo->value); + c_state_start(cpu_id, sample->time, value); return 0; } static int process_sample_power_end(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { c_state_end(sample->cpu, sample->time); return 0; } static int -process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused, - struct perf_sample *sample) +process_sample_power_frequency(struct perf_evsel *evsel, + struct perf_sample *sample, + const char *backtrace __maybe_unused) { - struct power_entry_old *peo = sample->raw_data; + u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); + u64 value = perf_evsel__intval(evsel, sample, "value"); - p_state_change(peo->cpu_id, sample->time, peo->value); + p_state_change(cpu_id, sample->time, value); return 0; } #endif /* SUPPORT_OLD_POWER_EVENTS */ @@ -739,11 +798,12 @@ static void draw_wakeups(void) } if (we->waker == -1) - svg_interrupt(we->time, to); + svg_interrupt(we->time, to, we->backtrace); else if (from && to && abs(from - to) == 1) - svg_wakeline(we->time, from, to); + svg_wakeline(we->time, from, to, we->backtrace); else - svg_partial_wakeline(we->time, from, task_from, to, task_to); + svg_partial_wakeline(we->time, from, task_from, to, + task_to, we->backtrace); we = we->next; free(task_from); @@ -796,11 +856,20 @@ static void draw_process_bars(void) sample = c->samples; while (sample) { if (sample->type == TYPE_RUNNING) - svg_sample(Y, sample->cpu, sample->start_time, sample->end_time); + svg_running(Y, sample->cpu, + sample->start_time, + sample->end_time, + sample->backtrace); if (sample->type == TYPE_BLOCKED) - svg_box(Y, sample->start_time, sample->end_time, "blocked"); + svg_blocked(Y, sample->cpu, + sample->start_time, + sample->end_time, + sample->backtrace); if (sample->type == TYPE_WAITING) - svg_waiting(Y, sample->start_time, sample->end_time); + svg_waiting(Y, sample->cpu, + sample->start_time, + sample->end_time, + sample->backtrace); sample = sample->next; } @@ -911,7 +980,7 @@ static int determine_display_tasks(u64 threshold) /* no exit marker, task kept running to the end */ if (p->end_time == 0) p->end_time = last_time; - if (p->total_time >= threshold && !power_only) + if (p->total_time >= threshold) p->display = 1; c = p->all; @@ -922,7 +991,7 @@ static int determine_display_tasks(u64 threshold) if (c->start_time == 1) c->start_time = first_time; - if (c->total_time >= threshold && !power_only) { + if (c->total_time >= threshold) { c->display = 1; count++; } @@ -945,15 +1014,19 @@ static void write_svg_file(const char *filename) { u64 i; int count; + int thresh = TIME_THRESH; numcpus++; + if (power_only) + proc_num = 0; - count = determine_display_tasks(TIME_THRESH); - - /* We'd like to show at least 15 tasks; be less picky if we have fewer */ - if (count < 15) - count = determine_display_tasks(TIME_THRESH / 10); + /* We'd like to show at least proc_num tasks; + * be less picky if we have fewer */ + do { + count = determine_display_tasks(thresh); + thresh /= 10; + } while (!process_filter && thresh && count < proc_num); open_svg(filename, numcpus, count, first_time, last_time); @@ -964,9 +1037,12 @@ static void write_svg_file(const char *filename) svg_cpu_box(i, max_freq, turbo_frequency); draw_cpu_usage(); - draw_process_bars(); - draw_c_p_states(); - draw_wakeups(); + if (proc_num) + draw_process_bars(); + if (!tasks_only) + draw_c_p_states(); + if (proc_num) + draw_wakeups(); svg_close(); } @@ -1031,50 +1107,92 @@ static int __cmd_timechart(const char *output_name) static int __cmd_record(int argc, const char **argv) { -#ifdef SUPPORT_OLD_POWER_EVENTS - const char * const record_old_args[] = { + unsigned int rec_argc, i, j; + const char **rec_argv; + const char **p; + unsigned int record_elems; + + const char * const common_args[] = { "record", "-a", "-R", "-c", "1", + }; + unsigned int common_args_nr = ARRAY_SIZE(common_args); + + const char * const backtrace_args[] = { + "-g", + }; + unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args); + + const char * const power_args[] = { + "-e", "power:cpu_frequency", + "-e", "power:cpu_idle", + }; + unsigned int power_args_nr = ARRAY_SIZE(power_args); + + const char * const old_power_args[] = { +#ifdef SUPPORT_OLD_POWER_EVENTS "-e", "power:power_start", "-e", "power:power_end", "-e", "power:power_frequency", - "-e", "sched:sched_wakeup", - "-e", "sched:sched_switch", - }; #endif - const char * const record_new_args[] = { - "record", "-a", "-R", "-c", "1", - "-e", "power:cpu_frequency", - "-e", "power:cpu_idle", + }; + unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args); + + const char * const tasks_args[] = { "-e", "sched:sched_wakeup", "-e", "sched:sched_switch", }; - unsigned int rec_argc, i, j; - const char **rec_argv; - const char * const *record_args = record_new_args; - unsigned int record_elems = ARRAY_SIZE(record_new_args); + unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args); #ifdef SUPPORT_OLD_POWER_EVENTS if (!is_valid_tracepoint("power:cpu_idle") && is_valid_tracepoint("power:power_start")) { use_old_power_events = 1; - record_args = record_old_args; - record_elems = ARRAY_SIZE(record_old_args); + power_args_nr = 0; + } else { + old_power_args_nr = 0; } #endif - rec_argc = record_elems + argc - 1; + if (power_only) + tasks_args_nr = 0; + + if (tasks_only) { + power_args_nr = 0; + old_power_args_nr = 0; + } + + if (!with_backtrace) + backtrace_args_no = 0; + + record_elems = common_args_nr + tasks_args_nr + + power_args_nr + old_power_args_nr + backtrace_args_no; + + rec_argc = record_elems + argc; rec_argv = calloc(rec_argc + 1, sizeof(char *)); if (rec_argv == NULL) return -ENOMEM; - for (i = 0; i < record_elems; i++) - rec_argv[i] = strdup(record_args[i]); + p = rec_argv; + for (i = 0; i < common_args_nr; i++) + *p++ = strdup(common_args[i]); - for (j = 1; j < (unsigned int)argc; j++, i++) - rec_argv[i] = argv[j]; + for (i = 0; i < backtrace_args_no; i++) + *p++ = strdup(backtrace_args[i]); - return cmd_record(i, rec_argv, NULL); + for (i = 0; i < tasks_args_nr; i++) + *p++ = strdup(tasks_args[i]); + + for (i = 0; i < power_args_nr; i++) + *p++ = strdup(power_args[i]); + + for (i = 0; i < old_power_args_nr; i++) + *p++ = strdup(old_power_args[i]); + + for (j = 1; j < (unsigned int)argc; j++) + *p++ = argv[j]; + + return cmd_record(rec_argc, rec_argv, NULL); } static int @@ -1090,16 +1208,20 @@ int cmd_timechart(int argc, const char **argv, const char *prefix __maybe_unused) { const char *output_name = "output.svg"; - const struct option options[] = { + const struct option timechart_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_INTEGER('w', "width", &svg_page_width, "page width"), OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"), + OPT_BOOLEAN('T', "tasks-only", &tasks_only, + "output processes data only"), OPT_CALLBACK('p', "process", NULL, "process", "process selector. Pass a pid or process name.", parse_process), OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", "Look for files with symbols relative to this directory"), + OPT_INTEGER('n', "proc-num", &proc_num, + "min. number of tasks to print"), OPT_END() }; const char * const timechart_usage[] = { @@ -1107,15 +1229,39 @@ int cmd_timechart(int argc, const char **argv, NULL }; - argc = parse_options(argc, argv, options, timechart_usage, + const struct option record_options[] = { + OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"), + OPT_BOOLEAN('T', "tasks-only", &tasks_only, + "output processes data only"), + OPT_BOOLEAN('g', "callchain", &with_backtrace, "record callchain"), + OPT_END() + }; + const char * const record_usage[] = { + "perf timechart record []", + NULL + }; + argc = parse_options(argc, argv, timechart_options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (power_only && tasks_only) { + pr_err("-P and -T options cannot be used at the same time.\n"); + return -1; + } + symbol__init(); - if (argc && !strncmp(argv[0], "rec", 3)) + if (argc && !strncmp(argv[0], "rec", 3)) { + argc = parse_options(argc, argv, record_options, record_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (power_only && tasks_only) { + pr_err("-P and -T options cannot be used at the same time.\n"); + return -1; + } + return __cmd_record(argc, argv); - else if (argc) - usage_with_options(timechart_usage, options); + } else if (argc) + usage_with_options(timechart_usage, timechart_options); setup_pager(); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 71e6402729a8..03d37a76c612 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -634,26 +634,9 @@ static void *display_thread(void *arg) return NULL; } -/* Tag samples to be skipped. */ -static const char *skip_symbols[] = { - "intel_idle", - "default_idle", - "native_safe_halt", - "cpu_idle", - "enter_idle", - "exit_idle", - "mwait_idle", - "mwait_idle_with_hints", - "poll_idle", - "ppc64_runlatch_off", - "pseries_dedicated_idle_sleep", - NULL -}; - static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) { const char *name = sym->name; - int i; /* * ppc64 uses function descriptors and appends a '.' to the @@ -671,12 +654,8 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) strstr(name, "_text_end")) return 1; - for (i = 0; skip_symbols[i]; i++) { - if (!strcmp(skip_symbols[i], name)) { - sym->ignore = true; - break; - } - } + if (symbol__is_idle(sym)) + sym->ignore = true; return 0; } @@ -1084,7 +1063,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) "dump the symbol table used for profiling"), OPT_INTEGER('f', "count-filter", &top.count_filter, "only display functions with more events than this"), - OPT_BOOLEAN('g', "group", &opts->group, + OPT_BOOLEAN(0, "group", &opts->group, "put the counters into a counter group"), OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit, "child tasks do not inherit counters"), @@ -1105,7 +1084,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) " abort, in_tx, transaction"), OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, "Show a column with the number of samples"), - OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts, + OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, NULL, "enables call-graph recording", &callchain_opt), OPT_CALLBACK(0, "call-graph", &top.record_opts, diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8be17fc462ba..e9f345e2551a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2158,7 +2158,6 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) size_t printed = data->printed; struct trace *trace = data->trace; struct thread_trace *ttrace = thread->priv; - const char *color; double ratio; if (ttrace == NULL) @@ -2166,17 +2165,9 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; - color = PERF_COLOR_NORMAL; - if (ratio > 50.0) - color = PERF_COLOR_RED; - else if (ratio > 25.0) - color = PERF_COLOR_GREEN; - else if (ratio > 5.0) - color = PERF_COLOR_YELLOW; - - printed += color_fprintf(fp, color, " %s (%d), ", thread__comm_str(thread), thread->tid); + printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid); printed += fprintf(fp, "%lu events, ", ttrace->nr_events); - printed += color_fprintf(fp, color, "%.1f%%", ratio); + printed += fprintf(fp, "%.1f%%", ratio); printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); printed += thread__dump_stats(ttrace, trace, fp); diff --git a/tools/perf/bash_completion b/tools/perf/perf-completion.sh similarity index 65% rename from tools/perf/bash_completion rename to tools/perf/perf-completion.sh index 62e157db2e2b..49494882d9bb 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/perf-completion.sh @@ -1,4 +1,4 @@ -# perf completion +# perf bash and zsh completion # Taken from git.git's completion script. __my_reassemble_comp_words_by_ref() @@ -89,37 +89,113 @@ __ltrim_colon_completions() fi } -type perf &>/dev/null && -_perf() +__perfcomp () { - local cur words cword prev cmd + COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) +} - COMPREPLY=() - _get_comp_words_by_ref -n =: cur words cword prev +__perfcomp_colon () +{ + __perfcomp "$1" "$2" + __ltrim_colon_completions $cur +} + +__perf_main () +{ + local cmd cmd=${words[0]} + COMPREPLY=() # List perf subcommands or long options if [ $cword -eq 1 ]; then if [[ $cur == --* ]]; then - COMPREPLY=( $( compgen -W '--help --version \ + __perfcomp '--help --version \ --exec-path --html-path --paginate --no-pager \ - --perf-dir --work-tree --debugfs-dir' -- "$cur" ) ) + --perf-dir --work-tree --debugfs-dir' -- "$cur" else cmds=$($cmd --list-cmds) - COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) + __perfcomp "$cmds" "$cur" fi # List possible events for -e option elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then evts=$($cmd list --raw-dump) - COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) ) - __ltrim_colon_completions $cur + __perfcomp_colon "$evts" "$cur" # List long option names elif [[ $cur == --* ]]; then subcmd=${words[1]} opts=$($cmd $subcmd --list-opts) - COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) ) + __perfcomp "$opts" "$cur" fi +} + +if [[ -n ${ZSH_VERSION-} ]]; then + autoload -U +X compinit && compinit + + __perfcomp () + { + emulate -L zsh + + local c IFS=$' \t\n' + local -a array + + for c in ${=1}; do + case $c in + --*=*|*.) ;; + *) c="$c " ;; + esac + array[${#array[@]}+1]="$c" + done + + compset -P '*[=:]' + compadd -Q -S '' -a -- array && _ret=0 + } + + __perfcomp_colon () + { + emulate -L zsh + + local cur_="${2-$cur}" + local c IFS=$' \t\n' + local -a array + + if [[ "$cur_" == *:* ]]; then + local colon_word=${cur_%"${cur_##*:}"} + fi + + for c in ${=1}; do + case $c in + --*=*|*.) ;; + *) c="$c " ;; + esac + array[$#array+1]=${c#"$colon_word"} + done + + compset -P '*[=:]' + compadd -Q -S '' -a -- array && _ret=0 + } + + _perf () + { + local _ret=1 cur cword prev + cur=${words[CURRENT]} + prev=${words[CURRENT-1]} + let cword=CURRENT-1 + emulate ksh -c __perf_main + let _ret && _default && _ret=0 + return _ret + } + + compdef _perf perf + return +fi + +type perf &>/dev/null && +_perf() +{ + local cur words cword prev + _get_comp_words_by_ref -n =: cur words cword prev + __perf_main } && complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \ diff --git a/tools/perf/perf.h b/tools/perf/perf.h index b079304bd53d..b23fed527514 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -254,6 +254,7 @@ struct perf_record_opts { bool inherit_stat; bool no_delay; bool no_inherit; + bool no_inherit_set; bool no_samples; bool raw_samples; bool sample_address; diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/attr/test-record-no-inherit index 9079a25cd643..44edcb2edcd5 100644 --- a/tools/perf/tests/attr/test-record-no-inherit +++ b/tools/perf/tests/attr/test-record-no-inherit @@ -3,5 +3,5 @@ command = record args = -i kill >/dev/null 2>&1 [event:base-record] -sample_type=259 +sample_type=263 inherit=0 diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bb788c109fe6..c77814bf01e1 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -732,8 +732,7 @@ int perf_event__preprocess_sample(const union perf_event *event, if (thread == NULL) return -1; - if (symbol_conf.comm_list && - !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) + if (thread__is_filtered(thread)) goto out_filtered; dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index bbc746aa5716..76fa76431329 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -819,8 +819,10 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) if (evlist->threads == NULL) return -1; - if (target->force_per_cpu) - evlist->cpus = cpu_map__new(target->cpu_list); + if (target->default_per_cpu) + evlist->cpus = target->per_thread ? + cpu_map__dummy_new() : + cpu_map__new(target->cpu_list); else if (target__has_task(target)) evlist->cpus = cpu_map__dummy_new(); else if (!target__has_cpu(target) && !target->uses_mmap) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index dad64926170f..b5fe7f9b2e15 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -574,6 +574,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_evsel *leader = evsel->leader; struct perf_event_attr *attr = &evsel->attr; int track = !evsel->idx; /* only the first counter needs these */ + bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; attr->inherit = !opts->no_inherit; @@ -647,7 +648,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } } - if (target__has_cpu(&opts->target) || opts->target.force_per_cpu) + if (target__has_cpu(&opts->target)) perf_evsel__set_sample_bit(evsel, CPU); if (opts->period) @@ -655,7 +656,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (!perf_missing_features.sample_id_all && (opts->sample_time || !opts->no_inherit || - target__has_cpu(&opts->target) || opts->target.force_per_cpu)) + target__has_cpu(&opts->target) || per_cpu)) perf_evsel__set_sample_bit(evsel, TIME); if (opts->raw_samples) { diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 31f404a032a9..d22e3f8017dc 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_BOOLEAN: *(bool *)opt->value = unset ? false : true; + if (opt->set) + *(bool *)opt->set = true; return 0; case OPTION_INCR: @@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, return 0; } if (!rest) { + if (!prefixcmp(options->long_name, "no-")) { + /* + * The long name itself starts with "no-", so + * accept the option without "no-" so that users + * do not have to enter "no-no-" to get the + * negation. + */ + rest = skip_prefix(arg, options->long_name + 3); + if (rest) { + flags |= OPT_UNSET; + goto match; + } + /* Abbreviated case */ + if (!prefixcmp(options->long_name + 3, arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + } /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: @@ -259,6 +279,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, if (!rest) continue; } +match: if (*rest) { if (*rest != '=') continue; diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b0241e28eaf7..cbf0149cf221 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. + * + * `set`:: + * whether an option was set by the user */ struct option { enum parse_opt_type type; @@ -94,6 +97,7 @@ struct option { int flags; parse_opt_cb *callback; intptr_t defval; + bool *set; }; #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) @@ -103,6 +107,10 @@ struct option { #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_BOOLEAN_SET(s, l, v, os, h) \ + { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, bool *), .help = (h), \ + .set = check_vtype(os, bool *)} #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f36d24a02445..b0b15e213df5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1522,6 +1522,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, if (!node) break; + if (node->sym && node->sym->ignore) + goto next; + if (print_ip) printf("%c%16" PRIx64, s, node->ip); @@ -1544,12 +1547,15 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, if (!print_oneline) printf("\n"); - callchain_cursor_advance(&callchain_cursor); - stack_depth--; +next: + callchain_cursor_advance(&callchain_cursor); } } else { + if (al.sym && al.sym->ignore) + return; + if (print_ip) printf("%16" PRIx64, sample->ip); diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 96c866045d60..8b79d3ad1246 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -95,6 +95,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; fprintf(svgfile, " \n"); + fprintf(svgfile, "\n"); fprintf(svgfile, "\n", svg_page_width, total_height); fprintf(svgfile, "\n