Merge tag 'perf-urgent-for-mingo-4.20-20181106' of git://git.kernel.org/pub/scm/linux...
authorIngo Molnar <mingo@kernel.org>
Tue, 6 Nov 2018 19:03:11 +0000 (20:03 +0100)
committerIngo Molnar <mingo@kernel.org>
Tue, 6 Nov 2018 19:03:11 +0000 (20:03 +0100)
Pull perf/urgent improvements and fixes from Arnaldo Carvalho de Melo:

Intel PT SQL viewer: (Adrian Hunter)

- Fall back to /usr/local/lib/libxed.so
- Add Selected branches report
- Add help window
- Fix table find when table re-ordered

Intel PT debug log (Adrian Hunter)

- Add more event information
- Add MTC and CYC timestamps

perf record: (Andi Kleen)

- Support weak groups, just like with 'perf stat'

perf trace: (Arnaldo Carvalho de Melo)

- Start augmenting raw_syscalls:{sys_enter,sys_exit}: goal is to have a
  generic, arch independent eBPF kernel component that is programmed with
  syscall table details, what to copy, how many bytes, pid, arg filters from the
  userspace via eBPF maps by the 'perf trace' tool that continues to use all its
  argument beautifiers, just taking advantage of the extra pointer contents.

JVMTI: (Gustavo Romero)

- Fix undefined symbol scnprintf in libperf-jvmti.so

perf top: (Jin Yao)

- Display the LBR stats in callchain entries

perf stat: (Thomas Richter)

- Handle different PMU names with common prefix

arm64: Will (Deacon)

- Fix arm64 tools build failure wrt smp_load_{acquire,release}.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
19 files changed:
tools/arch/arm64/include/asm/barrier.h
tools/perf/Documentation/perf-list.txt
tools/perf/Makefile.perf
tools/perf/builtin-record.c
tools/perf/builtin-stat.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/examples/bpf/augmented_raw_syscalls.c [new file with mode: 0644]
tools/perf/jvmti/jvmti_agent.c
tools/perf/scripts/python/exported-sql-viewer.py
tools/perf/tests/attr/test-record-group-sampling
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
tools/perf/util/intel-pt-decoder/intel-pt-log.c
tools/perf/util/intel-pt-decoder/intel-pt-log.h
tools/perf/util/intel-pt.c
tools/perf/util/pmu.c

index 12835ea0e4173c281e5ddfe0883595492e985b09..378c051fa1776534b0e1fca7e56567f9f8a3d2d4 100644 (file)
 #define wmb()          asm volatile("dmb ishst" ::: "memory")
 #define rmb()          asm volatile("dmb ishld" ::: "memory")
 
