Merge tag 'perf-urgent-for-mingo-4.19-20180903' of git://git.kernel.org/pub/scm/linux...
authorIngo Molnar <mingo@kernel.org>
Sun, 9 Sep 2018 19:36:31 +0000 (21:36 +0200)
committerIngo Molnar <mingo@kernel.org>
Sun, 9 Sep 2018 19:36:31 +0000 (21:36 +0200)
Pull perf/urgent fixes from Arnaldo Carvalho de Melo:

Kernel:

- Modify breakpoint fixes (Jiri Olsa)

perf annotate:

- Fix parsing aarch64 branch instructions after objdump update (Kim Phillips)

- Fix parsing indirect calls in 'perf annotate' (Martin Liška)

perf probe:

- Ignore SyS symbols irrespective of endianness on PowerPC (Sandipan Das)

perf trace:

- Fix include path for asm-generic/unistd.h on arm64 (Kim Phillips)

Core libraries:

- Fix potential null pointer dereference in perf_evsel__new_idx() (Hisao Tanabe)

- Use fixed size string for comms instead of scanf("%m"), that is
  not present in the bionic libc and leads to a crash (Chris Phlipot)

- Fix bad memory access in trace info on 32-bit systems, we were reading
  8 bytes from a 4-byte long variable when saving the command line in the
  perf.data file.  (Chris Phlipot)

Build system:

- Streamline bpf examples and headers installation, clarifying
  some install messages. (Arnaldo Carvalho de Melo)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
15 files changed:
kernel/events/core.c
kernel/events/hw_breakpoint.c
tools/perf/Makefile.perf
tools/perf/arch/arm64/Makefile
tools/perf/arch/arm64/entry/syscalls/mksyscalltbl
tools/perf/arch/powerpc/util/sym-handling.c
tools/perf/arch/x86/include/arch-tests.h
tools/perf/arch/x86/tests/Build
tools/perf/arch/x86/tests/arch-tests.c
tools/perf/arch/x86/tests/bp-modify.c [new file with mode: 0644]
tools/perf/util/annotate.c
tools/perf/util/annotate.h
tools/perf/util/evsel.c
tools/perf/util/trace-event-info.c
tools/perf/util/trace-event-parse.c

