diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-12-02 10:08:03 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-12-02 10:08:03 +0100 |
commit | e7af7b15121ca08c31a0ab9df71a41b4c53365b4 (patch) | |
tree | 3c4ee7bd52c8cc41642efee0b5549b573d69268a | |
parent | 3782746a08f6b0a8e385058b6748a5a0f166f3a7 (diff) | |
parent | 0fcb1da4aba6e6c7b32de5e0948b740b31ad822d (diff) | |
download | linux-e7af7b15121ca08c31a0ab9df71a41b4c53365b4.tar.gz linux-e7af7b15121ca08c31a0ab9df71a41b4c53365b4.tar.bz2 linux-e7af7b15121ca08c31a0ab9df71a41b4c53365b4.zip |
Merge tag 'perf-core-for-mingo-20161201' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
New features:
- Support AArch64 in the 'annotate' code, native/local and
cross-arch/remote (Kim Phillips)
- Allow considering just events in a given time interval, via the
'--time start.s.ms,end.s.ms' command line, added to 'perf kmem',
'perf report', 'perf sched timehist' and 'perf script' (David Ahern)
- Add option to stop printing a callchain at one of a given group of
symbol names (David Ahern)
- Handle CPU migration events in 'perf sched timehist' (David Ahern)
- Track memory freed in 'perf kmem stat' (David Ahern)
Infrastructure:
- Add initial support (and perf test entry) for tooling hooks, starting with
'record_start' and 'record_end', that will have as its initial user the
eBPF infrastructure, where perf_ prefixed functions will be JITed and
run when such hooks are called (Wang Nan)
- Remove redundant "test" and similar strings from 'perf test' descriptions
(Arnaldo Carvalho de Melo)
- Implement assorted libbpf improvements (Wang Nan)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
37 files changed, 842 insertions, 149 deletions
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 4212ed62235b..8143536b462a 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -110,3 +110,59 @@ int bpf_map_update_elem(int fd, void *key, void *value, return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); } + +int bpf_map_lookup_elem(int fd, void *key, void *value) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.map_fd = fd; + attr.key = ptr_to_u64(key); + attr.value = ptr_to_u64(value); + + return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); +} + +int bpf_map_delete_elem(int fd, void *key) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.map_fd = fd; + attr.key = ptr_to_u64(key); + + return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); +} + +int bpf_map_get_next_key(int fd, void *key, void *next_key) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.map_fd = fd; + attr.key = ptr_to_u64(key); + attr.next_key = ptr_to_u64(next_key); + + return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); +} + +int bpf_obj_pin(int fd, const char *pathname) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.pathname = ptr_to_u64((void *)pathname); + attr.bpf_fd = fd; + + return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr)); +} + +int bpf_obj_get(const char *pathname) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.pathname = ptr_to_u64((void *)pathname); + + return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr)); +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index e8ba54087497..253c3dbb06b4 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -35,4 +35,11 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, int bpf_map_update_elem(int fd, void *key, void *value, u64 flags); + +int bpf_map_lookup_elem(int fd, void *key, void *value); +int bpf_map_delete_elem(int fd, void *key); +int bpf_map_get_next_key(int fd, void *key, void *next_key); +int bpf_obj_pin(int fd, const char *pathname); +int bpf_obj_get(const char *pathname); + #endif diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 96a2b2ff1212..2e974593f3e8 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -229,6 +229,10 @@ struct bpf_object { * all objects. */ struct list_head list; + + void *priv; + bpf_object_clear_priv_t clear_priv; + char path[]; }; #define obj_elf_valid(o) ((o)->efile.elf) @@ -1229,6 +1233,9 @@ void bpf_object__close(struct bpf_object *obj) if (!obj) return; + if (obj->clear_priv) + obj->clear_priv(obj, obj->priv); + bpf_object__elf_finish(obj); bpf_object__unload(obj); @@ -1282,6 +1289,22 @@ unsigned int bpf_object__kversion(struct bpf_object *obj) return obj ? obj->kern_version : 0; } +int bpf_object__set_priv(struct bpf_object *obj, void *priv, + bpf_object_clear_priv_t clear_priv) +{ + if (obj->priv && obj->clear_priv) + obj->clear_priv(obj, obj->priv); + + obj->priv = priv; + obj->clear_priv = clear_priv; + return 0; +} + +void *bpf_object__priv(struct bpf_object *obj) +{ + return obj ? obj->priv : ERR_PTR(-EINVAL); +} + struct bpf_program * bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) { @@ -1501,3 +1524,15 @@ bpf_object__find_map_by_name(struct bpf_object *obj, const char *name) } return NULL; } + +struct bpf_map * +bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset) +{ + int i; + + for (i = 0; i < obj->nr_maps; i++) { + if (obj->maps[i].offset == offset) + return &obj->maps[i]; + } + return ERR_PTR(-ENOENT); +} diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index dd7a513efb10..a5a8b86a06fe 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -24,6 +24,7 @@ #include <stdio.h> #include <stdbool.h> #include <linux/err.h> +#include <sys/types.h> // for size_t enum libbpf_errno { __LIBBPF_ERRNO__START = 4000, @@ -79,6 +80,11 @@ struct bpf_object *bpf_object__next(struct bpf_object *prev); (pos) != NULL; \ (pos) = (tmp), (tmp) = bpf_object__next(tmp)) +typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *); +int bpf_object__set_priv(struct bpf_object *obj, void *priv, + bpf_object_clear_priv_t clear_priv); +void *bpf_object__priv(struct bpf_object *prog); + /* Accessors of bpf_program. */ struct bpf_program; struct bpf_program *bpf_program__next(struct bpf_program *prog, @@ -195,6 +201,13 @@ struct bpf_map; struct bpf_map * bpf_object__find_map_by_name(struct bpf_object *obj, const char *name); +/* + * Get bpf_map through the offset of corresponding struct bpf_map_def + * in the bpf object file. + */ +struct bpf_map * +bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset); + struct bpf_map * bpf_map__next(struct bpf_map *map, struct bpf_object *obj); #define bpf_map__for_each(pos, obj) \ diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt index ff0f433b3fce..479fc3261a50 100644 --- a/tools/perf/Documentation/perf-kmem.txt +++ b/tools/perf/Documentation/perf-kmem.txt @@ -61,6 +61,13 @@ OPTIONS default, but this option shows live (currently allocated) pages instead. (This option works with --page option only) +--time:: + Only analyze samples within given time window: <start>,<stop>. Times + have the format seconds.microseconds. If start is not given (i.e., time + string is ',x.y') then analysis starts at the beginning of the file. If + stop time is not given (i.e, time string is 'x.y,') then analysis goes + to end of file. + SEE ALSO -------- linkperf:perf-record[1] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 2d1746295abf..3a166ae4a4d3 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -382,6 +382,13 @@ OPTIONS --header-only:: Show only perf.data header (forces --stdio). +--time:: + Only analyze samples within given time window: <start>,<stop>. Times + have the format seconds.microseconds. If start is not given (i.e., time + string is ',x.y') then analysis starts at the beginning of the file. If + stop time is not given (i.e, time string is 'x.y,') then analysis goes + to end of file. + --itrace:: Options for decoding instruction tracing data. The options are: diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index fb9e52d65fca..7775b1eb2bee 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -128,6 +128,18 @@ OPTIONS for 'perf sched timehist' --wakeups:: Show wakeup events. +-M:: +--migrations:: + Show migration events. + +--time:: + Only analyze samples within given time window: <start>,<stop>. Times + have the format seconds.microseconds. If start is not given (i.e., time + string is ',x.y') then analysis starts at the beginning of the file. If + stop time is not given (i.e, time string is 'x.y,') then analysis goes + to end of file. + + SEE ALSO -------- linkperf:perf-record[1] diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index c01904f388ce..5dc5c6a09ac4 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -212,6 +212,9 @@ OPTIONS --hide-call-graph:: When printing symbols do not display call chain. +--stop-bt:: + Stop display of callgraph at these symbols + -C:: --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can be provided as a comma-separated list with no space: 0,1. Ranges of @@ -289,6 +292,13 @@ include::itrace.txt[] --force:: Don't do ownership validation. +--time:: + Only analyze samples within given time window: <start>,<stop>. Times + have the format seconds.microseconds. If start is not given (i.e., time + string is ',x.y') then analysis starts at the beginning of the file. If + stop time is not given (i.e, time string is 'x.y,') then analysis goes + to end of file. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script-perl[1], diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c new file mode 100644 index 000000000000..44eafd6f2d50 --- /dev/null +++ b/tools/perf/arch/arm64/annotate/instructions.c @@ -0,0 +1,62 @@ +#include <sys/types.h> +#include <regex.h> + +struct arm64_annotate { + regex_t call_insn, + jump_insn; +}; + +static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name) +{ + struct arm64_annotate *arm = arch->priv; + struct ins_ops *ops; + regmatch_t match[2]; + + if (!regexec(&arm->jump_insn, name, 2, match, 0)) + ops = &jump_ops; + else if (!regexec(&arm->call_insn, name, 2, match, 0)) + ops = &call_ops; + else if (!strcmp(name, "ret")) + ops = &ret_ops; + else + return NULL; + + arch__associate_ins_ops(arch, name, ops); + return ops; +} + +static int arm64__annotate_init(struct arch *arch) +{ + struct arm64_annotate *arm; + int err; + + if (arch->initialized) + return 0; + + arm = zalloc(sizeof(*arm)); + if (!arm) + return -1; + + /* bl, blr */ + err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED); + if (err) + goto out_free_arm; + /* b, b.cond, br, cbz/cbnz, tbz/tbnz */ + err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl)?n?z?$", + REG_EXTENDED); + if (err) + goto out_free_call; + + arch->initialized = true; + arch->priv = arm; + arch->associate_instruction_ops = arm64__associate_instruction_ops; + arch->objdump.comment_char = ';'; + arch->objdump.skip_functions_char = '+'; + return 0; + +out_free_call: + regfree(&arm->call_insn); +out_free_arm: + free(arm); + return -1; +} diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index 2218cb64f840..99d66191e56c 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -4,27 +4,27 @@ struct test arch_tests[] = { { - .desc = "x86 rdpmc test", + .desc = "x86 rdpmc", .func = test__rdpmc, }, { - .desc = "Test converting perf time to TSC", + .desc = "Convert perf time to TSC", .func = test__perf_time_to_tsc, }, #ifdef HAVE_DWARF_UNWIND_SUPPORT { - .desc = "Test dwarf unwind", + .desc = "DWARF unwind", .func = test__dwarf_unwind, }, #endif #ifdef HAVE_AUXTRACE_SUPPORT { - .desc = "Test x86 instruction decoder - new instructions", + .desc = "x86 instruction decoder - new instructions", .func = test__insn_x86, }, #endif { - .desc = "Test intel cqm nmi context read", + .desc = "Intel cqm nmi context read", .func = test__intel_cqm_count_nmi_context, }, { diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index d426dcb18ce9..35a02f8e5a4a 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -11,6 +11,7 @@ #include "util/session.h" #include "util/tool.h" #include "util/callchain.h" +#include "util/time-utils.h" #include <subcmd/parse-options.h> #include "util/trace-event.h" @@ -49,6 +50,7 @@ struct alloc_stat { u64 ptr; u64 bytes_req; u64 bytes_alloc; + u64 last_alloc; u32 hit; u32 pingpong; @@ -62,9 +64,13 @@ static struct rb_root root_alloc_sorted; static struct rb_root root_caller_stat; static struct rb_root root_caller_sorted; -static unsigned long total_requested, total_allocated; +static unsigned long total_requested, total_allocated, total_freed; static unsigned long nr_allocs, nr_cross_allocs; +/* filters for controlling start and stop of time of analysis */ +static struct perf_time_interval ptime; +const char *time_str; + static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, int bytes_req, int bytes_alloc, int cpu) { @@ -105,6 +111,8 @@ static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, } data->call_site = call_site; data->alloc_cpu = cpu; + data->last_alloc = bytes_alloc; + return 0; } @@ -223,6 +231,8 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel, if (!s_alloc) return 0; + total_freed += s_alloc->last_alloc; + if ((short)sample->cpu != s_alloc->alloc_cpu) { s_alloc->pingpong++; @@ -907,6 +917,15 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, return 0; } +static bool perf_kmem__skip_sample(struct perf_sample *sample) +{ + /* skip sample based on time? */ + if (perf_time__skip_sample(&ptime, sample->time)) + return true; + + return false; +} + typedef int (*tracepoint_handler)(struct perf_evsel *evsel, struct perf_sample *sample); @@ -926,6 +945,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } + if (perf_kmem__skip_sample(sample)) + return 0; + dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); if (evsel->handler != NULL) { @@ -1128,6 +1150,11 @@ static void print_slab_summary(void) printf("\n========================\n"); printf("Total bytes requested: %'lu\n", total_requested); printf("Total bytes allocated: %'lu\n", total_allocated); + printf("Total bytes freed: %'lu\n", total_freed); + if (total_allocated > total_freed) { + printf("Net total bytes allocated: %'lu\n", + total_allocated - total_freed); + } printf("Total bytes wasted on internal fragmentation: %'lu\n", total_allocated - total_requested); printf("Internal fragmentation: %f%%\n", @@ -1884,6 +1911,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", parse_page_opt), OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"), + OPT_STRING(0, "time", &time_str, "str", + "Time span of interest (start,stop)"), OPT_END() }; const char *const kmem_subcommands[] = { "record", "stat", NULL }; @@ -1944,6 +1973,11 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) symbol__init(&session->header.env); + if (perf_time__parse_str(&ptime, time_str) != 0) { + pr_err("Invalid time string\n"); + return -EINVAL; + } + if (!strcmp(argv[0], "stat")) { setlocale(LC_ALL, ""); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 67d2a9003294..fa26865364b6 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -37,6 +37,7 @@ #include "util/llvm-utils.h" #include "util/bpf-loader.h" #include "util/trigger.h" +#include "util/perf-hooks.h" #include "asm/bug.h" #include <unistd.h> @@ -206,6 +207,12 @@ static void sig_handler(int sig) done = 1; } +static void sigsegv_handler(int sig) +{ + perf_hooks__recover(); + sighandler_dump_stack(sig); +} + static void record__sig_exit(void) { if (signr == -1) @@ -833,6 +840,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) signal(SIGCHLD, sig_handler); signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); + signal(SIGSEGV, sigsegv_handler); if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) { signal(SIGUSR2, snapshot_sig_handler); @@ -970,6 +978,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) trigger_ready(&auxtrace_snapshot_trigger); trigger_ready(&switch_output_trigger); + perf_hooks__invoke_record_start(); for (;;) { unsigned long long hits = rec->samples; @@ -1114,6 +1123,8 @@ out_child: } } + perf_hooks__invoke_record_end(); + if (!err && !quiet) { char samples[128]; const char *postfix = rec->timestamp_filename ? diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3dfbfffe2ecd..d2afbe4a240d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -36,7 +36,7 @@ #include "util/hist.h" #include "util/data.h" #include "arch/common.h" - +#include "util/time-utils.h" #include "util/auxtrace.h" #include <dlfcn.h> @@ -59,6 +59,8 @@ struct report { const char *pretty_printing_style; const char *cpu_list; const char *symbol_filter_str; + const char *time_str; + struct perf_time_interval ptime; float min_percent; u64 nr_entries; u64 queue_size; @@ -158,6 +160,9 @@ static int process_sample_event(struct perf_tool *tool, }; int ret = 0; + if (perf_time__skip_sample(&rep->ptime, sample->time)) + return 0; + if (machine__resolve(machine, &al, sample) < 0) { pr_debug("problem processing %d event, skipping it.\n", event->header.type); @@ -830,6 +835,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK_DEFAULT(0, "stdio-color", NULL, "mode", "'always' (default), 'never' or 'auto' only applicable to --stdio mode", stdio__config_color, "always"), + OPT_STRING(0, "time", &report.time_str, "str", + "Time span of interest (start,stop)"), OPT_END() }; struct perf_data_file file = { @@ -1015,6 +1022,11 @@ repeat: if (symbol__init(&session->header.env) < 0) goto error; + if (perf_time__parse_str(&report.ptime, report.time_str) != 0) { + pr_err("Invalid time string\n"); + return -EINVAL; + } + sort__setup_elide(stdout); ret = __cmd_report(&report); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index a49a032f5b15..870d94cd20ba 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -15,6 +15,7 @@ #include "util/color.h" #include "util/stat.h" #include "util/callchain.h" +#include "util/time-utils.h" #include <subcmd/parse-options.h> #include "util/trace-event.h" @@ -203,7 +204,10 @@ struct perf_sched { unsigned int max_stack; bool show_cpu_visual; bool show_wakeups; + bool show_migrations; u64 skipped_samples; + const char *time_str; + struct perf_time_interval ptime; }; /* per thread run time data */ @@ -216,6 +220,8 @@ struct thread_runtime { struct stats run_stats; u64 total_run_time; + + u64 migrations; }; /* per event run time data */ @@ -1834,13 +1840,14 @@ static void timehist_header(struct perf_sched *sched) static void timehist_print_sample(struct perf_sched *sched, struct perf_sample *sample, struct addr_location *al, - struct thread *thread) + struct thread *thread, + u64 t) { struct thread_runtime *tr = thread__priv(thread); u32 max_cpus = sched->max_cpu + 1; char tstr[64]; - timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr)); + timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); printf("%15s [%04d] ", tstr, sample->cpu); if (sched->show_cpu_visual) { @@ -2191,12 +2198,94 @@ static int timehist_sched_wakeup_event(struct perf_tool *tool, tr->ready_to_run = sample->time; /* show wakeups if requested */ - if (sched->show_wakeups) + if (sched->show_wakeups && + !perf_time__skip_sample(&sched->ptime, sample->time)) timehist_print_wakeup_event(sched, sample, machine, thread); return 0; } +static void timehist_print_migration_event(struct perf_sched *sched, + struct perf_evsel *evsel, + struct perf_sample *sample, + struct machine *machine, + struct thread *migrated) +{ + struct thread *thread; + char tstr[64]; + u32 max_cpus = sched->max_cpu + 1; + u32 ocpu, dcpu; + + if (sched->summary_only) + return; + + max_cpus = sched->max_cpu + 1; + ocpu = perf_evsel__intval(evsel, sample, "orig_cpu"); + dcpu = perf_evsel__intval(evsel, sample, "dest_cpu"); + + thread = machine__findnew_thread(machine, sample->pid, sample->tid); + if (thread == NULL) + return; + + if (timehist_skip_sample(sched, thread) && + timehist_skip_sample(sched, migrated)) { + return; + } + + timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr)); + printf("%15s [%04d] ", tstr, sample->cpu); + + if (sched->show_cpu_visual) { + u32 i; + char c; + + printf(" "); + for (i = 0; i < max_cpus; ++i) { + c = (i == sample->cpu) ? 'm' : ' '; + printf("%c", c); + } + printf(" "); + } + + printf(" %-*s ", comm_width, timehist_get_commstr(thread)); + + /* dt spacer */ + printf(" %9s %9s %9s ", "", "", ""); + + printf("migrated: %s", timehist_get_commstr(migrated)); + printf(" cpu %d => %d", ocpu, dcpu); + + printf("\n"); +} + +static int timehist_migrate_task_event(struct perf_tool *tool, + union perf_event *event __maybe_unused, + struct perf_evsel *evsel, + struct perf_sample *sample, + struct machine *machine) +{ + struct perf_sched *sched = container_of(tool, struct perf_sched, tool); + struct thread *thread; + struct thread_runtime *tr = NULL; + /* want pid of migrated task not pid in sample */ + const u32 pid = perf_evsel__intval(evsel, sample, "pid"); + + thread = machine__findnew_thread(machine, 0, pid); + if (thread == NULL) + return -1; + + tr = thread__get_runtime(thread); + if (tr == NULL) + return -1; + + tr->migrations++; + + /* show migrations if requested */ + timehist_print_migration_event(sched, evsel, sample, machine, thread); + + return 0; +} + static int timehist_sched_change_event(struct perf_tool *tool, union perf_event *event, struct perf_evsel *evsel, @@ -2204,10 +2293,11 @@ static int timehist_sched_change_event(struct perf_tool *tool, struct machine *machine) { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); + struct perf_time_interval *ptime = &sched->ptime; struct addr_location al; struct thread *thread; struct thread_runtime *tr = NULL; - u64 tprev; + u64 tprev, t = sample->time; int rc = 0; if (machine__resolve(machine, &al, sample) < 0) { @@ -2234,9 +2324,35 @@ static int timehist_sched_change_event(struct perf_tool *tool, tprev = perf_evsel__get_time(evsel, sample->cpu); - timehist_update_runtime_stats(tr, sample->time, tprev); + /* + * If start time given: + * - sample time is under window user cares about - skip sample + * - tprev is under window user cares about - reset to start of window + */ + if (ptime->start && ptime->start > t) + goto out; + + if (ptime->start > tprev) + tprev = ptime->start; + + /* + * If end time given: + * - previous sched event is out |