-#define smp_store_release(p, v)                                        \
-do {                                                           \
-       union { typeof(*p) __val; char __c[1]; } __u =          \
-               { .__val = (__force typeof(*p)) (v) };          \
-                                                               \
-       switch (sizeof(*p)) {                                   \
-       case 1:                                                 \
-               asm volatile ("stlrb %w1, %0"                   \
-                               : "=Q" (*p)                     \
-                               : "r" (*(__u8 *)__u.__c)        \
-                               : "memory");                    \
-               break;                                          \
-       case 2:                                                 \
-               asm volatile ("stlrh %w1, %0"                   \
-                               : "=Q" (*p)                     \
-                               : "r" (*(__u16 *)__u.__c)       \
-                               : "memory");                    \
-               break;                                          \
-       case 4:                                                 \
-               asm volatile ("stlr %w1, %0"                    \
-                               : "=Q" (*p)                     \
-                               : "r" (*(__u32 *)__u.__c)       \
-                               : "memory");                    \
-               break;                                          \
-       case 8:                                                 \
-               asm volatile ("stlr %1, %0"                     \
-                               : "=Q" (*p)                     \
-                               : "r" (*(__u64 *)__u.__c)       \
-                               : "memory");                    \
-               break;                                          \
-       default:                                                \
-               /* Only to shut up gcc ... */                   \
-               mb();                                           \
-               break;                                          \
-       }                                                       \
+#define smp_store_release(p, v)                                                \
+do {                                                                   \
+       union { typeof(*p) __val; char __c[1]; } __u =                  \
+               { .__val = (v) };                                       \
+                                                                       \
+       switch (sizeof(*p)) {                                           \
+       case 1:                                                         \
+               asm volatile ("stlrb %w1, %0"                           \
+                               : "=Q" (*p)                             \
+                               : "r" (*(__u8_alias_t *)__u.__c)        \
+                               : "memory");                            \
+               break;                                                  \
+       case 2:                                                         \
+               asm volatile ("stlrh %w1, %0"                           \
+                               : "=Q" (*p)                             \
+                               : "r" (*(__u16_alias_t *)__u.__c)       \
+                               : "memory");                            \
+               break;                                                  \
+       case 4:                                                         \
+               asm volatile ("stlr %w1, %0"                            \
+                               : "=Q" (*p)                             \
+                               : "r" (*(__u32_alias_t *)__u.__c)       \
+                               : "memory");                            \
+               break;                                                  \
+       case 8:                                                         \
+               asm volatile ("stlr %1, %0"                             \
+                               : "=Q" (*p)                             \
+                               : "r" (*(__u64_alias_t *)__u.__c)       \
+                               : "memory");                            \
+               break;                                                  \
+       default:                                                        \
+               /* Only to shut up gcc ... */                           \
+               mb();                                                   \
+               break;                                                  \
+       }                                                               \
 } while (0)
 
-#define smp_load_acquire(p)                                    \
-({                                                             \
-       union { typeof(*p) __val; char __c[1]; } __u;           \
-                                                               \
-       switch (sizeof(*p)) {                                   \
-       case 1:                                                 \
-               asm volatile ("ldarb %w0, %1"                   \
-                       : "=r" (*(__u8 *)__u.__c)               \
-                       : "Q" (*p) : "memory");                 \
-               break;                                          \
-       case 2:                                                 \
-               asm volatile ("ldarh %w0, %1"                   \
-                       : "=r" (*(__u16 *)__u.__c)              \
-                       : "Q" (*p) : "memory");                 \
-               break;                                          \
-       case 4:                                                 \
-               asm volatile ("ldar %w0, %1"                    \
-                       : "=r" (*(__u32 *)__u.__c)              \
-                       : "Q" (*p) : "memory");                 \
-               break;                                          \
-       case 8:                                                 \
-               asm volatile ("ldar %0, %1"                     \
-                       : "=r" (*(__u64 *)__u.__c)              \
-                       : "Q" (*p) : "memory");                 \
-               break;                                          \
-       default:                                                \
-               /* Only to shut up gcc ... */                   \
-               mb();                                           \
-               break;                                          \
-       }                                                       \
-       __u.__val;                                              \
+#define smp_load_acquire(p)                                            \
+({                                                                     \
+       union { typeof(*p) __val; char __c[1]; } __u =                  \
+               { .__c = { 0 } };                                       \
+                                                                       \
+       switch (sizeof(*p)) {                                           \
+       case 1:                                                         \
+               asm volatile ("ldarb %w0, %1"                           \
+                       : "=r" (*(__u8_alias_t *)__u.__c)               \
+                       : "Q" (*p) : "memory");                         \
+               break;                                                  \
+       case 2:                                                         \
+               asm volatile ("ldarh %w0, %1"                           \
+                       : "=r" (*(__u16_alias_t *)__u.__c)              \
+                       : "Q" (*p) : "memory");                         \
+               break;                                                  \
+       case 4:                                                         \
+               asm volatile ("ldar %w0, %1"                            \
+                       : "=r" (*(__u32_alias_t *)__u.__c)              \
+                       : "Q" (*p) : "memory");                         \
+               break;                                                  \
+       case 8:                                                         \
+               asm volatile ("ldar %0, %1"                             \
+                       : "=r" (*(__u64_alias_t *)__u.__c)              \
+                       : "Q" (*p) : "memory");                         \
+               break;                                                  \
+       default:                                                        \
+               /* Only to shut up gcc ... */                           \
+               mb();                                                   \
+               break;                                                  \
+       }                                                               \
+       __u.__val;                                                      \
 })
 
 #endif /* _TOOLS_LINUX_ASM_AARCH64_BARRIER_H */
index 236b9b97dfdb1d5d52d6cc9dcb9198cfd2ec1739..667c14e56031b5c3ac91c291aed3c876644d1247 100644 (file)
@@ -55,7 +55,6 @@ counted. The following modifiers exist:
  S - read sample value (PERF_SAMPLE_READ)
  D - pin the event to the PMU
  W - group is weak and will fallback to non-group if not schedulable,
-     only supported in 'perf stat' for now.
 
 The 'p' modifier can be used for specifying how precise the instruction
 address should be. The 'p' modifier can be specified multiple times:
index 3ccb4f0bf0883cd80a8b33f612c649e84aaec126..d95655489f7e17adcd16dcca6a772875f6d1c6b2 100644 (file)
@@ -387,7 +387,7 @@ SHELL = $(SHELL_PATH)
 
 linux_uapi_dir := $(srctree)/tools/include/uapi/linux
 asm_generic_uapi_dir := $(srctree)/tools/include/uapi/asm-generic
-arch_asm_uapi_dir := $(srctree)/tools/arch/$(ARCH)/include/uapi/asm/
+arch_asm_uapi_dir := $(srctree)/tools/arch/$(SRCARCH)/include/uapi/asm/
 
 beauty_outdir := $(OUTPUT)trace/beauty/generated
 beauty_ioctl_outdir := $(beauty_outdir)/ioctl
index 10cf889c6d75d2db1d78014215e30948ae3b1cb4..488779bc4c8d2f6ed8dbcad69e1de5e477ede138 100644 (file)
@@ -391,7 +391,12 @@ try_again:
                                        ui__warning("%s\n", msg);
                                goto try_again;
                        }
-
+                       if ((errno == EINVAL || errno == EBADF) &&
+                           pos->leader != pos &&
+                           pos->weak_group) {
+                               pos = perf_evlist__reset_weak_group(evlist, pos);
+                               goto try_again;
+                       }
                        rc = -errno;
                        perf_evsel__open_strerror(pos, &opts->target,
                                                  errno, msg, sizeof(msg));
index d1028d7755bbcb946a517c333a3757d91e75c8ed..a635abfa77b6a9e38fa7aa994ea4cfd31608f16a 100644 (file)
@@ -383,32 +383,6 @@ static bool perf_evsel__should_store_id(struct perf_evsel *counter)
        return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID;
 }
 
-static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel)
-{
-       struct perf_evsel *c2, *leader;
-       bool is_open = true;
-
-       leader = evsel->leader;
-       pr_debug("Weak group for %s/%d failed\n",
-                       leader->name, leader->nr_members);
-
-       /*
-        * for_each_group_member doesn't work here because it doesn't
-        * include the first entry.
-        */
-       evlist__for_each_entry(evsel_list, c2) {
-               if (c2 == evsel)
-                       is_open = false;
-               if (c2->leader == leader) {
-                       if (is_open)
-                               perf_evsel__close(c2);
-                       c2->leader = c2;
-                       c2->nr_members = 0;
-               }
-       }
-       return leader;
-}
-
 static bool is_target_alive(struct target *_target,
                            struct thread_map *threads)
 {
@@ -477,7 +451,7 @@ try_again:
                        if ((errno == EINVAL || errno == EBADF) &&
                            counter->leader != counter &&
                            counter->weak_group) {
-                               counter = perf_evsel__reset_weak_group(counter);
+                               counter = perf_evlist__reset_weak_group(evsel_list, counter);
                                goto try_again;
                        }
 
index b2838de13de02e29f6ad766eb8de11ec27bfa31b..aa0c73e5792404355c5e8c2de048ff8e5da18e39 100644 (file)
@@ -1429,6 +1429,9 @@ int cmd_top(int argc, const char **argv)
                }
        }
 