index 2a62b96600ad91dede076a2dc8ad4e7fc16afe09..abaed4f8bb7f227bafdbc26b465df060a765eaf3 100644 (file)
@@ -2867,16 +2867,11 @@ static int perf_event_modify_breakpoint(struct perf_event *bp,
        _perf_event_disable(bp);
 
        err = modify_user_hw_breakpoint_check(bp, attr, true);
-       if (err) {
-               if (!bp->attr.disabled)
-                       _perf_event_enable(bp);
 
-               return err;
-       }
-
-       if (!attr->disabled)
+       if (!bp->attr.disabled)
                _perf_event_enable(bp);
-       return 0;
+
+       return err;
 }
 
 static int perf_event_modify_attr(struct perf_event *event,
index b3814fce5ecb6bf7729ea858ec17ddb9018105f1..d6b56180827c73f29fa21eef238c73c1a01540ad 100644 (file)
@@ -509,6 +509,8 @@ modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *a
  */
 int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
 {
+       int err;
+
        /*
         * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it
         * will not be possible to raise IPIs that invoke __perf_event_disable.
@@ -520,15 +522,12 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
        else
                perf_event_disable(bp);
 
-       if (!attr->disabled) {
-               int err = modify_user_hw_breakpoint_check(bp, attr, false);
+       err = modify_user_hw_breakpoint_check(bp, attr, false);
 
-               if (err)
-                       return err;
+       if (!bp->attr.disabled)
                perf_event_enable(bp);
-               bp->attr.disabled = 0;
-       }
-       return 0;
+
+       return err;
 }
 EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
 
index b3d1b12a5081ba10a92d19c79b38c6fb4654a597..5224ade3d5afed19b93a811a162a3ae6012c7ee7 100644 (file)
@@ -777,14 +777,12 @@ endif
        $(call QUIET_INSTALL, libexec) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
 ifndef NO_LIBBPF
-       $(call QUIET_INSTALL, lib) \
-               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'
-       $(call QUIET_INSTALL, include/bpf) \
-               $(INSTALL) include/bpf/*.h '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'
-       $(call QUIET_INSTALL, lib) \
-               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'
-       $(call QUIET_INSTALL, examples/bpf) \
-               $(INSTALL) examples/bpf/*.c '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'
+       $(call QUIET_INSTALL, bpf-headers) \
+               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'; \
+               $(INSTALL) include/bpf/*.h -t '$(DESTDIR_SQ)$(perf_include_instdir_SQ)/bpf'
+       $(call QUIET_INSTALL, bpf-examples) \
+               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'; \
+               $(INSTALL) examples/bpf/*.c -t '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'
 endif
        $(call QUIET_INSTALL, perf-archive) \
                $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
index f013b115dc860d001120233df6d912ad35c842ec..dbef716a19135fffaf2afb1adedc418a3effd420 100644 (file)
@@ -11,7 +11,8 @@ PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
 
 out    := $(OUTPUT)arch/arm64/include/generated/asm
 header := $(out)/syscalls.c
-sysdef := $(srctree)/tools/include/uapi/asm-generic/unistd.h
+incpath := $(srctree)/tools
+sysdef := $(srctree)/tools/arch/arm64/include/uapi/asm/unistd.h
 sysprf := $(srctree)/tools/perf/arch/arm64/entry/syscalls/
 systbl := $(sysprf)/mksyscalltbl
 
@@ -19,7 +20,7 @@ systbl := $(sysprf)/mksyscalltbl
 _dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)')
 
 $(header): $(sysdef) $(systbl)
-       $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(sysdef) > $@
+       $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@
 
 clean::
        $(call QUIET_CLEAN, arm64) $(RM) $(header)
index 52e197317d3ee22b82ced11a0b2ee4a50dd4b837..2dbb8cade048f76b4b43d88d5d9e27c09e025e0f 100755 (executable)
@@ -11,7 +11,8 @@
 
 gcc=$1
 hostcc=$2
-input=$3
+incpath=$3
+input=$4
 
 if ! test -r $input; then
        echo "Could not read input file" >&2
@@ -28,7 +29,6 @@ create_table_from_c()
 
        cat <<-_EoHEADER
                #include <stdio.h>
-               #define __ARCH_WANT_RENAMEAT
                #include "$input"
                int main(int argc, char *argv[])
                {
@@ -42,7 +42,7 @@ create_table_from_c()
        printf "%s\n" " printf(\"#define SYSCALLTBL_ARM64_MAX_ID %d\\n\", __NR_$last_sc);"
        printf "}\n"
 
-       } | $hostcc -o $create_table_exe -x c -
+       } | $hostcc -I $incpath/include/uapi -o $create_table_exe -x c -
 
        $create_table_exe
 
index 20e7d74d86cd16e86c8fd60e5839222d476f6409..10a44e946f7734b911ed00f74184f754b09d56ba 100644 (file)
@@ -22,15 +22,16 @@ bool elf__needs_adjust_symbols(GElf_Ehdr ehdr)
 
 #endif
 
-#if !defined(_CALL_ELF) || _CALL_ELF != 2
 int arch__choose_best_symbol(struct symbol *syma,
                             struct symbol *symb __maybe_unused)
 {
        char *sym = syma->name;
 
+#if !defined(_CALL_ELF) || _CALL_ELF != 2
        /* Skip over any initial dot */
        if (*sym == '.')
                sym++;
+#endif
 
        /* Avoid "SyS" kernel syscall aliases */
        if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3))
@@ -41,6 +42,7 @@ int arch__choose_best_symbol(struct symbol *syma,
        return SYMBOL_A;
 }
 
