]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - tools/perf/util/probe-file.c
Merge tag 'perf-core-for-mingo-4.12-20170503' of git://git.kernel.org/pub/scm/linux...
[karo-tx-linux.git] / tools / perf / util / probe-file.c
index 9c3b9ed5b3c3ec68ee175b6bec91d1c90211798b..d679389e627c1800c58b6b632ef65e67ff49d771 100644 (file)
  * GNU General Public License for more details.
  *
  */
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/uio.h>
+#include <unistd.h>
 #include "util.h"
 #include "event.h"
 #include "strlist.h"
+#include "strfilter.h"
 #include "debug.h"
 #include "cache.h"
 #include "color.h"
 #include "probe-event.h"
 #include "probe-file.h"
 #include "session.h"
+#include "perf_regs.h"
+#include "string2.h"
 
-#define MAX_CMDLEN 256
+/* 4096 - 2 ('\n' + '\0') */
+#define MAX_CMDLEN 4094
 
 static void print_open_warning(int err, bool uprobe)
 {
@@ -70,14 +78,13 @@ static void print_both_open_warning(int kerr, int uerr)
        }
 }
 
-static int open_probe_events(const char *trace_file, bool readwrite)
+int open_trace_file(const char *trace_file, bool readwrite)
 {
        char buf[PATH_MAX];
-       const char *tracing_dir = "";
        int ret;
 
-       ret = e_snprintf(buf, PATH_MAX, "%s/%s%s",
-                        tracing_path, tracing_dir, trace_file);
+       ret = e_snprintf(buf, PATH_MAX, "%s/%s",
+                        tracing_path, trace_file);
        if (ret >= 0) {
                pr_debug("Opening %s write=%d\n", buf, readwrite);
                if (readwrite && !probe_event_dry_run)
@@ -93,12 +100,12 @@ static int open_probe_events(const char *trace_file, bool readwrite)
 
 static int open_kprobe_events(bool readwrite)
 {
-       return open_probe_events("kprobe_events", readwrite);
+       return open_trace_file("kprobe_events", readwrite);
 }
 
 static int open_uprobe_events(bool readwrite)
 {
-       return open_probe_events("uprobe_events", readwrite);
+       return open_trace_file("uprobe_events", readwrite);
 }
 
 int probe_file__open(int flag)
@@ -688,6 +695,110 @@ static unsigned long long sdt_note__get_addr(struct sdt_note *note)
                 : (unsigned long long)note->addr.a64[0];
 }
 
+static const char * const type_to_suffix[] = {
+       ":s64", "", "", "", ":s32", "", ":s16", ":s8",
+       "", ":u8", ":u16", "", ":u32", "", "", "", ":u64"
+};
+
+/*
+ * Isolate the string number and convert it into a decimal value;
+ * this will be an index to get suffix of the uprobe name (defining
+ * the type)
+ */
+static int sdt_arg_parse_size(char *n_ptr, const char **suffix)
+{
+       long type_idx;
+
+       type_idx = strtol(n_ptr, NULL, 10);
+       if (type_idx < -8 || type_idx > 8) {
+               pr_debug4("Failed to get a valid sdt type\n");
+               return -1;
+       }
+
+       *suffix = type_to_suffix[type_idx + 8];
+       return 0;
+}
+
+static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
+{
+       char *op, *desc = strdup(arg), *new_op = NULL;
+       const char *suffix = "";
+       int ret = -1;
+
+       if (desc == NULL) {
+               pr_debug4("Allocation error\n");
+               return ret;
+       }
+
+       /*
+        * Argument is in N@OP format. N is size of the argument and OP is
+        * the actual assembly operand. N can be omitted; in that case
+        * argument is just OP(without @).
+        */
+       op = strchr(desc, '@');
+       if (op) {
+               op[0] = '\0';
+               op++;
+
+               if (sdt_arg_parse_size(desc, &suffix))
+                       goto error;
+       } else {
+               op = desc;
+       }
+
+       ret = arch_sdt_arg_parse_op(op, &new_op);
+
+       if (ret < 0)
+               goto error;
+
+       if (ret == SDT_ARG_VALID) {
+               ret = strbuf_addf(buf, " arg%d=%s%s", i + 1, new_op, suffix);
+               if (ret < 0)
+                       goto error;
+       }
+
+       ret = 0;
+error:
+       free(desc);
+       free(new_op);
+       return ret;
+}
+
+static char *synthesize_sdt_probe_command(struct sdt_note *note,
+                                       const char *pathname,
+                                       const char *sdtgrp)
+{
+       struct strbuf buf;
+       char *ret = NULL, **args;
+       int i, args_count;
+
+       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)
+               goto error;
+
+       if (!note->args)
+               goto out;
+
+       if (note->args) {
+               args = argv_split(note->args, &args_count);
+
+               for (i = 0; i < args_count; ++i) {
+                       if (synthesize_sdt_probe_arg(&buf, i, args[i]) < 0)
+                               goto error;
+               }
+       }
+
+out:
+       ret = strbuf_detach(&buf, NULL);
+error:
+       strbuf_release(&buf);
+       return ret;
+}
+
 int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
 {
        struct probe_cache_entry *entry = NULL;
@@ -700,7 +811,7 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
        INIT_LIST_HEAD(&sdtlist);
        ret = get_sdt_note_list(&sdtlist, pathname);
        if (ret < 0) {
-               pr_debug("Failed to get sdt note: %d\n", ret);
+               pr_debug4("Failed to get sdt note: %d\n", ret);
                return ret;
        }
        list_for_each_entry(note, &sdtlist, note_list) {
@@ -724,11 +835,12 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
                        entry->pev.group = strdup(sdtgrp);
                        list_add_tail(&entry->node, &pcache->entries);
                }
-               ret = asprintf(&buf, "p:%s/%s %s:0x%llx",
-                               sdtgrp, note->name, pathname,
-                               sdt_note__get_addr(note));
-               if (ret < 0)
+               buf = synthesize_sdt_probe_command(note, pathname, sdtgrp);
+               if (!buf) {
+                       ret = -ENOMEM;
                        break;
+               }
+
                strlist__add(entry->tevlist, buf);
                free(buf);
                entry = NULL;
@@ -877,3 +989,73 @@ int probe_cache__show_all_caches(struct strfilter *filter)
 
        return 0;
 }