+       if (opts->branch_stack && callchain_param.enabled)
+               symbol_conf.show_branchflag_count = true;
+
        sort__mode = SORT_MODE__TOP;
        /* display thread wants entries to be collapsed in a different tree */
        perf_hpp_list.need_collapse = 1;
index dc8a6c4986ce2066b0e76cd58e0edb2f7f09852a..835619476370cc0ae1d43bae705597f819a4c732 100644 (file)
@@ -108,6 +108,7 @@ struct trace {
        } stats;
        unsigned int            max_stack;
        unsigned int            min_stack;
+       bool                    raw_augmented_syscalls;
        bool                    not_ev_qualifier;
        bool                    live;
        bool                    full_time;
@@ -1724,13 +1725,28 @@ static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel,
        return printed;
 }
 
-static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size)
+static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size, bool raw_augmented)
 {
        void *augmented_args = NULL;
+       /*
+        * For now with BPF raw_augmented we hook into raw_syscalls:sys_enter
+        * and there we get all 6 syscall args plus the tracepoint common
+        * fields (sizeof(long)) and the syscall_nr (another long). So we check
+        * if that is the case and if so don't look after the sc->args_size,
+        * but always after the full raw_syscalls:sys_enter payload, which is
+        * fixed.
+        *
+        * We'll revisit this later to pass s->args_size to the BPF augmenter
+        * (now tools/perf/examples/bpf/augmented_raw_syscalls.c, so that it
+        * copies only what we need for each syscall, like what happens when we
+        * use syscalls:sys_enter_NAME, so that we reduce the kernel/userspace
+        * traffic to just what is needed for each syscall.
+        */
+       int args_size = raw_augmented ? (8 * (int)sizeof(long)) : sc->args_size;
 
-       *augmented_args_size = sample->raw_size - sc->args_size;
+       *augmented_args_size = sample->raw_size - args_size;
        if (*augmented_args_size > 0)
-               augmented_args = sample->raw_data + sc->args_size;
+               augmented_args = sample->raw_data + args_size;
 
        return augmented_args;
 }
@@ -1780,7 +1796,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
         * here and avoid using augmented syscalls when the evsel is the raw_syscalls one.
         */
        if (evsel != trace->syscalls.events.sys_enter)
-               augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size);
+               augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls);
        ttrace->entry_time = sample->time;
        msg = ttrace->entry_str;
        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
                goto out_put;
 
        args = perf_evsel__sc_tp_ptr(evsel, args, sample);
-       augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size);
+       augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls);
        syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread);
        fprintf(trace->output, "%s", msg);
        err = 0;