+#if !defined(_CALL_ELF) || _CALL_ELF != 2
 /* Allow matching against dot variants */
 int arch__compare_symbol_names(const char *namea, const char *nameb)
 {
index c1bd979b957be76a2ff55f45d0d4011c056e0de7..613709cfbbd03d45e2c6254c2fb5a95f7f5415b5 100644 (file)
@@ -9,6 +9,7 @@ struct test;
 int test__rdpmc(struct test *test __maybe_unused, int subtest);
 int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest);
 int test__insn_x86(struct test *test __maybe_unused, int subtest);
+int test__bp_modify(struct test *test, int subtest);
 
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
 struct thread;
index 8e2c5a38c3b90c18c2159b8f91e54500734cd9f3..586849ff83a079468abc96294077c28b68a3e0c0 100644 (file)
@@ -5,3 +5,4 @@ libperf-y += arch-tests.o
 libperf-y += rdpmc.o
 libperf-y += perf-time-to-tsc.o
 libperf-$(CONFIG_AUXTRACE) += insn-x86.o
+libperf-$(CONFIG_X86_64) += bp-modify.o
index cc1802ff54109ebccd23130fcc37f023d6e5ae9c..d47d3f8e3c8e076111d47c6ca51252552df31117 100644 (file)
@@ -23,6 +23,12 @@ struct test arch_tests[] = {
                .desc = "x86 instruction decoder - new instructions",
                .func = test__insn_x86,
        },
