perf probe: Support SDT markers having reference counter (semaphore)
authorRavi Bangoria <ravi.bangoria@linux.ibm.com>
Mon, 20 Aug 2018 04:42:50 +0000 (10:12 +0530)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Mon, 24 Sep 2018 08:44:54 +0000 (04:44 -0400)
With this, perf buildid-cache will save SDT markers with reference
counter in probe cache. Perf probe will be able to probe markers
having reference counter. Ex,

  # readelf -n /tmp/tick | grep -A1 loop2
    Name: loop2
    ... Semaphore: 0x0000000010020036

  # ./perf buildid-cache --add /tmp/tick
  # ./perf probe sdt_tick:loop2
  # ./perf stat -e sdt_tick:loop2 /tmp/tick
    hi: 0
    hi: 1
    hi: 2
    ^C
     Performance counter stats for '/tmp/tick':
                 3      sdt_tick:loop2
       2.561851452 seconds time elapsed

Link: http://lkml.kernel.org/r/20180820044250.11659-5-ravi.bangoria@linux.ibm.com
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Reviewed-by: Song Liu <songliubraving@fb.com>
Tested-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h
tools/perf/util/probe-file.c
tools/perf/util/probe-file.h
tools/perf/util/symbol-elf.c
tools/perf/util/symbol.h

index f119eb6..e86f8be 100644 (file)
@@ -1819,6 +1819,12 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
                        tp->offset = strtoul(fmt2_str, NULL, 10);
        }
 
+       if (tev->uprobes) {
+               fmt2_str = strchr(p, '(');
+               if (fmt2_str)
+                       tp->ref_ctr_offset = strtoul(fmt2_str + 1, NULL, 0);
+       }
+
        tev->nargs = argc - 2;
        tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
        if (tev->args == NULL) {
@@ -2012,6 +2018,22 @@ static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,
        return err;
 }
 
+static int
+synthesize_uprobe_trace_def(struct probe_trace_event *tev, struct strbuf *buf)
+{
+       struct probe_trace_point *tp = &tev->point;
+       int err;
+
+       err = strbuf_addf(buf, "%s:0x%lx", tp->module, tp->address);
+
+       if (err >= 0 && tp->ref_ctr_offset) {
+               if (!uprobe_ref_ctr_is_supported())
+                       return -1;
+               err = strbuf_addf(buf, "(0x%lx)", tp->ref_ctr_offset);
+       }
+       return err >= 0 ? 0 : -1;
+}
+
 char *synthesize_probe_trace_command(struct probe_trace_event *tev)
 {
        struct probe_trace_point *tp = &tev->point;
@@ -2041,15 +2063,17 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
        }
 
        /* Use the tp->address for uprobes */
-       if (tev->uprobes)
-               err = strbuf_addf(&buf, "%s:0x%lx", tp->module, tp->address);
-       else if (!strncmp(tp->symbol, "0x", 2))
+       if (tev->uprobes) {
+               err = synthesize_uprobe_trace_def(tev, &buf);
+       } else if (!strncmp(tp->symbol, "0x", 2)) {
                /* Absolute address. See try_to_find_absolute_address() */
                err = strbuf_addf(&buf, "%s%s0x%lx", tp->module ?: "",
                                  tp->module ? ":" : "", tp->address);
-       else
+       } else {
                err = strbuf_addf(&buf, "%s%s%s+%lu", tp->module ?: "",
                                tp->module ? ":" : "", tp->symbol, tp->offset);
+       }
+
        if (err)
                goto error;
 