@@ -3501,7 +3517,15 @@ int cmd_trace(int argc, const char **argv)
                evsel->handler = trace__sys_enter;
 
                evlist__for_each_entry(trace.evlist, evsel) {
+                       bool raw_syscalls_sys_exit = strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_exit") == 0;
+
+                       if (raw_syscalls_sys_exit) {
+                               trace.raw_augmented_syscalls = true;
+                               goto init_augmented_syscall_tp;
+                       }
+
                        if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) {
+init_augmented_syscall_tp:
                                perf_evsel__init_augmented_syscall_tp(evsel);
                                perf_evsel__init_augmented_syscall_tp_ret(evsel);
                                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 (file)
index 0000000..90a1933
--- /dev/null
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Augment the raw_syscalls tracepoints with the contents of the pointer arguments.
+ *
+ * Test it with:
+ *
+ * perf trace -e tools/perf/examples/bpf/augmented_raw_syscalls.c cat /etc/passwd > /dev/null
+ *
+ * This exactly matches what is marshalled into the raw_syscall:sys_enter
+ * payload expected by the 'perf trace' beautifiers.
+ *
+ * For now it just uses the existing tracepoint augmentation code in 'perf
+ * trace', in the next csets we'll hook up these with the sys_enter/sys_exit
+ * code that will combine entry/exit in a strace like way.
+ */
+
+#include <stdio.h>
+#include <linux/socket.h>
+
+/* bpf-output associated map */
+struct bpf_map SEC("maps") __augmented_syscalls__ = {
+       .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(u32),
+       .max_entries = __NR_CPUS__,
+};
+
+struct syscall_enter_args {
+       unsigned long long common_tp_fields;
+       long               syscall_nr;
+       unsigned long      args[6];
+};
+
+struct syscall_exit_args {
+       unsigned long long common_tp_fields;
+       long               syscall_nr;
+       long               ret;
+};
+
+struct augmented_filename {
+       unsigned int    size;
+       int             reserved;
+       char            value[256];
+};
+
+#define SYS_OPEN 2
+#define SYS_OPENAT 257
+
+SEC("raw_syscalls:sys_enter")
+int sys_enter(struct syscall_enter_args *args)
+{
+       struct {
+               struct syscall_enter_args args;
+               struct augmented_filename filename;
+       } augmented_args;
+       unsigned int len = sizeof(augmented_args);
+       const void *filename_arg = NULL;
+
+       probe_read(&augmented_args.args, sizeof(augmented_args.args), args);
+       /*
+        * Yonghong and Edward Cree sayz:
+        *
+        * https://www.spinics.net/lists/netdev/msg531645.html
+        *
+        * >>   R0=inv(id=0) R1=inv2 R6=ctx(id=0,off=0,imm=0) R7=inv64 R10=fp0,call_-1
+        * >> 10: (bf) r1 = r6
+        * >> 11: (07) r1 += 16
+        * >> 12: (05) goto pc+2
+        * >> 15: (79) r3 = *(u64 *)(r1 +0)
+        * >> dereference of modified ctx ptr R1 off=16 disallowed
+        * > Aha, we at least got a different error message this time.
+        * > And indeed llvm has done that optimisation, rather than the more obvious
+        * > 11: r3 = *(u64 *)(r1 +16)
+        * > because it wants to have lots of reads share a single insn.  You may be able
+        * > to defeat that optimisation by adding compiler barriers, idk.  Maybe someone
+        * > with llvm knowledge can figure out how to stop it (ideally, llvm would know
+        * > when it's generating for bpf backend and not do that).  -O0?  ¯\_(ツ)_/¯
+        *
+        * The optimization mostly likes below:
+        *
+        *      br1:
+        *      ...
+        *      r1 += 16
+        *      goto merge
+        *      br2:
+        *      ...
+        *      r1 += 20
+        *      goto merge
+        *      merge:
+        *      *(u64 *)(r1 + 0)
+        *
+        * The compiler tries to merge common loads. There is no easy way to
+        * stop this compiler optimization without turning off a lot of other
+        * optimizations. The easiest way is to add barriers:
+        *
+        *       __asm__ __volatile__("": : :"memory")
+        *
+        *       after the ctx memory access to prevent their down stream merging.
+        */
+       switch (augmented_args.args.syscall_nr) {
+       case SYS_OPEN:   filename_arg = (const void *)args->args[0];
+                       __asm__ __volatile__("": : :"memory");
+                        break;
+       case SYS_OPENAT: filename_arg = (const void *)args->args[1];
+                        break;
+       }
+
+       if (filename_arg != NULL) {
+               augmented_args.filename.reserved = 0;
+               augmented_args.filename.size = probe_read_str(&augmented_args.filename.value,
+                                                             sizeof(augmented_args.filename.value),
+                                                             filename_arg);
+               if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) {
+                       len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size;
+                       len &= sizeof(augmented_args.filename.value) - 1;
+               }
+       } else {
+               len = sizeof(augmented_args.args);
+       }
+
+       perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, &augmented_args, len);
+       return 0;
+}
+
+SEC("raw_syscalls:sys_exit")
+int sys_exit(struct syscall_exit_args *args)
+{
+       return 1; /* 0 as soon as we start copying data returned by the kernel, e.g. 'read' */
+}
+
+license(GPL);
index ac1bcdc17dae7554f51a780b843605c441c6abbf..f7eb63cbbc655bdcebbd710ed38ce39a907db4bc 100644 (file)
@@ -125,7 +125,7 @@ perf_get_timestamp(void)
 }
 
 static int
-debug_cache_init(void)
+create_jit_cache_dir(void)
 {
        char str[32];
        char *base, *p;
@@ -144,8 +144,13 @@ debug_cache_init(void)
 
        strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm);
 
-       snprintf(jit_path, PATH_MAX - 1, "%s/.debug/", base);
-
+       ret = snprintf(jit_path, PATH_MAX, "%s/.debug/", base);
+       if (ret >= PATH_MAX) {
+               warnx("jvmti: cannot generate jit cache dir because %s/.debug/"
+                       " is too long, please check the cwd, JITDUMPDIR, and"
+                       " HOME variables", base);
+               return -1;
+       }
        ret = mkdir(jit_path, 0755);
        if (ret == -1) {
                if (errno != EEXIST) {
@@ -154,20 +159,32 @@ debug_cache_init(void)
                }
        }
 
-       snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit", base);
+       ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit", base);
+       if (ret >= PATH_MAX) {
+               warnx("jvmti: cannot generate jit cache dir because"
+                       " %s/.debug/jit is too long, please check the cwd,"
+                       " JITDUMPDIR, and HOME variables", base);
+               return -1;
+       }
        ret = mkdir(jit_path, 0755);
        if (ret == -1) {
                if (errno != EEXIST) {
-                       warn("cannot create jit cache dir %s", jit_path);
+                       warn("jvmti: cannot create jit cache dir %s", jit_path);
                        return -1;
                }
        }
 
-       snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit/%s.XXXXXXXX", base, str);
-
+       ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit/%s.XXXXXXXX", base, str);
+       if (ret >= PATH_MAX) {
+               warnx("jvmti: cannot generate jit cache dir because"
+                       " %s/.debug/jit/%s.XXXXXXXX is too long, please check"
+                       " the cwd, JITDUMPDIR, and HOME variables",
+                       base, str);
+               return -1;
+       }
        p = mkdtemp(jit_path);
        if (p != jit_path) {
-               warn("cannot create jit cache dir %s", jit_path);
+               warn("jvmti: cannot create jit cache dir %s", jit_path);
                return -1;
        }
 
@@ -228,7 +245,7 @@ void *jvmti_open(void)
 {
        char dump_path[PATH_MAX];
        struct jitheader header;
-       int fd;
+       int fd, ret;
        FILE *fp;
 
        init_arch_timestamp();
@@ -245,12 +262,22 @@ void *jvmti_open(void)
 
        memset(&header, 0, sizeof(header));
 
-       debug_cache_init();
+       /*
+        * jitdump file dir
+        */
+       if (create_jit_cache_dir() < 0)
+               return NULL;
 
        /*
         * jitdump file name
         */
-       scnprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid());
+       ret = snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid());
+       if (ret >= PATH_MAX) {
+               warnx("jvmti: cannot generate jitdump file full path because"
+                       " %s/jit-%i.dump is too long, please check the cwd,"
+                       " JITDUMPDIR, and HOME variables", jit_path, getpid());
+               return NULL;
+       }
 
        fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666);
        if (fd == -1)