+#endif
+#if defined(__x86_64__)
+       {
+               .desc = "x86 bp modify",
+               .func = test__bp_modify,
+       },
 #endif
        {
                .func = NULL,
diff --git a/tools/perf/arch/x86/tests/bp-modify.c b/tools/perf/arch/x86/tests/bp-modify.c
new file mode 100644 (file)
index 0000000..f53e440
--- /dev/null
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/compiler.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+#include <errno.h>
+#include "debug.h"
+#include "tests/tests.h"
+#include "arch-tests.h"
+
+static noinline int bp_1(void)
+{
+       pr_debug("in %s\n", __func__);
+       return 0;
+}
+
+static noinline int bp_2(void)
+{
+       pr_debug("in %s\n", __func__);
+       return 0;
+}
+
+static int spawn_child(void)
+{
+       int child = fork();
+
+       if (child == 0) {
+               /*
+                * The child sets itself for as tracee and
+                * waits in signal for parent to trace it,
+                * then it calls bp_1 and quits.
+                */
+               int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+
+               if (err) {
+                       pr_debug("failed to PTRACE_TRACEME\n");
+                       exit(1);
+               }
+
+               raise(SIGCONT);
+               bp_1();
+               exit(0);
+       }
+
+       return child;
+}
+
+/*
+ * This tests creates HW breakpoint, tries to
+ * change it and checks it was properly changed.
+ */
+static int bp_modify1(void)
+{
+       pid_t child;
+       int status;
+       unsigned long rip = 0, dr7 = 1;
+
+       child = spawn_child();
+
+       waitpid(child, &status, 0);
+       if (WIFEXITED(status)) {
+               pr_debug("tracee exited prematurely 1\n");
+               return TEST_FAIL;
+       }
+
+       /*
+        * The parent does following steps:
+        *  - creates a new breakpoint (id 0) for bp_2 function
+        *  - changes that breakponit to bp_1 function
+        *  - waits for the breakpoint to hit and checks
+        *    it has proper rip of bp_1 function
+        *  - detaches the child
+        */
+       if (ptrace(PTRACE_POKEUSER, child,
+                  offsetof(struct user, u_debugreg[0]), bp_2)) {
+               pr_debug("failed to set breakpoint, 1st time: %s\n",
+                        strerror(errno));
+               goto out;
+       }
+
+       if (ptrace(PTRACE_POKEUSER, child,
+                  offsetof(struct user, u_debugreg[0]), bp_1)) {
+               pr_debug("failed to set breakpoint, 2nd time: %s\n",
+                        strerror(errno));
+               goto out;
+       }
+
+       if (ptrace(PTRACE_POKEUSER, child,
+                  offsetof(struct user, u_debugreg[7]), dr7)) {
+               pr_debug("failed to set dr7: %s\n", strerror(errno));
+               goto out;
+       }
+
+       if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
+               pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
+               goto out;
+       }
+
+       waitpid(child, &status, 0);
+       if (WIFEXITED(status)) {
+               pr_debug("tracee exited prematurely 2\n");
+               return TEST_FAIL;
+       }
+
+       rip = ptrace(PTRACE_PEEKUSER, child,
+                    offsetof(struct user_regs_struct, rip), NULL);
+       if (rip == (unsigned long) -1) {
+               pr_debug("failed to PTRACE_PEEKUSER: %s\n",
+                        strerror(errno));
+               goto out;
+       }
+
+       pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
+
+out:
+       if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
+               pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
+               return TEST_FAIL;
+       }
+
+       return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
+}
+
+/*
+ * This tests creates HW breakpoint, tries to
+ * change it to bogus value and checks the original
+ * breakpoint is hit.
+ */
+static int bp_modify2(void)
+{
+       pid_t child;
+       int status;
+       unsigned long rip = 0, dr7 = 1;
+
+       child = spawn_child();
+
+       waitpid(child, &status, 0);
+       if (WIFEXITED(status)) {
+               pr_debug("tracee exited prematurely 1\n");
+               return TEST_FAIL;
+       }
+
+       /*
+        * The parent does following steps:
+        *  - creates a new breakpoint (id 0) for bp_1 function
+        *  - tries to change that breakpoint to (-1) address
+        *  - waits for the breakpoint to hit and checks
+        *    it has proper rip of bp_1 function
+        *  - detaches the child
+        */
+       if (ptrace(PTRACE_POKEUSER, child,
+                  offsetof(struct user, u_debugreg[0]), bp_1)) {
+               pr_debug("failed to set breakpoint: %s\n",
+                        strerror(errno));
+               goto out;
+       }
+
+       if (ptrace(PTRACE_POKEUSER, child,
+                  offsetof(struct user, u_debugreg[7]), dr7)) {
+               pr_debug("failed to set dr7: %s\n", strerror(errno));
+               goto out;
+       }
+
+       if (!ptrace(PTRACE_POKEUSER, child,
+                  offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
+               pr_debug("failed, breakpoint set to bogus address\n");
+               goto out;
+       }
+
+       if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
+               pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
+               goto out;
+       }
+
+       waitpid(child, &status, 0);
+       if (WIFEXITED(status)) {
+               pr_debug("tracee exited prematurely 2\n");
+               return TEST_FAIL;
+       }
+
+       rip = ptrace(PTRACE_PEEKUSER, child,
+                    offsetof(struct user_regs_struct, rip), NULL);
+       if (rip == (unsigned long) -1) {
+               pr_debug("failed to PTRACE_PEEKUSER: %s\n",
+                        strerror(errno));
+               goto out;
+       }
+
+       pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
+
+out:
+       if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
+               pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
+               return TEST_FAIL;
+       }
+
+       return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
+}
+
+int test__bp_modify(struct test *test __maybe_unused,
+                   int subtest __maybe_unused)
+{
+       TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
+       TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
+
+       return 0;
+}
index 20061cf4228875bb43e34a081da7224d69531f49..28cd6a17491b2077815ce0d9bb86f741f7a2be6e 100644 (file)
@@ -246,8 +246,14 @@ find_target:
 
 indirect_call:
        tok = strchr(endptr, '*');
-       if (tok != NULL)
-               ops->target.addr = strtoull(tok + 1, NULL, 16);
+       if (tok != NULL) {
+               endptr++;
+
+               /* Indirect call can use a non-rip register and offset: callq  *0x8(%rbx).
+                * Do not parse such instruction.  */
+               if (strstr(endptr, "(%r") == NULL)
+                       ops->target.addr = strtoull(endptr, NULL, 16);
+       }
        goto find_target;
 }
 