@@ -2633,6 +2657,13 @@ static void warn_uprobe_event_compat(struct probe_trace_event *tev)
 {
        int i;
        char *buf = synthesize_probe_trace_command(tev);
+       struct probe_trace_point *tp = &tev->point;
+
+       if (tp->ref_ctr_offset && !uprobe_ref_ctr_is_supported()) {
+               pr_warning("A semaphore is associated with %s:%s and "
+                          "seems your kernel doesn't support it.\n",
+                          tev->group, tev->event);
+       }
 
        /* Old uprobe event doesn't support memory dereference */
        if (!tev->uprobes || tev->nargs == 0 || !buf)
index 45b14f0..15a98c3 100644 (file)
@@ -27,6 +27,7 @@ struct probe_trace_point {
        char            *symbol;        /* Base symbol */
        char            *module;        /* Module name */
        unsigned long   offset;         /* Offset from symbol */
+       unsigned long   ref_ctr_offset; /* SDT reference counter offset */
        unsigned long   address;        /* Actual address of the trace point */
        bool            retprobe;       /* Return probe flag */
 };
index b76088f..aac7817 100644 (file)
@@ -696,8 +696,16 @@ out_err:
 #ifdef HAVE_GELF_GETNOTE_SUPPORT
 static unsigned long long sdt_note__get_addr(struct sdt_note *note)
 {
-       return note->bit32 ? (unsigned long long)note->addr.a32[0]
-                : (unsigned long long)note->addr.a64[0];
+       return note->bit32 ?
+               (unsigned long long)note->addr.a32[SDT_NOTE_IDX_LOC] :
+               (unsigned long long)note->addr.a64[SDT_NOTE_IDX_LOC];
+}
+
+static unsigned long long sdt_note__get_ref_ctr_offset(struct sdt_note *note)
+{
+       return note->bit32 ?
+               (unsigned long long)note->addr.a32[SDT_NOTE_IDX_REFCTR] :
+               (unsigned long long)note->addr.a64[SDT_NOTE_IDX_REFCTR];
 }
 
 static const char * const type_to_suffix[] = {
@@ -775,14 +783,21 @@ static char *synthesize_sdt_probe_command(struct sdt_note *note,
 {
        struct strbuf buf;
        char *ret = NULL, **args;
-       int i, args_count;
+       int i, args_count, err;
+       unsigned long long ref_ctr_offset;
 
        if (strbuf_init(&buf, 32) < 0)
                return NULL;
 
-       if (strbuf_addf(&buf, "p:%s/%s %s:0x%llx",
-                               sdtgrp, note->name, pathname,
-                               sdt_note__get_addr(note)) < 0)
+       err = strbuf_addf(&buf, "p:%s/%s %s:0x%llx",
+                       sdtgrp, note->name, pathname,
+                       sdt_note__get_addr(note));
+
+       ref_ctr_offset = sdt_note__get_ref_ctr_offset(note);
+       if (ref_ctr_offset && err >= 0)
+               err = strbuf_addf(&buf, "(0x%llx)", ref_ctr_offset);
+
+       if (err < 0)
                goto error;
 
        if (!note->args)
@@ -998,6 +1013,7 @@ int probe_cache__show_all_caches(struct strfilter *filter)
 enum ftrace_readme {
        FTRACE_README_PROBE_TYPE_X = 0,
        FTRACE_README_KRETPROBE_OFFSET,
+       FTRACE_README_UPROBE_REF_CTR,
        FTRACE_README_END,
 };
 
@@ -1009,6 +1025,7 @@ static struct {
        [idx] = {.pattern = pat, .avail = false}
        DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"),
        DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"),
+       DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"),
 };
 
 static bool scan_ftrace_readme(enum ftrace_readme type)
@@ -1064,3 +1081,8 @@ bool kretprobe_offset_is_supported(void)
 {
        return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET);
 }
+
+bool uprobe_ref_ctr_is_supported(void)
+{
+       return scan_ftrace_readme(FTRACE_README_UPROBE_REF_CTR);
+}
index 63f29b1..2a24918 100644 (file)
@@ -69,6 +69,7 @@ struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
 int probe_cache__show_all_caches(struct strfilter *filter);
 bool probe_type_is_available(enum probe_type type);
 bool kretprobe_offset_is_supported(void);
+bool uprobe_ref_ctr_is_supported(void);
 #else  /* ! HAVE_LIBELF_SUPPORT */
 static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused)
 {
index 29770ea..0281d5e 100644 (file)
@@ -1947,6 +1947,34 @@ void kcore_extract__delete(struct kcore_extract *kce)
 }
 
 #ifdef HAVE_GELF_GETNOTE_SUPPORT
+
+static void sdt_adjust_loc(struct sdt_note *tmp, GElf_Addr base_off)
+{
+       if (!base_off)
+               return;
+
+       if (tmp->bit32)
+               tmp->addr.a32[SDT_NOTE_IDX_LOC] =
+                       tmp->addr.a32[SDT_NOTE_IDX_LOC] + base_off -
+                       tmp->addr.a32[SDT_NOTE_IDX_BASE];
+       else
+               tmp->addr.a64[SDT_NOTE_IDX_LOC] =
+                       tmp->addr.a64[SDT_NOTE_IDX_LOC] + base_off -
+                       tmp->addr.a64[SDT_NOTE_IDX_BASE];
+}
+
+static void sdt_adjust_refctr(struct sdt_note *tmp, GElf_Addr base_addr,
+                             GElf_Addr base_off)
+{
+       if (!base_off)
+               return;
+
+       if (tmp->bit32 && tmp->addr.a32[SDT_NOTE_IDX_REFCTR])
+               tmp->addr.a32[SDT_NOTE_IDX_REFCTR] -= (base_addr - base_off);
+       else if (tmp->addr.a64[SDT_NOTE_IDX_REFCTR])
+               tmp->addr.a64[SDT_NOTE_IDX_REFCTR] -= (base_addr - base_off);
+}
+
 /**
  * populate_sdt_note : Parse raw data and identify SDT note
  * @elf: elf of the opened file
@@ -1964,7 +1992,6 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len,
        const char *provider, *name, *args;
        struct sdt_note *tmp = NULL;
        GElf_Ehdr ehdr;
-       GElf_Addr base_off = 0;
        GElf_Shdr shdr;
        int ret = -EINVAL;
 
@@ -2060,17 +2087,12 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len,
         * base address in the description of the SDT note. If its different,
         * then accordingly, adjust the note location.
         */
-       if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) {
-               base_off = shdr.sh_offset;
-               if (base_off) {
-                       if (tmp->bit32)
-                               tmp->addr.a32[0] = tmp->addr.a32[0] + base_off -
-                                       tmp->addr.a32[1];
-                       else
-                               tmp->addr.a64[0] = tmp->addr.a64[0] + base_off -
-                                       tmp->addr.a64[1];
-               }
-       }
+       if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL))
+               sdt_adjust_loc(tmp, shdr.sh_offset);
+
+       /* Adjust reference counter offset */
+       if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_PROBES_SCN, NULL))
+               sdt_adjust_refctr(tmp, shdr.sh_addr, shdr.sh_offset);
 
        list_add_tail(&tmp->note_list, sdt_notes);
        return 0;
index f25fae4..20f4977 100644 (file)
@@ -379,12 +379,19 @@ int get_sdt_note_list(struct list_head *head, const char *target);
 int cleanup_sdt_note_list(struct list_head *sdt_notes);
 int sdt_notes__get_count(struct list_head *start);
 
+#define SDT_PROBES_SCN ".probes"
 #define SDT_BASE_SCN ".stapsdt.base"
 #define SDT_NOTE_SCN  ".note.stapsdt"
 #define SDT_NOTE_TYPE 3
 #define SDT_NOTE_NAME "stapsdt"
 #define NR_ADDR 3
 
+enum {
+       SDT_NOTE_IDX_LOC = 0,
+       SDT_NOTE_IDX_BASE,
+       SDT_NOTE_IDX_REFCTR,
+};
+
 struct mem_info *mem_info__new(void);
 struct mem_info *mem_info__get(struct mem_info *mi);
 void   mem_info__put(struct mem_info *mi);