index 24cb0bd56afa56bb4f0c4d48cefa4f7446681c66..f278ce5ebab76640de4eb61ee8661040d0ac092a 100755 (executable)
@@ -119,6 +119,14 @@ def dsoname(name):
                return "[kernel]"
        return name
 
+def findnth(s, sub, n, offs=0):
+       pos = s.find(sub)
+       if pos < 0:
+               return pos
+       if n <= 1:
+               return offs + pos
+       return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
+
 # Percent to one decimal place
 
 def PercentToOneDP(n, d):
@@ -1464,6 +1472,317 @@ class BranchWindow(QMdiSubWindow):
                else:
                        self.find_bar.NotFound()
 
+# Dialog data item converted and validated using a SQL table
+
+class SQLTableDialogDataItem():
+
+       def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
+               self.glb = glb
+               self.label = label
+               self.placeholder_text = placeholder_text
+               self.table_name = table_name
+               self.match_column = match_column
+               self.column_name1 = column_name1
+               self.column_name2 = column_name2
+               self.parent = parent
+
+               self.value = ""
+
+               self.widget = QLineEdit()
+               self.widget.editingFinished.connect(self.Validate)
+               self.widget.textChanged.connect(self.Invalidate)
+               self.red = False
+               self.error = ""
+               self.validated = True
+
+               self.last_id = 0
+               self.first_time = 0
+               self.last_time = 2 ** 64
+               if self.table_name == "<timeranges>":
+                       query = QSqlQuery(self.glb.db)
+                       QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
+                       if query.next():
+                               self.last_id = int(query.value(0))
+                               self.last_time = int(query.value(1))
+                       QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
+                       if query.next():
+                               self.first_time = int(query.value(0))
+                       if placeholder_text:
+                               placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
+
+               if placeholder_text:
+                       self.widget.setPlaceholderText(placeholder_text)
+
+       def ValueToIds(self, value):
+               ids = []
+               query = QSqlQuery(self.glb.db)
+               stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
+               ret = query.exec_(stmt)
+               if ret:
+                       while query.next():
+                               ids.append(str(query.value(0)))
+               return ids
+
+       def IdBetween(self, query, lower_id, higher_id, order):
+               QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
+               if query.next():
+                       return True, int(query.value(0))
+               else:
+                       return False, 0
+
+       def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
+               query = QSqlQuery(self.glb.db)
+               while True:
+                       next_id = int((lower_id + higher_id) / 2)
+                       QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
+                       if not query.next():
+                               ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
+                               if not ok:
+                                       ok, dbid = self.IdBetween(query, next_id, higher_id, "")
+                                       if not ok:
+                                               return str(higher_id)
+                               next_id = dbid
+                               QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
+                       next_time = int(query.value(0))
+                       if get_floor:
+                               if target_time > next_time:
+                                       lower_id = next_id
+                               else:
+                                       higher_id = next_id
+                               if higher_id <= lower_id + 1:
+                                       return str(higher_id)
+                       else:
+                               if target_time >= next_time:
+                                       lower_id = next_id
+                               else:
+                                       higher_id = next_id
+                               if higher_id <= lower_id + 1:
+                                       return str(lower_id)
+
+       def ConvertRelativeTime(self, val):
+               print "val ", val
+               mult = 1
+               suffix = val[-2:]
+               if suffix == "ms":
+                       mult = 1000000
+               elif suffix == "us":
+                       mult = 1000
+               elif suffix == "ns":
+                       mult = 1
+               else:
+                       return val
+               val = val[:-2].strip()
+               if not self.IsNumber(val):
+                       return val
+               val = int(val) * mult
+               if val >= 0:
+                       val += self.first_time
+               else:
+                       val += self.last_time
+               return str(val)
+
+       def ConvertTimeRange(self, vrange):
+               print "vrange ", vrange
+               if vrange[0] == "":
+                       vrange[0] = str(self.first_time)
+               if vrange[1] == "":
+                       vrange[1] = str(self.last_time)
+               vrange[0] = self.ConvertRelativeTime(vrange[0])
+               vrange[1] = self.ConvertRelativeTime(vrange[1])
+               print "vrange2 ", vrange
+               if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
+                       return False
+               print "ok1"
+               beg_range = max(int(vrange[0]), self.first_time)
+               end_range = min(int(vrange[1]), self.last_time)
+               if beg_range > self.last_time or end_range < self.first_time:
+                       return False
+               print "ok2"
+               vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
+               vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
+               print "vrange3 ", vrange
+               return True
+
+       def AddTimeRange(self, value, ranges):
+               print "value ", value
+               n = value.count("-")
+               if n == 1:
+                       pass
+               elif n == 2:
+                       if value.split("-")[1].strip() == "":
+                               n = 1
+               elif n == 3:
+                       n = 2
+               else:
+                       return False
+               pos = findnth(value, "-", n)
+               vrange = [value[:pos].strip() ,value[pos+1:].strip()]
+               if self.ConvertTimeRange(vrange):
+                       ranges.append(vrange)
+                       return True
+               return False
+
+       def InvalidValue(self, value):
+               self.value = ""
+               palette = QPalette()
+               palette.setColor(QPalette.Text,Qt.red)
+               self.widget.setPalette(palette)
+               self.red = True
+               self.error = self.label + " invalid value '" + value + "'"
+               self.parent.ShowMessage(self.error)
+
+       def IsNumber(self, value):
+               try:
+                       x = int(value)
+               except:
+                       x = 0
+               return str(x) == value
+
+       def Invalidate(self):
+               self.validated = False
+
+       def Validate(self):
+               input_string = self.widget.text()
+               self.validated = True
+               if self.red:
+                       palette = QPalette()
+                       self.widget.setPalette(palette)
+                       self.red = False
+               if not len(input_string.strip()):
+                       self.error = ""
+                       self.value = ""
+                       return
+               if self.table_name == "<timeranges>":
+                       ranges = []
+                       for value in [x.strip() for x in input_string.split(",")]:
+                               if not self.AddTimeRange(value, ranges):
+                                       return self.InvalidValue(value)
+                       ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
+                       self.value = " OR ".join(ranges)
+               elif self.table_name == "<ranges>":
+                       singles = []
+                       ranges = []
+                       for value in [x.strip() for x in input_string.split(",")]:
+                               if "-" in value:
+                                       vrange = value.split("-")
+                                       if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
+                                               return self.InvalidValue(value)
+                                       ranges.append(vrange)
+                               else:
+                                       if not self.IsNumber(value):
+                                               return self.InvalidValue(value)
+                                       singles.append(value)
+                       ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
+                       if len(singles):
+                               ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")")
+                       self.value = " OR ".join(ranges)
+               elif self.table_name:
+                       all_ids = []
+                       for value in [x.strip() for x in input_string.split(",")]:
+                               ids = self.ValueToIds(value)
+                               if len(ids):
+                                       all_ids.extend(ids)
+                               else:
+                                       return self.InvalidValue(value)
+                       self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
+                       if self.column_name2:
+                               self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
+               else:
+                       self.value = input_string.strip()
+               self.error = ""
+               self.parent.ClearMessage()
+
+       def IsValid(self):
+               if not self.validated:
+                       self.Validate()
+               if len(self.error):
+                       self.parent.ShowMessage(self.error)
+                       return False
+               return True
+
+# Selected branch report creation dialog
+
+class SelectedBranchDialog(QDialog):
+
+       def __init__(self, glb, parent=None):
+               super(SelectedBranchDialog, self).__init__(parent)
+
+               self.glb = glb
+
+               self.name = ""
+               self.where_clause = ""
+
+               self.setWindowTitle("Selected Branches")
+               self.setMinimumWidth(600)
+
+               items = (
+                       ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""),
+                       ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""),
+                       ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""),
+                       ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""),
+                       ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""),
+                       ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""),
+                       ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"),
+                       ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"),
+                       ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""),
+                       )
+               self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items]
+
+               self.grid = QGridLayout()
+
+               for row in xrange(len(self.data_items)):
+                       self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
+                       self.grid.addWidget(self.data_items[row].widget, row, 1)
+
+               self.status = QLabel()
+
+               self.ok_button = QPushButton("Ok", self)
+               self.ok_button.setDefault(True)
+               self.ok_button.released.connect(self.Ok)
+               self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
+
+               self.cancel_button = QPushButton("Cancel", self)
+               self.cancel_button.released.connect(self.reject)
+               self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
+
+               self.hbox = QHBoxLayout()
+               #self.hbox.addStretch()
+               self.hbox.addWidget(self.status)
+               self.hbox.addWidget(self.ok_button)
+               self.hbox.addWidget(self.cancel_button)
+
+               self.vbox = QVBoxLayout()
+               self.vbox.addLayout(self.grid)
+               self.vbox.addLayout(self.hbox)
+
+               self.setLayout(self.vbox);
+
+       def Ok(self):
+               self.name = self.data_items[0].value
+               if not self.name:
+                       self.ShowMessage("Report name is required")
+                       return
+               for d in self.data_items:
+                       if not d.IsValid():
+                               return
+               for d in self.data_items[1:]:
+                       if len(d.value):
+                               if len(self.where_clause):
+                                       self.where_clause += " AND "
+                               self.where_clause += d.value
+               if len(self.where_clause):
+                       self.where_clause = " AND ( " + self.where_clause + " ) "
+               else:
+                       self.ShowMessage("No selection")
+                       return
+               self.accept()
+
+       def ShowMessage(self, msg):
+               self.status.setText("<font color=#FF0000>" + msg)
+
+       def ClearMessage(self):
+               self.status.setText("")
+
 # Event list
 
 def GetEventList(db):