@@ -276,7 +282,19 @@ bool ins__is_call(const struct ins *ins)
        return ins->ops == &call_ops || ins->ops == &s390_call_ops;
 }
 
-static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms)
+/*
+ * Prevents from matching commas in the comment section, e.g.:
+ * ffff200008446e70:       b.cs    ffff2000084470f4 <generic_exec_single+0x314>  // b.hs, b.nlast
+ */
+static inline const char *validate_comma(const char *c, struct ins_operands *ops)
+{
+       if (ops->raw_comment && c > ops->raw_comment)
+               return NULL;
+
+       return c;
+}
+
+static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
 {
        struct map *map = ms->map;
        struct symbol *sym = ms->sym;
@@ -285,6 +303,10 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
        };
        const char *c = strchr(ops->raw, ',');
        u64 start, end;
+
+       ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char);
+       c = validate_comma(c, ops);
+
        /*
         * Examples of lines to parse for the _cpp_lex_token@@Base
         * function:
@@ -304,6 +326,7 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
                ops->target.addr = strtoull(c, NULL, 16);
                if (!ops->target.addr) {
                        c = strchr(c, ',');
+                       c = validate_comma(c, ops);
                        if (c++ != NULL)
                                ops->target.addr = strtoull(c, NULL, 16);
                }
@@ -361,9 +384,12 @@ static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
                return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name);
 
        c = strchr(ops->raw, ',');
+       c = validate_comma(c, ops);
+
        if (c != NULL) {
                const char *c2 = strchr(c + 1, ',');
 
+               c2 = validate_comma(c2, ops);
                /* check for 3-op insn */
                if (c2 != NULL)
                        c = c2;
index 005a5fe8a8c6bccc49ed7d6b1861952313ecb2df..5399ba2321bbb2c348e89fac2db9fed7c8d35384 100644 (file)
@@ -22,6 +22,7 @@ struct ins {
 
 struct ins_operands {
        char    *raw;
+       char    *raw_comment;
        struct {
                char    *raw;
                char    *name;
index c980bbff63536ebaba167d8ca045b65fc7060f56..1a61628a1c1262c86adff2d76c548985470d9e11 100644 (file)
@@ -251,8 +251,9 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
 {
        struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
 
-       if (evsel != NULL)
-               perf_evsel__init(evsel, attr, idx);
+       if (!evsel)
+               return NULL;
+       perf_evsel__init(evsel, attr, idx);
 
        if (perf_evsel__is_bpf_output(evsel)) {
                evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
index c85d0d1a65ed72ffbdf2195004f0cd106602dde7..7b0ca7cbb7de852433a2dbd7494789096b555edd 100644 (file)
@@ -377,7 +377,7 @@ out:
 
 static int record_saved_cmdline(void)
 {
-       unsigned int size;
+       unsigned long long size;
        char *path;
        struct stat st;
        int ret, err = 0;
index 920b1d58a06899d6cecf6aea2aa30abedc95695d..e76214f8d596bc97c71390f1c82c5f75ab2fd607 100644 (file)
@@ -164,16 +164,15 @@ void parse_ftrace_printk(struct tep_handle *pevent,
 void parse_saved_cmdline(struct tep_handle *pevent,
                         char *file, unsigned int size __maybe_unused)
 {
-       char *comm;
+       char comm[17]; /* Max comm length in the kernel is 16. */
        char *line;
        char *next = NULL;
        int pid;
 
        line = strtok_r(file, "\n", &next);
        while (line) {
-               sscanf(line, "%d %ms", &pid, &comm);
-               tep_register_comm(pevent, comm, pid);
-               free(comm);
+               if (sscanf(line, "%d %16s", &pid, comm) == 2)
+                       tep_register_comm(pevent, comm, pid);
                line = strtok_r(NULL, "\n", &next);
        }
 }