+
+enum ftrace_readme {
+       FTRACE_README_PROBE_TYPE_X = 0,
+       FTRACE_README_KRETPROBE_OFFSET,
+       FTRACE_README_END,
+};
+
+static struct {
+       const char *pattern;
+       bool avail;
+} ftrace_readme_table[] = {
+#define DEFINE_TYPE(idx, pat)                  \
+       [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): *"),
+};
+
+static bool scan_ftrace_readme(enum ftrace_readme type)
+{
+       int fd;
+       FILE *fp;
+       char *buf = NULL;
+       size_t len = 0;
+       bool ret = false;
+       static bool scanned = false;
+
+       if (scanned)
+               goto result;
+
+       fd = open_trace_file("README", false);
+       if (fd < 0)
+               return ret;
+
+       fp = fdopen(fd, "r");
+       if (!fp) {
+               close(fd);
+               return ret;
+       }
+
+       while (getline(&buf, &len, fp) > 0)
+               for (enum ftrace_readme i = 0; i < FTRACE_README_END; i++)
+                       if (!ftrace_readme_table[i].avail)
+                               ftrace_readme_table[i].avail =
+                                       strglobmatch(buf, ftrace_readme_table[i].pattern);
+       scanned = true;
+
+       fclose(fp);
+       free(buf);
+
+result:
+       if (type >= FTRACE_README_END)
+               return false;
+
+       return ftrace_readme_table[type].avail;
+}
+
+bool probe_type_is_available(enum probe_type type)
+{
+       if (type >= PROBE_TYPE_END)
+               return false;
+       else if (type == PROBE_TYPE_X)
+               return scan_ftrace_readme(FTRACE_README_PROBE_TYPE_X);
+
+       return true;
+}
+
+bool kretprobe_offset_is_supported(void)
+{
+       return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET);
+}