@@ -1656,7 +1975,7 @@ class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
        def FindDone(self, row):
                self.find_bar.Idle()
                if row >= 0:
-                       self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
+                       self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
                else:
                        self.find_bar.NotFound()
 
@@ -1765,6 +2084,149 @@ class WindowMenu():
        def setActiveSubWindow(self, nr):
                self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
 
+# Help text
+
+glb_help_text = """
+<h1>Contents</h1>
+<style>
+p.c1 {
+    text-indent: 40px;
+}
+p.c2 {
+    text-indent: 80px;
+}
+}
+</style>
+<p class=c1><a href=#reports>1. Reports</a></p>
+<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
+<p class=c2><a href=#allbranches>1.2 All branches</a></p>
+<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
+<p class=c1><a href=#tables>2. Tables</a></p>
+<h1 id=reports>1. Reports</h1>
+<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
+The result is a GUI window with a tree representing a context-sensitive
+call-graph. Expanding a couple of levels of the tree and adjusting column
+widths to suit will display something like:
+<pre>
+                                         Call Graph: pt_example
+Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
+v- ls
+    v- 2638:2638
+        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
+          |- unknown               unknown       1        13198     0.1              1              0.0
+          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
+          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
+          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
+             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
+             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
+             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
+             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
+             v- main               ls            1      8182043    99.6         180254             99.9
+</pre>
+<h3>Points to note:</h3>
+<ul>
+<li>The top level is a command name (comm)</li>
+<li>The next level is a thread (pid:tid)</li>
+<li>Subsequent levels are functions</li>
+<li>'Count' is the number of calls</li>
+<li>'Time' is the elapsed time until the function returns</li>
+<li>Percentages are relative to the level above</li>
+<li>'Branch Count' is the total number of branches for that function and all functions that it calls
+</ul>
+<h3>Find</h3>
+Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
+The pattern matching symbols are ? for any character and * for zero or more characters.
+<h2 id=allbranches>1.2 All branches</h2>
+The All branches report displays all branches in chronological order.
+Not all data is fetched immediately. More records can be fetched using the Fetch bar provided.
+<h3>Disassembly</h3>
+Open a branch to display disassembly. This only works if:
+<ol>
+<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
+<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
+The default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
+One exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
+or alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
+</ol>
+<h4 id=xed>Intel XED Setup</h4>
+To use Intel XED, libxed.so must be present.  To build and install libxed.so:
+<pre>
+git clone https://github.com/intelxed/mbuild.git mbuild
+git clone https://github.com/intelxed/xed
+cd xed
+./mfile.py --share
+sudo ./mfile.py --prefix=/usr/local install
+sudo ldconfig
+</pre>
+<h3>Find</h3>
+Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
+Refer to Python documentation for the regular expression syntax.
+All columns are searched, but only currently fetched rows are searched.
+<h2 id=selectedbranches>1.3 Selected branches</h2>
+This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
+by various selection criteria. A dialog box displays available criteria which are AND'ed together.
+<h3>1.3.1 Time ranges</h3>
+The time ranges hint text shows the total time range. Relative time ranges can also be entered in
+ms, us or ns. Also, negative values are relative to the end of trace.  Examples:
+<pre>
+       81073085947329-81073085958238   From 81073085947329 to 81073085958238
+       100us-200us             From 100us to 200us
+       10ms-                   From 10ms to the end
+       -100ns                  The first 100ns
+       -10ms-                  The last 10ms
+</pre>
+N.B. Due to the granularity of timestamps, there could be no branches in any given time range.
+<h1 id=tables>2. Tables</h1>
+The Tables menu shows all tables and views in the database. Most tables have an associated view
+which displays the information in a more friendly way. Not all data for large tables is fetched
+immediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
+but that can be slow for large tables.
+<p>There are also tables of database meta-information.
+For SQLite3 databases, the sqlite_master table is included.
+For PostgreSQL databases, information_schema.tables/views/columns are included.
+<h3>Find</h3>
+Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
+Refer to Python documentation for the regular expression syntax.
+All columns are searched, but only currently fetched rows are searched.
+<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
+will go to the next/previous result in id order, instead of display order.
+"""
+
+# Help window
+
+class HelpWindow(QMdiSubWindow):
+
+       def __init__(self, glb, parent=None):
+               super(HelpWindow, self).__init__(parent)
+
+               self.text = QTextBrowser()
+               self.text.setHtml(glb_help_text)
+               self.text.setReadOnly(True)
+               self.text.setOpenExternalLinks(True)
+
+               self.setWidget(self.text)
+
+               AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
+
+# Main window that only displays the help text
+
+class HelpOnlyWindow(QMainWindow):
+
+       def __init__(self, parent=None):
+               super(HelpOnlyWindow, self).__init__(parent)
+
+               self.setMinimumSize(200, 100)
+               self.resize(800, 600)
+               self.setWindowTitle("Exported SQL Viewer Help")
+               self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
+
+               self.text = QTextBrowser()
+               self.text.setHtml(glb_help_text)
+               self.text.setReadOnly(True)
+               self.text.setOpenExternalLinks(True)
+
+               self.setCentralWidget(self.text)
+
 # Font resize
 
 def ResizeFont(widget, diff):
