diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-11 17:39:12 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-11 17:39:12 -0500 |
| commit | 655c6b977755594d104d2b87d3e896bd09fd34e8 (patch) | |
| tree | 73b49a23eb032953deb5f79b57dff3c492a24dc4 | |
| parent | 08b5278650d6e9f61a210f88e3f418c87389df5b (diff) | |
| parent | 45fd808091449086f5bf83dff263ad081108020d (diff) | |
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf fixes from Thomas Gleixner:
"A bunch of perf tooling fixes:
- Make the Intel PT SQL viewer more robust
- Make the Intel PT debug log more useful
- Support weak groups in perf record so it's behaving the same way as
perf stat
- Display the LBR stats in callchain entries properly in perf top
- Handle different PMu names with common prefix properlin in pert
stat
- Start syscall augmenting in perf trace. Preparation for
architecture independent eBPF instrumentation of syscalls.
- Fix build breakage in JVMTI perf lib
- Fix arm64 tools build failure wrt smp_load_{acquire,release}"
* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
perf tools: Do not zero sample_id_all for group members
perf tools: Fix undefined symbol scnprintf in libperf-jvmti.so
perf beauty: Use SRCARCH, ARCH=x86_64 must map to "x86" to find the headers
perf intel-pt: Add MTC and CYC timestamps to debug log
perf intel-pt: Add more event information to debug log
perf scripts python: exported-sql-viewer.py: Fix table find when table re-ordered
perf scripts python: exported-sql-viewer.py: Add help window
perf scripts python: exported-sql-viewer.py: Add Selected branches report
perf scripts python: exported-sql-viewer.py: Fall back to /usr/local/lib/libxed.so
perf top: Display the LBR stats in callchain entry
perf stat: Handle different PMU names with common prefix
perf record: Support weak groups
perf evlist: Move perf_evsel__reset_weak_group into evlist
perf augmented_syscalls: Start collecting pathnames in the BPF program
perf trace: Fix setting of augmented payload when using eBPF + raw_syscalls
perf trace: When augmenting raw_syscalls plug raw_syscalls:sys_exit too
perf examples bpf: Start augmenting raw_syscalls:sys_{start,exit}
tools headers barrier: Fix arm64 tools build failure wrt smp_load_{acquire,release}
| -rw-r--r-- | tools/arch/arm64/include/asm/barrier.h | 133 | ||||
| -rw-r--r-- | tools/perf/Documentation/perf-list.txt | 1 | ||||
| -rw-r--r-- | tools/perf/Makefile.perf | 2 | ||||
| -rw-r--r-- | tools/perf/builtin-record.c | 7 | ||||
| -rw-r--r-- | tools/perf/builtin-stat.c | 28 | ||||
| -rw-r--r-- | tools/perf/builtin-top.c | 3 | ||||
| -rw-r--r-- | tools/perf/builtin-trace.c | 34 | ||||
| -rw-r--r-- | tools/perf/examples/bpf/augmented_raw_syscalls.c | 131 | ||||
| -rw-r--r-- | tools/perf/jvmti/jvmti_agent.c | 49 | ||||
| -rwxr-xr-x | tools/perf/scripts/python/exported-sql-viewer.py | 493 | ||||
| -rw-r--r-- | tools/perf/tests/attr/test-record-group-sampling | 1 | ||||
| -rw-r--r-- | tools/perf/util/evlist.c | 27 | ||||
| -rw-r--r-- | tools/perf/util/evlist.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/evsel.c | 1 | ||||
| -rw-r--r-- | tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | 4 | ||||
| -rw-r--r-- | tools/perf/util/intel-pt-decoder/intel-pt-log.c | 5 | ||||
| -rw-r--r-- | tools/perf/util/intel-pt-decoder/intel-pt-log.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/intel-pt.c | 16 | ||||
| -rw-r--r-- | tools/perf/util/pmu.c | 2 |
19 files changed, 820 insertions, 121 deletions
diff --git a/tools/arch/arm64/include/asm/barrier.h b/tools/arch/arm64/include/asm/barrier.h index 12835ea0e417..378c051fa177 100644 --- a/tools/arch/arm64/include/asm/barrier.h +++ b/tools/arch/arm64/include/asm/barrier.h | |||
| @@ -14,74 +14,75 @@ | |||
| 14 | #define wmb() asm volatile("dmb ishst" ::: "memory") | 14 | #define wmb() asm volatile("dmb ishst" ::: "memory") |
| 15 | #define rmb() asm volatile("dmb ishld" ::: "memory") | 15 | #define rmb() asm volatile("dmb ishld" ::: "memory") |
| 16 | 16 | ||
| 17 | #define smp_store_release(p, v) \ | 17 | #define smp_store_release(p, v) \ |
| 18 | do { \ | 18 | do { \ |
| 19 | union { typeof(*p) __val; char __c[1]; } __u = \ | 19 | union { typeof(*p) __val; char __c[1]; } __u = \ |
| 20 | { .__val = (__force typeof(*p)) (v) }; \ | 20 | { .__val = (v) }; \ |
| 21 | \ | 21 | \ |
| 22 | switch (sizeof(*p)) { \ | 22 | switch (sizeof(*p)) { \ |
| 23 | case 1: \ | 23 | case 1: \ |
| 24 | asm volatile ("stlrb %w1, %0" \ | 24 | asm volatile ("stlrb %w1, %0" \ |
| 25 | : "=Q" (*p) \ | 25 | : "=Q" (*p) \ |
| 26 | : "r" (*(__u8 *)__u.__c) \ | 26 | : "r" (*(__u8_alias_t *)__u.__c) \ |
| 27 | : "memory"); \ | 27 | : "memory"); \ |
| 28 | break; \ | 28 | break; \ |
| 29 | case 2: \ | 29 | case 2: \ |
| 30 | asm volatile ("stlrh %w1, %0" \ | 30 | asm volatile ("stlrh %w1, %0" \ |
| 31 | : "=Q" (*p) \ | 31 | : "=Q" (*p) \ |
| 32 | : "r" (*(__u16 *)__u.__c) \ | 32 | : "r" (*(__u16_alias_t *)__u.__c) \ |
| 33 | : "memory"); \ | 33 | : "memory"); \ |
| 34 | break; \ | 34 | break; \ |
| 35 | case 4: \ | 35 | case 4: \ |
| 36 | asm volatile ("stlr %w1, %0" \ | 36 | asm volatile ("stlr %w1, %0" \ |
| 37 | : "=Q" (*p) \ | 37 | : "=Q" (*p) \ |
| 38 | : "r" (*(__u32 *)__u.__c) \ | 38 | : "r" (*(__u32_alias_t *)__u.__c) \ |
| 39 | : "memory"); \ | 39 | : "memory"); \ |
| 40 | break; \ | 40 | break; \ |
| 41 | case 8: \ | 41 | case 8: \ |
| 42 | asm volatile ("stlr %1, %0" \ | 42 | asm volatile ("stlr %1, %0" \ |
| 43 | : "=Q" (*p) \ | 43 | : "=Q" (*p) \ |
| 44 | : "r" (*(__u64 *)__u.__c) \ | 44 | : "r" (*(__u64_alias_t *)__u.__c) \ |
| 45 | : "memory"); \ | 45 | : "memory"); \ |
| 46 | break; \ | 46 | break; \ |
| 47 | default: \ | 47 | default: \ |
| 48 | /* Only to shut up gcc ... */ \ | 48 | /* Only to shut up gcc ... */ \ |
| 49 | mb(); \ | 49 | mb(); \ |
| 50 | break; \ | 50 | break; \ |
| 51 | } \ | 51 | } \ |
| 52 | } while (0) | 52 | } while (0) |
| 53 | 53 | ||
| 54 | #define smp_load_acquire(p) \ | 54 | #define smp_load_acquire(p) \ |
| 55 | ({ \ | 55 | ({ \ |
| 56 | union { typeof(*p) __val; char __c[1]; } __u; \ | 56 | union { typeof(*p) __val; char __c[1]; } __u = \ |
| 57 | \ | 57 | { .__c = { 0 } }; \ |
| 58 | switch (sizeof(*p)) { \ | 58 | \ |
| 59 | case 1: \ | 59 | switch (sizeof(*p)) { \ |
| 60 | asm volatile ("ldarb %w0, %1" \ | 60 | case 1: \ |
| 61 | : "=r" (*(__u8 *)__u.__c) \ | 61 | asm volatile ("ldarb %w0, %1" \ |
| 62 | : "Q" (*p) : "memory"); \ | 62 | : "=r" (*(__u8_alias_t *)__u.__c) \ |
| 63 | break; \ | 63 | : "Q" (*p) : "memory"); \ |
| 64 | case 2: \ | 64 | break; \ |
| 65 | asm volatile ("ldarh %w0, %1" \ | 65 | case 2: \ |
| 66 | : "=r" (*(__u16 *)__u.__c) \ | 66 | asm volatile ("ldarh %w0, %1" \ |
| 67 | : "Q" (*p) : "memory"); \ | 67 | : "=r" (*(__u16_alias_t *)__u.__c) \ |
| 68 | break; \ | 68 | : "Q" (*p) : "memory"); \ |
| 69 | case 4: \ | 69 | break; \ |
| 70 | asm volatile ("ldar %w0, %1" \ | 70 | case 4: \ |
| 71 | : "=r" (*(__u32 *)__u.__c) \ | 71 | asm volatile ("ldar %w0, %1" \ |
| 72 | : "Q" (*p) : "memory"); \ | 72 | : "=r" (*(__u32_alias_t *)__u.__c) \ |
| 73 | break; \ | 73 | : "Q" (*p) : "memory"); \ |
| 74 | case 8: \ | 74 | break; \ |
| 75 | asm volatile ("ldar %0, %1" \ | 75 | case 8: \ |
| 76 | : "=r" (*(__u64 *)__u.__c) \ | 76 | asm volatile ("ldar %0, %1" \ |
| 77 | : "Q" (*p) : "memory"); \ | 77 | : "=r" (*(__u64_alias_t *)__u.__c) \ |
| 78 | break; \ | 78 | : "Q" (*p) : "memory"); \ |
| 79 | default: \ | 79 | break; \ |
| 80 | /* Only to shut up gcc ... */ \ | 80 | default: \ |
| 81 | mb(); \ | 81 | /* Only to shut up gcc ... */ \ |
| 82 | break; \ | 82 | mb(); \ |
| 83 | } \ | 83 | break; \ |
| 84 | __u.__val; \ | 84 | } \ |
| 85 | __u.__val; \ | ||
| 85 | }) | 86 | }) |
| 86 | 87 | ||
| 87 | #endif /* _TOOLS_LINUX_ASM_AARCH64_BARRIER_H */ | 88 | #endif /* _TOOLS_LINUX_ASM_AARCH64_BARRIER_H */ |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 236b9b97dfdb..667c14e56031 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
| @@ -55,7 +55,6 @@ counted. The following modifiers exist: | |||
| 55 | S - read sample value (PERF_SAMPLE_READ) | 55 | S - read sample value (PERF_SAMPLE_READ) |
| 56 | D - pin the event to the PMU | 56 | D - pin the event to the PMU |
| 57 | W - group is weak and will fallback to non-group if not schedulable, | 57 | W - group is weak and will fallback to non-group if not schedulable, |
| 58 | only supported in 'perf stat' for now. | ||
| 59 | 58 | ||
| 60 | The 'p' modifier can be used for specifying how precise the instruction | 59 | The 'p' modifier can be used for specifying how precise the instruction |
| 61 | address should be. The 'p' modifier can be specified multiple times: | 60 | address should be. The 'p' modifier can be specified multiple times: |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 3ccb4f0bf088..d95655489f7e 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
| @@ -387,7 +387,7 @@ SHELL = $(SHELL_PATH) | |||
| 387 | 387 | ||
| 388 | linux_uapi_dir := $(srctree)/tools/include/uapi/linux | 388 | linux_uapi_dir := $(srctree)/tools/include/uapi/linux |
| 389 | asm_generic_uapi_dir := $(srctree)/tools/include/uapi/asm-generic | 389 | asm_generic_uapi_dir := $(srctree)/tools/include/uapi/asm-generic |
| 390 | arch_asm_uapi_dir := $(srctree)/tools/arch/$(ARCH)/include/uapi/asm/ | 390 | arch_asm_uapi_dir := $(srctree)/tools/arch/$(SRCARCH)/include/uapi/asm/ |
| 391 | 391 | ||
| 392 | beauty_outdir := $(OUTPUT)trace/beauty/generated | 392 | beauty_outdir := $(OUTPUT)trace/beauty/generated |
| 393 | beauty_ioctl_outdir := $(beauty_outdir)/ioctl | 393 | beauty_ioctl_outdir := $(beauty_outdir)/ioctl |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 10cf889c6d75..488779bc4c8d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -391,7 +391,12 @@ try_again: | |||
| 391 | ui__warning("%s\n", msg); | 391 | ui__warning("%s\n", msg); |
| 392 | goto try_again; | 392 | goto try_again; |
| 393 | } | 393 | } |
| 394 | 394 | if ((errno == EINVAL || errno == EBADF) && | |
| 395 | pos->leader != pos && | ||
| 396 | pos->weak_group) { | ||
| 397 | pos = perf_evlist__reset_weak_group(evlist, pos); | ||
| 398 | goto try_again; | ||
| 399 | } | ||
| 395 | rc = -errno; | 400 | rc = -errno; |
| 396 | perf_evsel__open_strerror(pos, &opts->target, | 401 | perf_evsel__open_strerror(pos, &opts->target, |
| 397 | errno, msg, sizeof(msg)); | 402 | errno, msg, sizeof(msg)); |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d1028d7755bb..a635abfa77b6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -383,32 +383,6 @@ static bool perf_evsel__should_store_id(struct perf_evsel *counter) | |||
| 383 | return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; | 383 | return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; |
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel) | ||
| 387 | { | ||
| 388 | struct perf_evsel *c2, *leader; | ||
| 389 | bool is_open = true; | ||
| 390 | |||
| 391 | leader = evsel->leader; | ||
| 392 | pr_debug("Weak group for %s/%d failed\n", | ||
| 393 | leader->name, leader->nr_members); | ||
| 394 | |||
| 395 | /* | ||
| 396 | * for_each_group_member doesn't work here because it doesn't | ||
| 397 | * include the first entry. | ||
| 398 | */ | ||
| 399 | evlist__for_each_entry(evsel_list, c2) { | ||
| 400 | if (c2 == evsel) | ||
| 401 | is_open = false; | ||
| 402 | if (c2->leader == leader) { | ||
| 403 | if (is_open) | ||
| 404 | perf_evsel__close(c2); | ||
| 405 | c2->leader = c2; | ||
| 406 | c2->nr_members = 0; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | return leader; | ||
| 410 | } | ||
| 411 | |||
| 412 | static bool is_target_alive(struct target *_target, | 386 | static bool is_target_alive(struct target *_target, |
| 413 | struct thread_map *threads) | 387 | struct thread_map *threads) |
| 414 | { | 388 | { |
| @@ -477,7 +451,7 @@ try_again: | |||
| 477 | if ((errno == EINVAL || errno == EBADF) && | 451 | if ((errno == EINVAL || errno == EBADF) && |
| 478 | counter->leader != counter && | 452 | counter->leader != counter && |
| 479 | counter->weak_group) { | 453 | counter->weak_group) { |
| 480 | counter = perf_evsel__reset_weak_group(counter); | 454 | counter = perf_evlist__reset_weak_group(evsel_list, counter); |
| 481 | goto try_again; | 455 | goto try_again; |
| 482 | } | 456 | } |
| 483 | 457 | ||
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b2838de13de0..aa0c73e57924 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -1429,6 +1429,9 @@ int cmd_top(int argc, const char **argv) | |||
| 1429 | } | 1429 | } |
| 1430 | } | 1430 | } |
| 1431 | 1431 | ||
| 1432 | if (opts->branch_stack && callchain_param.enabled) | ||
| 1433 | symbol_conf.show_branchflag_count = true; | ||
| 1434 | |||
| 1432 | sort__mode = SORT_MODE__TOP; | 1435 | sort__mode = SORT_MODE__TOP; |
| 1433 | /* display thread wants entries to be collapsed in a different tree */ | 1436 | /* display thread wants entries to be collapsed in a different tree */ |
| 1434 | perf_hpp_list.need_collapse = 1; | 1437 | perf_hpp_list.need_collapse = 1; |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index dc8a6c4986ce..835619476370 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -108,6 +108,7 @@ struct trace { | |||
| 108 | } stats; | 108 | } stats; |
| 109 | unsigned int max_stack; | 109 | unsigned int max_stack; |
| 110 | unsigned int min_stack; | 110 | unsigned int min_stack; |
| 111 | bool raw_augmented_syscalls; | ||
| 111 | bool not_ev_qualifier; | 112 | bool not_ev_qualifier; |
| 112 | bool live; | 113 | bool live; |
| 113 | bool full_time; | 114 | bool full_time; |
| @@ -1724,13 +1725,28 @@ static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel, | |||
| 1724 | return printed; | 1725 | return printed; |
| 1725 | } | 1726 | } |
| 1726 | 1727 | ||
| 1727 | static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size) | 1728 | static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size, bool raw_augmented) |
| 1728 | { | 1729 | { |
| 1729 | void *augmented_args = NULL; | 1730 | void *augmented_args = NULL; |
| 1731 | /* | ||
| 1732 | * For now with BPF raw_augmented we hook into raw_syscalls:sys_enter | ||
| 1733 | * and there we get all 6 syscall args plus the tracepoint common | ||
| 1734 | * fields (sizeof(long)) and the syscall_nr (another long). So we check | ||
| 1735 | * if that is the case and if so don't look after the sc->args_size, | ||
| 1736 | * but always after the full raw_syscalls:sys_enter payload, which is | ||
| 1737 | * fixed. | ||
| 1738 | * | ||
| 1739 | * We'll revisit this later to pass s->args_size to the BPF augmenter | ||
| 1740 | * (now tools/perf/examples/bpf/augmented_raw_syscalls.c, so that it | ||
| 1741 | * copies only what we need for each syscall, like what happens when we | ||
| 1742 | * use syscalls:sys_enter_NAME, so that we reduce the kernel/userspace | ||
| 1743 | * traffic to just what is needed for each syscall. | ||
| 1744 | */ | ||
| 1745 | int args_size = raw_augmented ? (8 * (int)sizeof(long)) : sc->args_size; | ||
| 1730 | 1746 | ||
| 1731 | *augmented_args_size = sample->raw_size - sc->args_size; | 1747 | *augmented_args_size = sample->raw_size - args_size; |
| 1732 | if (*augmented_args_size > 0) | 1748 | if (*augmented_args_size > 0) |
| 1733 | augmented_args = sample->raw_data + sc->args_size; | 1749 | augmented_args = sample->raw_data + args_size; |
| 1734 | 1750 | ||
| 1735 | return augmented_args; | 1751 | return augmented_args; |
| 1736 | } | 1752 | } |
| @@ -1780,7 +1796,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
| 1780 | * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. | 1796 | * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. |
| 1781 | */ | 1797 | */ |
| 1782 | if (evsel != trace->syscalls.events.sys_enter) | 1798 | if (evsel != trace->syscalls.events.sys_enter) |
| 1783 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size); | 1799 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls); |
| 1784 | ttrace->entry_time = sample->time; | 1800 | ttrace->entry_time = sample->time; |
| 1785 | msg = ttrace->entry_str; | 1801 | msg = ttrace->entry_str; |
| 1786 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); | 1802 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); |
| @@ -1833,7 +1849,7 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evse | |||
| 1833 | goto out_put; | 1849 | goto out_put; |
| 1834 | 1850 | ||
| 1835 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); | 1851 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
| 1836 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size); | 1852 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls); |
| 1837 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); | 1853 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); |
| 1838 | fprintf(trace->output, "%s", msg); | 1854 | fprintf(trace->output, "%s", msg); |
| 1839 | err = 0; | 1855 | err = 0; |
| @@ -3501,7 +3517,15 @@ int cmd_trace(int argc, const char **argv) | |||
| 3501 | evsel->handler = trace__sys_enter; | 3517 | evsel->handler = trace__sys_enter; |
| 3502 | 3518 | ||
| 3503 | evlist__for_each_entry(trace.evlist, evsel) { | 3519 | evlist__for_each_entry(trace.evlist, evsel) { |
| 3520 | bool raw_syscalls_sys_exit = strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_exit") == 0; | ||
| 3521 | |||
| 3522 | if (raw_syscalls_sys_exit) { | ||
| 3523 | trace.raw_augmented_syscalls = true; | ||
| 3524 | goto init_augmented_syscall_tp; | ||
| 3525 | } | ||
| 3526 | |||
| 3504 | if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) { | 3527 | if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) { |
| 3528 | init_augmented_syscall_tp: | ||
| 3505 | perf_evsel__init_augmented_syscall_tp(evsel); | 3529 | perf_evsel__init_augmented_syscall_tp(evsel); |
| 3506 | perf_evsel__init_augmented_syscall_tp_ret(evsel); | 3530 | perf_evsel__init_augmented_syscall_tp_ret(evsel); |
| 3507 | evsel->handler = trace__sys_exit; | 3531 | evsel->handler = trace__sys_exit; |
diff --git a/tools/perf/examples/bpf/augmented_raw_syscalls.c b/tools/perf/examples/bpf/augmented_raw_syscalls.c new file mode 100644 index 000000000000..90a19336310b --- /dev/null +++ b/tools/perf/examples/bpf/augmented_raw_syscalls.c | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Augment the raw_syscalls tracepoints with the contents of the pointer arguments. | ||
| 4 | * | ||
| 5 | * Test it with: | ||
| 6 | * | ||
| 7 | * perf trace -e tools/perf/examples/bpf/augmented_raw_syscalls.c cat /etc/passwd > /dev/null | ||
| 8 | * | ||
| 9 | * This exactly matches what is marshalled into the raw_syscall:sys_enter | ||
| 10 | * payload expected by the 'perf trace' beautifiers. | ||
| 11 | * | ||
| 12 | * For now it just uses the existing tracepoint augmentation code in 'perf | ||
| 13 | * trace', in the next csets we'll hook up these with the sys_enter/sys_exit | ||
| 14 | * code that will combine entry/exit in a strace like way. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <stdio.h> | ||
| 18 | #include <linux/socket.h> | ||
| 19 | |||
| 20 | /* bpf-output associated map */ | ||
| 21 | struct bpf_map SEC("maps") __augmented_syscalls__ = { | ||
| 22 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, | ||
| 23 | .key_size = sizeof(int), | ||
| 24 | .value_size = sizeof(u32), | ||
| 25 | .max_entries = __NR_CPUS__, | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct syscall_enter_args { | ||
| 29 | unsigned long long common_tp_fields; | ||
| 30 | long syscall_nr; | ||
| 31 | unsigned long args[6]; | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct syscall_exit_args { | ||
| 35 | unsigned long long common_tp_fields; | ||
| 36 | long syscall_nr; | ||
| 37 | long ret; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct augmented_filename { | ||
| 41 | unsigned int size; | ||
| 42 | int reserved; | ||
| 43 | char value[256]; | ||
| 44 | }; | ||
| 45 | |||
| 46 | #define SYS_OPEN 2 | ||
| 47 | #define SYS_OPENAT 257 | ||
| 48 | |||
| 49 | SEC("raw_syscalls:sys_enter") | ||
| 50 | int sys_enter(struct syscall_enter_args *args) | ||
| 51 | { | ||
| 52 | struct { | ||
| 53 | struct syscall_enter_args args; | ||
| 54 | struct augmented_filename filename; | ||
| 55 | } augmented_args; | ||
| 56 | unsigned int len = sizeof(augmented_args); | ||
| 57 | const void *filename_arg = NULL; | ||
| 58 | |||
| 59 | probe_read(&augmented_args.args, sizeof(augmented_args.args), args); | ||
| 60 | /* | ||
| 61 | * Yonghong and Edward Cree sayz: | ||
| 62 | * | ||
| 63 | * https://www.spinics.net/lists/netdev/msg531645.html | ||
| 64 | * | ||
| 65 | * >> R0=inv(id=0) R1=inv2 R6=ctx(id=0,off=0,imm=0) R7=inv64 R10=fp0,call_-1 | ||
| 66 | * >> 10: (bf) r1 = r6 | ||
| 67 | * >> 11: (07) r1 += 16 | ||
| 68 | * >> 12: (05) goto pc+2 | ||
| 69 | * >> 15: (79) r3 = *(u64 *)(r1 +0) | ||
| 70 | * >> dereference of modified ctx ptr R1 off=16 disallowed | ||
| 71 | * > Aha, we at least got a different error message this time. | ||
| 72 | * > And indeed llvm has done that optimisation, rather than the more obvious | ||
| 73 | * > 11: r3 = *(u64 *)(r1 +16) | ||
| 74 | * > because it wants to have lots of reads share a single insn. You may be able | ||
| 75 | * > to defeat that optimisation by adding compiler barriers, idk. Maybe someone | ||
| 76 | * > with llvm knowledge can figure out how to stop it (ideally, llvm would know | ||
| 77 | * > when it's generating for bpf backend and not do that). -O0? ¯\_(ツ)_/¯ | ||
| 78 | * | ||
| 79 | * The optimization mostly likes below: | ||
| 80 | * | ||
| 81 | * br1: | ||
| 82 | * ... | ||
| 83 | * r1 += 16 | ||
| 84 | * goto merge | ||
| 85 | * br2: | ||
| 86 | * ... | ||
| 87 | * r1 += 20 | ||
| 88 | * goto merge | ||
| 89 | * merge: | ||
| 90 | * *(u64 *)(r1 + 0) | ||
| 91 | * | ||
| 92 | * The compiler tries to merge common loads. There is no easy way to | ||
| 93 | * stop this compiler optimization without turning off a lot of other | ||
| 94 | * optimizations. The easiest way is to add barriers: | ||
| 95 | * | ||
| 96 | * __asm__ __volatile__("": : :"memory") | ||
| 97 | * | ||
| 98 | * after the ctx memory access to prevent their down stream merging. | ||
| 99 | */ | ||
| 100 | switch (augmented_args.args.syscall_nr) { | ||
| 101 | case SYS_OPEN: filename_arg = (const void *)args->args[0]; | ||
| 102 | __asm__ __volatile__("": : :"memory"); | ||
| 103 | break; | ||
| 104 | case SYS_OPENAT: filename_arg = (const void *)args->args[1]; | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | |||
| 108 | if (filename_arg != NULL) { | ||
| 109 | augmented_args.filename.reserved = 0; | ||
| 110 | augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, | ||
| 111 | sizeof(augmented_args.filename.value), | ||
| 112 | filename_arg); | ||
| 113 | if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) { | ||
| 114 | len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size; | ||
| 115 | len &= sizeof(augmented_args.filename.value) - 1; | ||
| 116 | } | ||
| 117 | } else { | ||
| 118 | len = sizeof(augmented_args.args); | ||
| 119 | } | ||
| 120 | |||
| 121 | perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, &augmented_args, len); | ||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | SEC("raw_syscalls:sys_exit") | ||
| 126 | int sys_exit(struct syscall_exit_args *args) | ||
| 127 | { | ||
| 128 | return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */ | ||
| 129 | } | ||
| 130 | |||
| 131 | license(GPL); | ||
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c index ac1bcdc17dae..f7eb63cbbc65 100644 --- a/tools/perf/jvmti/jvmti_agent.c +++ b/tools/perf/jvmti/jvmti_agent.c | |||
| @@ -125,7 +125,7 @@ perf_get_timestamp(void) | |||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | static int | 127 | static int |
| 128 | debug_cache_init(void) | 128 | create_jit_cache_dir(void) |
| 129 | { | 129 | { |
| 130 | char str[32]; | 130 | char str[32]; |
| 131 | char *base, *p; | 131 | char *base, *p; |
| @@ -144,8 +144,13 @@ debug_cache_init(void) | |||
| 144 | 144 | ||
| 145 | strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); | 145 | strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); |
| 146 | 146 | ||
| 147 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/", base); | 147 | ret = snprintf(jit_path, PATH_MAX, "%s/.debug/", base); |
| 148 | 148 | if (ret >= PATH_MAX) { | |
| 149 | warnx("jvmti: cannot generate jit cache dir because %s/.debug/" | ||
| 150 | " is too long, please check the cwd, JITDUMPDIR, and" | ||
| 151 | " HOME variables", base); | ||
| 152 | return -1; | ||
| 153 | } | ||
| 149 | ret = mkdir(jit_path, 0755); | 154 | ret = mkdir(jit_path, 0755); |
| 150 | if (ret == -1) { | 155 | if (ret == -1) { |
| 151 | if (errno != EEXIST) { | 156 | if (errno != EEXIST) { |
| @@ -154,20 +159,32 @@ debug_cache_init(void) | |||
| 154 | } | 159 | } |
| 155 | } | 160 | } |
| 156 | 161 | ||
| 157 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit", base); | 162 | ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit", base); |
| 163 | if (ret >= PATH_MAX) { | ||
| 164 | warnx("jvmti: cannot generate jit cache dir because" | ||
| 165 | " %s/.debug/jit is too long, please check the cwd," | ||
| 166 | " JITDUMPDIR, and HOME variables", base); | ||
| 167 | return -1; | ||
| 168 | } | ||
| 158 | ret = mkdir(jit_path, 0755); | 169 | ret = mkdir(jit_path, 0755); |
| 159 | if (ret == -1) { | 170 | if (ret == -1) { |
| 160 | if (errno != EEXIST) { | 171 | if (errno != EEXIST) { |
| 161 | warn("cannot create jit cache dir %s", jit_path); | 172 | warn("jvmti: cannot create jit cache dir %s", jit_path); |
| 162 | return -1; | 173 | return -1; |
| 163 | } | 174 | } |
| 164 | } | 175 | } |
| 165 | 176 | ||
| 166 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit/%s.XXXXXXXX", base, str); | 177 | ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit/%s.XXXXXXXX", base, str); |
| 167 | 178 | if (ret >= PATH_MAX) { | |
| 179 | warnx("jvmti: cannot generate jit cache dir because" | ||
| 180 | " %s/.debug/jit/%s.XXXXXXXX is too long, please check" | ||
| 181 | " the cwd, JITDUMPDIR, and HOME variables", | ||
| 182 | base, str); | ||
| 183 | return -1; | ||
| 184 | } | ||
| 168 | p = mkdtemp(jit_path); | 185 | p = mkdtemp(jit_path); |
| 169 | if (p != jit_path) { | 186 | if (p != jit_path) { |
| 170 | warn("cannot create jit cache dir %s", jit_path); | 187 | warn("jvmti: cannot create jit cache dir %s", jit_path); |
| 171 | return -1; | 188 | return -1; |
| 172 | } | 189 | } |
| 173 | 190 | ||
| @@ -228,7 +245,7 @@ void *jvmti_open(void) | |||
| 228 | { | 245 | { |
| 229 | char dump_path[PATH_MAX]; | 246 | char dump_path[PATH_MAX]; |
| 230 | struct jitheader header; | 247 | struct jitheader header; |
| 231 | int fd; | 248 | int fd, ret; |
| 232 | FILE *fp; | 249 | FILE *fp; |
| 233 | 250 | ||
| 234 | init_arch_timestamp(); | 251 | init_arch_timestamp(); |
| @@ -245,12 +262,22 @@ void *jvmti_open(void) | |||
| 245 | 262 | ||
| 246 | memset(&header, 0, sizeof(header)); | 263 | memset(&header, 0, sizeof(header)); |
| 247 | 264 | ||
| 248 | debug_cache_init(); | 265 | /* |
| 266 | * jitdump file dir | ||
| 267 | */ | ||
| 268 | if (create_jit_cache_dir() < 0) | ||
| 269 | return NULL; | ||
| 249 | 270 | ||
| 250 | /* | 271 | /* |
| 251 | * jitdump file name | 272 | * jitdump file name |
| 252 | */ | 273 | */ |
| 253 | scnprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); | 274 | ret = snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); |
| 275 | if (ret >= PATH_MAX) { | ||
| 276 | warnx("jvmti: cannot generate jitdump file full path because" | ||
| 277 | " %s/jit-%i.dump is too long, please check the cwd," | ||
| 278 | " JITDUMPDIR, and HOME variables", jit_path, getpid()); | ||
| 279 | return NULL; | ||
| 280 | } | ||
| 254 | 281 | ||
| 255 | fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); | 282 | fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); |
| 256 | if (fd == -1) | 283 | if (fd == -1) |
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index 24cb0bd56afa..f278ce5ebab7 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py | |||
| @@ -119,6 +119,14 @@ def dsoname(name): | |||
| 119 | return "[kernel]" | 119 | return "[kernel]" |
| 120 | return name | 120 | return name |
| 121 | 121 | ||
| 122 | def findnth(s, sub, n, offs=0): | ||
| 123 | pos = s.find(sub) | ||
| 124 | if pos < 0: | ||
| 125 | return pos | ||
| 126 | if n <= 1: | ||
| 127 | return offs + pos | ||
| 128 | return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) | ||
| 129 | |||
| 122 | # Percent to one decimal place | 130 | # Percent to one decimal place |
| 123 | 131 | ||
| 124 | def PercentToOneDP(n, d): | 132 | def PercentToOneDP(n, d): |
| @@ -1464,6 +1472,317 @@ class BranchWindow(QMdiSubWindow): | |||
| 1464 | else: | 1472 | else: |
| 1465 | self.find_bar.NotFound() | 1473 | self.find_bar.NotFound() |
| 1466 | 1474 | ||
| 1475 | # Dialog data item converted and validated using a SQL table | ||
| 1476 | |||
| 1477 | class SQLTableDialogDataItem(): | ||
| 1478 | |||
| 1479 | def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): | ||
| 1480 | self.glb = glb | ||
| 1481 | self.label = label | ||
| 1482 | self.placeholder_text = placeholder_text | ||
| 1483 | self.table_name = table_name | ||
| 1484 | self.match_column = match_column | ||
| 1485 | self.column_name1 = column_name1 | ||
| 1486 | self.column_name2 = column_name2 | ||
| 1487 | self.parent = parent | ||
| 1488 | |||
| 1489 | self.value = "" | ||
| 1490 | |||
| 1491 | self.widget = QLineEdit() | ||
| 1492 | self.widget.editingFinished.connect(self.Validate) | ||
| 1493 | self.widget.textChanged.connect(self.Invalidate) | ||
| 1494 | self.red = False | ||
| 1495 | self.error = "" | ||
| 1496 | self.validated = True | ||
| 1497 | |||
| 1498 | self.last_id = 0 | ||
| 1499 | self.first_time = 0 | ||
| 1500 | self.last_time = 2 ** 64 | ||
| 1501 | if self.table_name == "<timeranges>": | ||
| 1502 | query = QSqlQuery(self.glb.db) | ||
| 1503 | QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") | ||
| 1504 | if query.next(): | ||
| 1505 | self.last_id = int(query.value(0)) | ||
| 1506 | self.last_time = int(query.value(1)) | ||
| 1507 | QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") | ||
| 1508 | if query.next(): | ||
| 1509 | self.first_time = int(query.value(0)) | ||
| 1510 | if placeholder_text: | ||
| 1511 | placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) | ||
| 1512 | |||
| 1513 | if placeholder_text: | ||
| 1514 | self.widget.setPlaceholderText(placeholder_text) | ||
| 1515 | |||
| 1516 | def ValueToIds(self, value): | ||
| 1517 | ids = [] | ||
| 1518 | query = QSqlQuery(self.glb.db) | ||
| 1519 | stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" | ||
| 1520 | ret = query.exec_(stmt) | ||
| 1521 | if ret: | ||
| 1522 | while query.next(): | ||
| 1523 | ids.append(str(query.value(0))) | ||
| 1524 | return ids | ||
| 1525 | |||
| 1526 | def IdBetween(self, query, lower_id, higher_id, order): | ||
| 1527 | QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") | ||
| 1528 | if query.next(): | ||
| 1529 | return True, int(query.value(0)) | ||
| 1530 | else: | ||
| 1531 | return False, 0 | ||
| 1532 | |||
| 1533 | def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): | ||
| 1534 | query = QSqlQuery(self.glb.db) | ||
| 1535 | while True: | ||
| 1536 | next_id = int((lower_id + higher_id) / 2) | ||
| 1537 | QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) | ||
| 1538 | if not query.next(): | ||
| 1539 | ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") | ||
| 1540 | if not ok: | ||
| 1541 | ok, dbid = self.IdBetween(query, next_id, higher_id, "") | ||
| 1542 | if not ok: | ||
| 1543 | return str(higher_id) | ||
| 1544 | next_id = dbid | ||
| 1545 | QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) | ||
| 1546 | next_time = int(query.value(0)) | ||
| 1547 | if get_floor: | ||
| 1548 | if target_time > next_time: | ||
| 1549 | lower_id = next_id | ||
| 1550 | else: | ||
| 1551 | higher_id = next_id | ||
| 1552 | if higher_id <= lower_id + 1: | ||
| 1553 | return str(higher_id) | ||
| 1554 | else: | ||
| 1555 | if target_time >= next_time: | ||
| 1556 | lower_id = next_id | ||
| 1557 | else: | ||
| 1558 | higher_id = next_id | ||
| 1559 | if higher_id <= lower_id + 1: | ||
| 1560 | return str(lower_id) | ||
| 1561 | |||
| 1562 | def ConvertRelativeTime(self, val): | ||
| 1563 | print "val ", val | ||
| 1564 | mult = 1 | ||
| 1565 | suffix = val[-2:] | ||
| 1566 | if suffix == "ms": | ||
| 1567 | mult = 1000000 | ||
| 1568 | elif suffix == "us": | ||
| 1569 | mult = 1000 | ||
| 1570 | elif suffix == "ns": | ||
| 1571 | mult = 1 | ||
| 1572 | else: | ||
| 1573 | return val | ||
| 1574 | val = val[:-2].strip() | ||
| 1575 | if not self.IsNumber(val): | ||
| 1576 | return val | ||
| 1577 | val = int(val) * mult | ||
| 1578 | if val >= 0: | ||
| 1579 | val += self.first_time | ||
| 1580 | else: | ||
| 1581 | val += self.last_time | ||
| 1582 | return str(val) | ||
| 1583 | |||
| 1584 | def ConvertTimeRange(self, vrange): | ||
| 1585 | print "vrange ", vrange | ||
| 1586 | if vrange[0] == "": | ||
| 1587 | vrange[0] = str(self.first_time) | ||
| 1588 | if vrange[1] == "": | ||
| 1589 | vrange[1] = str(self.last_time) | ||
| 1590 | vrange[0] = self.ConvertRelativeTime(vrange[0]) | ||
| 1591 | vrange[1] = self.ConvertRelativeTime(vrange[1]) | ||
| 1592 | print "vrange2 ", vrange | ||
| 1593 | if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): | ||
| 1594 | return False | ||
| 1595 | print "ok1" | ||
| 1596 | beg_range = max(int(vrange[0]), self.first_time) | ||
| 1597 | end_range = min(int(vrange[1]), self.last_time) | ||
| 1598 | if beg_range > self.last_time or end_range < self.first_time: | ||
| 1599 | return False | ||
| 1600 | print "ok2" | ||
| 1601 | vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) | ||
| 1602 | vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) | ||
| 1603 | print "vrange3 ", vrange | ||
| 1604 | return True | ||
| 1605 | |||
| 1606 | def AddTimeRange(self, value, ranges): | ||
| 1607 | print "value ", value | ||
| 1608 | n = value.count("-") | ||
| 1609 | if n == 1: | ||
| 1610 | pass | ||
| 1611 | elif n == 2: | ||
| 1612 | if value.split("-")[1].strip() == "": | ||
| 1613 | n = 1 | ||
| 1614 | elif n == 3: | ||
| 1615 | n = 2 | ||
| 1616 | else: | ||
| 1617 | return False | ||
| 1618 | pos = findnth(value, "-", n) | ||
| 1619 | vrange = [value[:pos].strip() ,value[pos+1:].strip()] | ||
| 1620 | if self.ConvertTimeRange(vrange): | ||
| 1621 | ranges.append(vrange) | ||
| 1622 | return True | ||
| 1623 | return False | ||
| 1624 | |||
| 1625 | def InvalidValue(self, value): | ||
| 1626 | self.value = "" | ||
| 1627 | palette = QPalette() | ||
| 1628 | palette.setColor(QPalette.Text,Qt.red) | ||
| 1629 | self.widget.setPalette(palette) | ||
| 1630 | self.red = True | ||
| 1631 | self.error = self.label + " invalid value '" + value + "'" | ||
| 1632 | self.parent.ShowMessage(self.error) | ||
| 1633 | |||
| 1634 | def IsNumber(self, value): | ||
| 1635 | try: | ||
| 1636 | x = int(value) | ||
| 1637 | except: | ||
| 1638 | x = 0 | ||
| 1639 | return str(x) == value | ||
| 1640 | |||
| 1641 | def Invalidate(self): | ||
| 1642 | self.validated = False | ||
| 1643 | |||
| 1644 | def Validate(self): | ||
| 1645 | input_string = self.widget.text() | ||
| 1646 | self.validated = True | ||
| 1647 | if self.red: | ||
| 1648 | palette = QPalette() | ||
| 1649 | self.widget.setPalette(palette) | ||
| 1650 | self.red = False | ||
| 1651 | if not len(input_string.strip()): | ||
| 1652 | self.error = "" | ||
| 1653 | self.value = "" | ||
| 1654 | return | ||
| 1655 | if self.table_name == "<timeranges>": | ||
| 1656 | ranges = [] | ||
| 1657 | for value in [x.strip() for x in input_string.split(",")]: | ||
| 1658 | if not self.AddTimeRange(value, ranges): | ||
| 1659 | return self.InvalidValue(value) | ||
| 1660 | ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] | ||
| 1661 | self.value = " OR ".join(ranges) | ||
| 1662 | elif self.table_name == "<ranges>": | ||
| 1663 | singles = [] | ||
| 1664 | ranges = [] | ||
| 1665 | for value in [x.strip() for x in input_string.split(",")]: | ||
| 1666 | if "-" in value: | ||
| 1667 | vrange = value.split("-") | ||
| 1668 | if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): | ||
| 1669 | return self.InvalidValue(value) | ||
| 1670 | ranges.append(vrange) | ||
| 1671 | else: | ||
| 1672 | if not self.IsNumber(value): | ||
| 1673 | return self.InvalidValue(value) | ||
| 1674 | singles.append(value) | ||
| 1675 | ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] | ||
| 1676 | if len(singles): | ||
| 1677 | ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")") | ||
| 1678 | self.value = " OR ".join(ranges) | ||
| 1679 | elif self.table_name: | ||
| 1680 | all_ids = [] | ||
| 1681 | for value in [x.strip() for x in input_string.split(",")]: | ||
| 1682 | ids = self.ValueToIds(value) | ||
| 1683 | if len(ids): | ||
| 1684 | all_ids.extend(ids) | ||
| 1685 | else: | ||
| 1686 | return self.InvalidValue(value) | ||
| 1687 | self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" | ||
| 1688 | if self.column_name2: | ||
| 1689 | self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" | ||
| 1690 | else: | ||
| 1691 | self.value = input_string.strip() | ||
| 1692 | self.error = "" | ||
| 1693 | self.parent.ClearMessage() | ||
| 1694 | |||
| 1695 | def IsValid(self): | ||
| 1696 | if not self.validated: | ||
| 1697 | self.Validate() | ||
| 1698 | if len(self.error): | ||
| 1699 | self.parent.ShowMessage(self.error) | ||
| 1700 | return False | ||
| 1701 | return True | ||
| 1702 | |||
| 1703 | # Selected branch report creation dialog | ||
| 1704 | |||
| 1705 | class SelectedBranchDialog(QDialog): | ||
| 1706 | |||
| 1707 | def __init__(self, glb, parent=None): | ||
| 1708 | super(SelectedBranchDialog, self).__init__(parent) | ||
| 1709 | |||
| 1710 | self.glb = glb | ||
| 1711 | |||
| 1712 | self.name = "" | ||
| 1713 | self.where_clause = "" | ||
| 1714 | |||
| 1715 | self.setWindowTitle("Selected Branches") | ||
| 1716 | self.setMinimumWidth(600) | ||
| 1717 | |||
| 1718 | items = ( | ||
| 1719 | ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""), | ||
| 1720 | ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""), | ||
| 1721 | ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""), | ||
| 1722 | ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""), | ||
| 1723 | ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""), | ||
| 1724 | ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""), | ||
| 1725 | ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"), | ||
| 1726 | ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"), | ||
| 1727 | ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""), | ||
| 1728 | ) | ||
| 1729 | self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items] | ||
| 1730 | |||
| 1731 | self.grid = QGridLayout() | ||
| 1732 | |||
| 1733 | for row in xrange(len(self.data_items)): | ||
| 1734 | self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) | ||
| 1735 | self.grid.addWidget(self.data_items[row].widget, row, 1) | ||
| 1736 | |||
| 1737 | self.status = QLabel() | ||
| 1738 | |||
| 1739 | self.ok_button = QPushButton("Ok", self) | ||
| 1740 | self.ok_button.setDefault(True) | ||
| 1741 | self.ok_button.released.connect(self.Ok) | ||
| 1742 | self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
| 1743 | |||
| 1744 | self.cancel_button = QPushButton("Cancel", self) | ||
| 1745 | self.cancel_button.released.connect(self.reject) | ||
| 1746 | self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | ||
| 1747 | |||
| 1748 | self.hbox = QHBoxLayout() | ||
| 1749 | #self.hbox.addStretch() | ||
| 1750 | self.hbox.addWidget(self.status) | ||
| 1751 | self.hbox.addWidget(self.ok_button) | ||
| 1752 | self.hbox.addWidget(self.cancel_button) | ||
| 1753 | |||
| 1754 | self.vbox = QVBoxLayout() | ||
| 1755 | self.vbox.addLayout(self.grid) | ||
| 1756 | self.vbox.addLayout(self.hbox) | ||
| 1757 | |||
| 1758 | self.setLayout(self.vbox); | ||
| 1759 | |||
| 1760 | def Ok(self): | ||
| 1761 | self.name = self.data_items[0].value | ||
| 1762 | if not self.name: | ||
| 1763 | self.ShowMessage("Report name is required") | ||
| 1764 | return | ||
| 1765 | for d in self.data_items: | ||
| 1766 | if not d.IsValid(): | ||
| 1767 | return | ||
| 1768 | for d in self.data_items[1:]: | ||
| 1769 | if len(d.value): | ||
| 1770 | if len(self.where_clause): | ||
| 1771 | self.where_clause += " AND " | ||
| 1772 | self.where_clause += d.value | ||
| 1773 | if len(self.where_clause): | ||
| 1774 | self.where_clause = " AND ( " + self.where_clause + " ) " | ||
| 1775 | else: | ||
| 1776 | self.ShowMessage("No selection") | ||
| 1777 | return | ||
| 1778 | self.accept() | ||
| 1779 | |||
| 1780 | def ShowMessage(self, msg): | ||
| 1781 | self.status.setText("<font color=#FF0000>" + msg) | ||
| 1782 | |||
| 1783 | def ClearMessage(self): | ||
| 1784 | self.status.setText("") | ||
| 1785 | |||
| 1467 | # Event list | 1786 | # Event list |
| 1468 | 1787 | ||
| 1469 | def GetEventList(db): | 1788 | def GetEventList(db): |
| @@ -1656,7 +1975,7 @@ class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): | |||
| 1656 | def FindDone(self, row): | 1975 | def FindDone(self, row): |
| 1657 | self.find_bar.Idle() | 1976 | self.find_bar.Idle() |
| 1658 | if row >= 0: | 1977 | if row >= 0: |
| 1659 | self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) | 1978 | self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) |
| 1660 | else: | 1979 | else: |
| 1661 | self.find_bar.NotFound() | 1980 | self.find_bar.NotFound() |
| 1662 | 1981 | ||
| @@ -1765,6 +2084,149 @@ class WindowMenu(): | |||
| 1765 | def setActiveSubWindow(self, nr): | 2084 | def setActiveSubWindow(self, nr): |
| 1766 | self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) | 2085 | self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) |
| 1767 | 2086 | ||
| 2087 | # Help text | ||
| 2088 | |||
| 2089 | glb_help_text = """ | ||
| 2090 | <h1>Contents</h1> | ||
| 2091 | <style> | ||
| 2092 | p.c1 { | ||
| 2093 | text-indent: 40px; | ||
| 2094 | } | ||
| 2095 | p.c2 { | ||
| 2096 | text-indent: 80px; | ||
| 2097 | } | ||
| 2098 | } | ||
| 2099 | </style> | ||
| 2100 | <p class=c1><a href=#reports>1. Reports</a></p> | ||
| 2101 | <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> | ||
| 2102 | <p class=c2><a href=#allbranches>1.2 All branches</a></p> | ||
| 2103 | <p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> | ||
| 2104 | <p class=c1><a href=#tables>2. Tables</a></p> | ||
| 2105 | <h1 id=reports>1. Reports</h1> | ||
| 2106 | <h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> | ||
| 2107 | The result is a GUI window with a tree representing a context-sensitive | ||
| 2108 | call-graph. Expanding a couple of levels of the tree and adjusting column | ||
| 2109 | widths to suit will display something like: | ||
| 2110 | <pre> | ||
| 2111 | Call Graph: pt_example | ||
| 2112 | Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) | ||
| 2113 | v- ls | ||
| 2114 | v- 2638:2638 | ||
| 2115 | v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 | ||
| 2116 | |- unknown unknown 1 13198 0.1 1 0.0 | ||
| 2117 | >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 | ||
| 2118 | >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 | ||
| 2119 | v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 | ||
| 2120 | >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 | ||
| 2121 | >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 | ||
| 2122 | >- __libc_csu_init ls 1 10354 0.1 10 0.0 | ||
| 2123 | |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 | ||
| 2124 | v- main ls 1 8182043 99.6 180254 99.9 | ||
| 2125 | </pre> | ||
| 2126 | <h3>Points to note:</h3> | ||
| 2127 | <ul> | ||
| 2128 | <li>The top level is a command name (comm)</li> | ||
| 2129 | <li>The next level is a thread (pid:tid)</li> | ||
| 2130 | <li>Subsequent levels are functions</li> | ||
| 2131 | <li>'Count' is the number of calls</li> | ||
| 2132 | <li>'Time' is the elapsed time until the function returns</li> | ||
| 2133 | <li>Percentages are relative to the level above</li> | ||
| 2134 | <li>'Branch Count' is the total number of branches for that function and all functions that it calls | ||
| 2135 | </ul> | ||
| 2136 | <h3>Find</h3> | ||
| 2137 | Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match. | ||
| 2138 | The pattern matching symbols are ? for any character and * for zero or more characters. | ||
| 2139 | <h2 id=allbranches>1.2 All branches</h2> | ||
| 2140 | The All branches report displays all branches in chronological order. | ||
| 2141 | Not all data is fetched immediately. More records can be fetched using the Fetch bar provided. | ||
| 2142 | <h3>Disassembly</h3> | ||
| 2143 | Open a branch to display disassembly. This only works if: | ||
| 2144 | <ol> | ||
| 2145 | <li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> | ||
| 2146 | <li>The object code is available. Currently, only the perf build ID cache is searched for object code. | ||
| 2147 | The default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. | ||
| 2148 | One exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), | ||
| 2149 | or alternatively, set environment variable PERF_KCORE to the kcore file name.</li> | ||
| 2150 | </ol> | ||
| 2151 | <h4 id=xed>Intel XED Setup</h4> | ||
| 2152 | To use Intel XED, libxed.so must be present. To build and install libxed.so: | ||
| 2153 | <pre> | ||
| 2154 | git clone https://github.com/intelxed/mbuild.git mbuild | ||
| 2155 | git clone https://github.com/intelxed/xed | ||
| 2156 | cd xed | ||
| 2157 | ./mfile.py --share | ||
| 2158 | sudo ./mfile.py --prefix=/usr/local install | ||
| 2159 | sudo ldconfig | ||
| 2160 | </pre> | ||
| 2161 | <h3>Find</h3> | ||
| 2162 | Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. | ||
| 2163 | Refer to Python documentation for the regular expression syntax. | ||
| 2164 | All columns are searched, but only currently fetched rows are searched. | ||
| 2165 | <h2 id=selectedbranches>1.3 Selected branches</h2> | ||
| 2166 | This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced | ||
| 2167 | by various selection criteria. A dialog box displays available criteria which are AND'ed together. | ||
| 2168 | <h3>1.3.1 Time ranges</h3> | ||
| 2169 | The time ranges hint text shows the total time range. Relative time ranges can also be entered in | ||
| 2170 | ms, us or ns. Also, negative values are relative to the end of trace. Examples: | ||
| 2171 | <pre> | ||
| 2172 | 81073085947329-81073085958238 From 81073085947329 to 81073085958238 | ||
| 2173 | 100us-200us From 100us to 200us | ||
| 2174 | 10ms- From 10ms to the end | ||
| 2175 | -100ns The first 100ns | ||
| 2176 | -10ms- The last 10ms | ||
| 2177 | </pre> | ||
| 2178 | N.B. Due to the granularity of timestamps, there could be no branches in any given time range. | ||
| 2179 | <h1 id=tables>2. Tables</h1> | ||
| 2180 | The Tables menu shows all tables and views in the database. Most tables have an associated view | ||
| 2181 | which displays the information in a more friendly way. Not all data for large tables is fetched | ||
| 2182 | immediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, | ||
| 2183 | but that can be slow for large tables. | ||
| 2184 | <p>There are also tables of database meta-information. | ||
| 2185 | For SQLite3 databases, the sqlite_master table is included. | ||
| 2186 | For PostgreSQL databases, information_schema.tables/views/columns are included. | ||
| 2187 | <h3>Find</h3> | ||
| 2188 | Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. | ||
| 2189 | Refer to Python documentation for the regular expression syntax. | ||
| 2190 | All columns are searched, but only currently fetched rows are searched. | ||
| 2191 | <p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous | ||
| 2192 | will go to the next/previous result in id order, instead of display order. | ||
| 2193 | """ | ||
| 2194 | |||
| 2195 | # Help window | ||
| 2196 | |||
| 2197 | class HelpWindow(QMdiSubWindow): | ||
| 2198 | |||
| 2199 | def __init__(self, glb, parent=None): | ||
| 2200 | super(HelpWindow, self).__init__(parent) | ||
| 2201 | |||
| 2202 | self.text = QTextBrowser() | ||
| 2203 | self.text.setHtml(glb_help_text) | ||
| 2204 | self.text.setReadOnly(True) | ||
| 2205 | self.text.setOpenExternalLinks(True) | ||
| 2206 | |||
| 2207 | self.setWidget(self.text) | ||
| 2208 | |||
| 2209 | AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") | ||
| 2210 | |||
| 2211 | # Main window that only displays the help text | ||
| 2212 | |||
| 2213 | class HelpOnlyWindow(QMainWindow): | ||
| 2214 | |||
| 2215 | def __init__(self, parent=None): | ||
| 2216 | super(HelpOnlyWindow, self).__init__(parent) | ||
| 2217 | |||
| 2218 | self.setMinimumSize(200, 100) | ||
| 2219 | self.resize(800, 600) | ||
| 2220 | self.setWindowTitle("Exported SQL Viewer Help") | ||
| 2221 | self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) | ||
| 2222 | |||
| 2223 | self.text = QTextBrowser() | ||
| 2224 | self.text.setHtml(glb_help_text) | ||
| 2225 | self.text.setReadOnly(True) | ||
| 2226 | self.text.setOpenExternalLinks(True) | ||
| 2227 | |||
| 2228 | self.setCentralWidget(self.text) | ||
| 2229 | |||
| 1768 | # Font resize | 2230 | # Font resize |
| 1769 | 2231 | ||
| 1770 | def ResizeFont(widget, diff): | 2232 | def ResizeFont(widget, diff): |
| @@ -1851,6 +2313,9 @@ class MainWindow(QMainWindow): | |||
| 1851 | 2313 | ||
| 1852 | self.window_menu = WindowMenu(self.mdi_area, menu) | 2314 | self.window_menu = WindowMenu(self.mdi_area, menu) |
| 1853 | 2315 | ||
| 2316 | help_menu = menu.addMenu("&Help") | ||
| 2317 | help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) | ||
| 2318 | |||
| 1854 | def Find(self): | 2319 | def Find(self): |
| 1855 | win = self.mdi_area.activeSubWindow() | 2320 | win = self.mdi_area.activeSubWindow() |
| 1856 | if win: | 2321 | if win: |
| @@ -1888,6 +2353,8 @@ class MainWindow(QMainWindow): | |||
| 1888 | if event == "branches": | 2353 | if event == "branches": |
| 1889 | label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" | 2354 | label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" |
| 1890 | reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) | 2355 | reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) |
| 2356 | label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" | ||
| 2357 | reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) | ||
| 1891 | 2358 | ||
| 1892 | def TableMenu(self, tables, menu): | 2359 | def TableMenu(self, tables, menu): |
| 1893 | table_menu = menu.addMenu("&Tables") | 2360 | table_menu = menu.addMenu("&Tables") |
| @@ -1900,9 +2367,18 @@ class MainWindow(QMainWindow): | |||
| 1900 | def NewBranchView(self, event_id): | 2367 | def NewBranchView(self, event_id): |
| 1901 | BranchWindow(self.glb, event_id, "", "", self) | 2368 | BranchWindow(self.glb, event_id, "", "", self) |
| 1902 | 2369 | ||
| 2370 | def NewSelectedBranchView(self, event_id): | ||
| 2371 | dialog = SelectedBranchDialog(self.glb, self) | ||
| 2372 | ret = dialog.exec_() | ||
| 2373 | if ret: | ||
| 2374 | BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self) | ||
| 2375 | |||
| 1903 | def NewTableView(self, table_name): | 2376 | def NewTableView(self, table_name): |
| 1904 | TableWindow(self.glb, table_name, self) | 2377 | TableWindow(self.glb, table_name, self) |
| 1905 | 2378 | ||
| 2379 | def Help(self): | ||
| 2380 | HelpWindow(self.glb, self) | ||
| 2381 | |||
| 1906 | # XED Disassembler | 2382 | # XED Disassembler |
| 1907 | 2383 | ||
| 1908 | class xed_state_t(Structure): | 2384 | class xed_state_t(Structure): |
| @@ -1929,7 +2405,12 @@ class XEDInstruction(): | |||
| 1929 | class LibXED(): | 2405 | class LibXED(): |
| 1930 | 2406 | ||
| 1931 | def __init__(self): | 2407 | def __init__(self): |
| 1932 | self.libxed = CDLL("libxed.so") | 2408 | try: |
| 2409 | self.libxed = CDLL("libxed.so") | ||
| 2410 | except: | ||
| 2411 | self.libxed = None | ||
| 2412 | if not self.libxed: | ||
| 2413 | self.libxed = CDLL("/usr/local/lib/libxed.so") | ||
| 1933 | 2414 | ||
| 1934 | self.xed_tables_init = self.libxed.xed_tables_init | 2415 | self.xed_tables_init = self.libxed.xed_tables_init |
| 1935 | self.xed_tables_init.restype = None | 2416 | self.xed_tables_init.restype = None |
| @@ -2097,10 +2578,16 @@ class DBRef(): | |||
| 2097 | 2578 | ||
| 2098 | def Main(): | 2579 | def Main(): |
| 2099 | if (len(sys.argv) < 2): | 2580 | if (len(sys.argv) < 2): |
| 2100 | print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" | 2581 | print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}" |
| 2101 | raise Exception("Too few arguments") | 2582 | raise Exception("Too few arguments") |
| 2102 | 2583 | ||
| 2103 | dbname = sys.argv[1] | 2584 | dbname = sys.argv[1] |
| 2585 | if dbname == "--help-only": | ||
| 2586 | app = QApplication(sys.argv) | ||
| 2587 | mainwindow = HelpOnlyWindow() | ||
| 2588 | mainwindow.show() | ||
| 2589 | err = app.exec_() | ||
| 2590 | sys.exit(err) | ||
| 2104 | 2591 | ||
| 2105 | is_sqlite3 = False | 2592 | is_sqlite3 = False |
| 2106 | try: | 2593 | try: |
diff --git a/tools/perf/tests/attr/test-record-group-sampling b/tools/perf/tests/attr/test-record-group-sampling index 8a33ca4f9e1f..f0729c454f16 100644 --- a/tools/perf/tests/attr/test-record-group-sampling +++ b/tools/perf/tests/attr/test-record-group-sampling | |||
| @@ -37,4 +37,3 @@ sample_freq=0 | |||
| 37 | sample_period=0 | 37 | sample_period=0 |
| 38 | freq=0 | 38 | freq=0 |
| 39 | write_backward=0 | 39 | write_backward=0 |
| 40 | sample_id_all=0 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index e88e6f9b1463..668d2a9ef0f4 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -1810,3 +1810,30 @@ void perf_evlist__force_leader(struct perf_evlist *evlist) | |||
| 1810 | leader->forced_leader = true; | 1810 | leader->forced_leader = true; |
| 1811 | } | 1811 | } |
| 1812 | } | 1812 | } |
| 1813 | |||
| 1814 | struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list, | ||
| 1815 | struct perf_evsel *evsel) | ||
| 1816 | { | ||
| 1817 | struct perf_evsel *c2, *leader; | ||
| 1818 | bool is_open = true; | ||
| 1819 | |||
| 1820 | leader = evsel->leader; | ||
| 1821 | pr_debug("Weak group for %s/%d failed\n", | ||
| 1822 | leader->name, leader->nr_members); | ||
| 1823 | |||
| 1824 | /* | ||
| 1825 | * for_each_group_member doesn't work here because it doesn't | ||
| 1826 | * include the first entry. | ||
| 1827 | */ | ||
| 1828 | evlist__for_each_entry(evsel_list, c2) { | ||
| 1829 | if (c2 == evsel) | ||
| 1830 | is_open = false; | ||
| 1831 | if (c2->leader == leader) { | ||
| 1832 | if (is_open) | ||
| 1833 | perf_evsel__close(c2); | ||
| 1834 | c2->leader = c2; | ||
| 1835 | c2->nr_members = 0; | ||
| 1836 | } | ||
| 1837 | } | ||
| 1838 | return leader; | ||
| 1839 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index dc66436add98..9919eed6d15b 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -312,4 +312,7 @@ bool perf_evlist__exclude_kernel(struct perf_evlist *evlist); | |||
| 312 | 312 | ||
| 313 | void perf_evlist__force_leader(struct perf_evlist *evlist); | 313 | void perf_evlist__force_leader(struct perf_evlist *evlist); |
| 314 | 314 | ||
| 315 | struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evlist, | ||
| 316 | struct perf_evsel *evsel); | ||
| 317 | |||
| 315 | #endif /* __PERF_EVLIST_H */ | 318 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 6d187059a373..d37bb1566cd9 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -956,7 +956,6 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
| 956 | attr->sample_freq = 0; | 956 | attr->sample_freq = 0; |
| 957 | attr->sample_period = 0; | 957 | attr->sample_period = 0; |
| 958 | attr->write_backward = 0; | 958 | attr->write_backward = 0; |
| 959 | attr->sample_id_all = 0; | ||
| 960 | } | 959 | } |
| 961 | 960 | ||
| 962 | if (opts->no_samples) | 961 | if (opts->no_samples) |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 58f6a9ceb590..4503f3ca45ab 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
| @@ -1474,6 +1474,8 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder) | |||
| 1474 | decoder->have_calc_cyc_to_tsc = false; | 1474 | decoder->have_calc_cyc_to_tsc = false; |
| 1475 | intel_pt_calc_cyc_to_tsc(decoder, true); | 1475 | intel_pt_calc_cyc_to_tsc(decoder, true); |
| 1476 | } | 1476 | } |
| 1477 | |||
| 1478 | intel_pt_log_to("Setting timestamp", decoder->timestamp); | ||
| 1477 | } | 1479 | } |
| 1478 | 1480 | ||
| 1479 | static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder) | 1481 | static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder) |
| @@ -1514,6 +1516,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder) | |||
| 1514 | decoder->timestamp = timestamp; | 1516 | decoder->timestamp = timestamp; |
| 1515 | 1517 | ||
| 1516 | decoder->timestamp_insn_cnt = 0; | 1518 | decoder->timestamp_insn_cnt = 0; |
| 1519 | |||
| 1520 | intel_pt_log_to("Setting timestamp", decoder->timestamp); | ||
| 1517 | } | 1521 | } |
| 1518 | 1522 | ||
| 1519 | /* Walk PSB+ packets when already in sync. */ | 1523 | /* Walk PSB+ packets when already in sync. */ |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.c b/tools/perf/util/intel-pt-decoder/intel-pt-log.c index e02bc7b166a0..5e64da270f97 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c | |||
| @@ -31,6 +31,11 @@ static FILE *f; | |||
| 31 | static char log_name[MAX_LOG_NAME]; | 31 | static char log_name[MAX_LOG_NAME]; |
| 32 | bool intel_pt_enable_logging; | 32 | bool intel_pt_enable_logging; |
| 33 | 33 | ||
| 34 | void *intel_pt_log_fp(void) | ||
| 35 | { | ||
| 36 | return f; | ||
| 37 | } | ||
| 38 | |||
| 34 | void intel_pt_log_enable(void) | 39 | void intel_pt_log_enable(void) |
| 35 | { | 40 | { |
| 36 | intel_pt_enable_logging = true; | 41 | intel_pt_enable_logging = true; |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.h b/tools/perf/util/intel-pt-decoder/intel-pt-log.h index 45b64f93f358..cc084937f701 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.h | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | 22 | ||
| 23 | struct intel_pt_pkt; | 23 | struct intel_pt_pkt; |
| 24 | 24 | ||
| 25 | void *intel_pt_log_fp(void); | ||
| 25 | void intel_pt_log_enable(void); | 26 | void intel_pt_log_enable(void); |
| 26 | void intel_pt_log_disable(void); | 27 | void intel_pt_log_disable(void); |
| 27 | void intel_pt_log_set_name(const char *name); | 28 | void intel_pt_log_set_name(const char *name); |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 86cc9a64e982..149ff361ca78 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
| @@ -206,6 +206,16 @@ static void intel_pt_dump_event(struct intel_pt *pt, unsigned char *buf, | |||
| 206 | intel_pt_dump(pt, buf, len); | 206 | intel_pt_dump(pt, buf, len); |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | static void intel_pt_log_event(union perf_event *event) | ||
| 210 | { | ||
| 211 | FILE *f = intel_pt_log_fp(); | ||
| 212 | |||
| 213 | if (!intel_pt_enable_logging || !f) | ||
| 214 | return; | ||
| 215 | |||
| 216 | perf_event__fprintf(event, f); | ||
| 217 | } | ||
| 218 | |||
| 209 | static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a, | 219 | static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a, |
| 210 | struct auxtrace_buffer *b) | 220 | struct auxtrace_buffer *b) |
| 211 | { | 221 | { |
| @@ -2010,9 +2020,9 @@ static int intel_pt_process_event(struct perf_session *session, | |||
| 2010 | event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) | 2020 | event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) |
| 2011 | err = intel_pt_context_switch(pt, event, sample); | 2021 | err = intel_pt_context_switch(pt, event, sample); |
| 2012 | 2022 | ||
| 2013 | intel_pt_log("event %s (%u): cpu %d time %"PRIu64" tsc %#"PRIx64"\n", | 2023 | intel_pt_log("event %u: cpu %d time %"PRIu64" tsc %#"PRIx64" ", |
| 2014 | perf_event__name(event->header.type), event->header.type, | 2024 | event->header.type, sample->cpu, sample->time, timestamp); |
| 2015 | sample->cpu, sample->time, timestamp); | 2025 | intel_pt_log_event(event); |
| 2016 | 2026 | ||
| 2017 | return err; | 2027 | return err; |
| 2018 | } | 2028 | } |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 7799788f662f..7e49baad304d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
| @@ -773,7 +773,7 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) | |||
| 773 | 773 | ||
| 774 | if (!is_arm_pmu_core(name)) { | 774 | if (!is_arm_pmu_core(name)) { |
| 775 | pname = pe->pmu ? pe->pmu : "cpu"; | 775 | pname = pe->pmu ? pe->pmu : "cpu"; |
| 776 | if (strncmp(pname, name, strlen(pname))) | 776 | if (strcmp(pname, name)) |
| 777 | continue; | 777 | continue; |
| 778 | } | 778 | } |
| 779 | 779 | ||