@@ -1851,6 +2313,9 @@ class MainWindow(QMainWindow):
 
                self.window_menu = WindowMenu(self.mdi_area, menu)
 
+               help_menu = menu.addMenu("&Help")
+               help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
+
        def Find(self):
                win = self.mdi_area.activeSubWindow()
                if win:
@@ -1888,6 +2353,8 @@ class MainWindow(QMainWindow):
                        if event == "branches":
                                label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
                                reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
+                               label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
+                               reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
 
        def TableMenu(self, tables, menu):
                table_menu = menu.addMenu("&Tables")
@@ -1900,9 +2367,18 @@ class MainWindow(QMainWindow):
        def NewBranchView(self, event_id):
                BranchWindow(self.glb, event_id, "", "", self)
 
+       def NewSelectedBranchView(self, event_id):
+               dialog = SelectedBranchDialog(self.glb, self)
+               ret = dialog.exec_()
+               if ret:
+                       BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self)
+
        def NewTableView(self, table_name):
                TableWindow(self.glb, table_name, self)
 
+       def Help(self):
+               HelpWindow(self.glb, self)
+
 # XED Disassembler
 
 class xed_state_t(Structure):
@@ -1929,7 +2405,12 @@ class XEDInstruction():
 class LibXED():
 
        def __init__(self):
-               self.libxed = CDLL("libxed.so")
+               try:
+                       self.libxed = CDLL("libxed.so")
+               except:
+                       self.libxed = None
+               if not self.libxed:
+                       self.libxed = CDLL("/usr/local/lib/libxed.so")
 
                self.xed_tables_init = self.libxed.xed_tables_init
                self.xed_tables_init.restype = None
@@ -2097,10 +2578,16 @@ class DBRef():
 
 def Main():
        if (len(sys.argv) < 2):
-               print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>"
+               print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}"
                raise Exception("Too few arguments")
 
        dbname = sys.argv[1]
+       if dbname == "--help-only":
+               app = QApplication(sys.argv)
+               mainwindow = HelpOnlyWindow()
+               mainwindow.show()
+               err = app.exec_()
+               sys.exit(err)
 
        is_sqlite3 = False
        try:
index 8a33ca4f9e1f7feed87159d755ba3b4797a987b6..f0729c454f160bed941b16133b9ac437c973404d 100644 (file)
@@ -37,4 +37,3 @@ sample_freq=0
 sample_period=0
 freq=0
 write_backward=0
-sample_id_all=0
index e88e6f9b1463f0674a0eaf12423b35978e58d8da..668d2a9ef0f4b698231c7ad0388210175f0f8dab 100644 (file)
@@ -1810,3 +1810,30 @@ void perf_evlist__force_leader(struct perf_evlist *evlist)
                leader->forced_leader = true;
        }
 }
+
+struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
+                                                struct perf_evsel *evsel)
+{
+       struct perf_evsel *c2, *leader;
+       bool is_open = true;
+
+       leader = evsel->leader;
+       pr_debug("Weak group for %s/%d failed\n",
+                       leader->name, leader->nr_members);
+
+       /*
+        * for_each_group_member doesn't work here because it doesn't
+        * include the first entry.
+        */
+       evlist__for_each_entry(evsel_list, c2) {
+               if (c2 == evsel)
+                       is_open = false;
+               if (c2->leader == leader) {
+                       if (is_open)
+                               perf_evsel__close(c2);
+                       c2->leader = c2;
+                       c2->nr_members = 0;
+               }
+       }
+       return leader;
+}
index dc66436add98a3c795efa3ddf0889f09f1d7abe3..9919eed6d15bc1994844187d9b55e69b746312cc 100644 (file)
@@ -312,4 +312,7 @@ bool perf_evlist__exclude_kernel(struct perf_evlist *evlist);
 
 void perf_evlist__force_leader(struct perf_evlist *evlist);
 
+struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evlist,
+                                                struct perf_evsel *evsel);
+
 #endif /* __PERF_EVLIST_H */
index 6d187059a37360ae669220c913c535b1278d6514..d37bb1566cd9da7eb0875f83dcfa46edce36a552 100644 (file)
@@ -956,7 +956,6 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
                attr->sample_freq    = 0;
                attr->sample_period  = 0;
                attr->write_backward = 0;
-               attr->sample_id_all  = 0;
        }
 
        if (opts->no_samples)
index 58f6a9ceb5909c1007c4e7a95aa92230da6fff41..4503f3ca45ab48d7305260cf36e51800eecaf089 100644 (file)
@@ -1474,6 +1474,8 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder)
                decoder->have_calc_cyc_to_tsc = false;
                intel_pt_calc_cyc_to_tsc(decoder, true);
        }
+
+       intel_pt_log_to("Setting timestamp", decoder->timestamp);
 }
 
 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)
                decoder->timestamp = timestamp;
 
        decoder->timestamp_insn_cnt = 0;
+
+       intel_pt_log_to("Setting timestamp", decoder->timestamp);
 }
 
 /* Walk PSB+ packets when already in sync. */
index e02bc7b166a0e48f1cf537606a71ab43875c6de8..5e64da270f97684b7f42c56079a04a2653a1b890 100644 (file)
@@ -31,6 +31,11 @@ static FILE *f;
 static char log_name[MAX_LOG_NAME];
 bool intel_pt_enable_logging;
 
+void *intel_pt_log_fp(void)
+{
+       return f;
+}
+
 void intel_pt_log_enable(void)
 {
        intel_pt_enable_logging = true;
index 45b64f93f358898c6fb8d5caca92b37cd70f2e2e..cc084937f701acfa16bc3eb3623aab7e6ed429d2 100644 (file)
@@ -22,6 +22,7 @@
 
 struct intel_pt_pkt;
 
+void *intel_pt_log_fp(void);
 void intel_pt_log_enable(void);
 void intel_pt_log_disable(void);
 void intel_pt_log_set_name(const char *name);
index 86cc9a64e982773408e2d51cc25a51a82c396ff5..149ff361ca789e460cd896beaa439ff7b225c2a2 100644 (file)
@@ -206,6 +206,16 @@ static void intel_pt_dump_event(struct intel_pt *pt, unsigned char *buf,
        intel_pt_dump(pt, buf, len);
 }
 
+static void intel_pt_log_event(union perf_event *event)
+{
+       FILE *f = intel_pt_log_fp();
+
+       if (!intel_pt_enable_logging || !f)
+               return;
+
+       perf_event__fprintf(event, f);
+}
+
 static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a,
                                   struct auxtrace_buffer *b)
 {
@@ -2010,9 +2020,9 @@ static int intel_pt_process_event(struct perf_session *session,
                 event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
                err = intel_pt_context_switch(pt, event, sample);
 
-       intel_pt_log("event %s (%u): cpu %d time %"PRIu64" tsc %#"PRIx64"\n",
-                    perf_event__name(event->header.type), event->header.type,
-                    sample->cpu, sample->time, timestamp);
+       intel_pt_log("event %u: cpu %d time %"PRIu64" tsc %#"PRIx64" ",
+                    event->header.type, sample->cpu, sample->time, timestamp);
+       intel_pt_log_event(event);
 
        return err;
 }
index 7799788f662fdc05765915b383d13085f2a932ac..7e49baad304d78815966a6ac918f472fd817620b 100644 (file)
@@ -773,7 +773,7 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
 
                if (!is_arm_pmu_core(name)) {
                        pname = pe->pmu ? pe->pmu : "cpu";
-                       if (strncmp(pname, name, strlen(pname)))
+                       if (strcmp(pname, name))
                                continue;
                }