perf probe: Add sdt probes arguments into the uprobe cmd string
[linux-2.6-microblaze.git] / tools / perf / util / probe-file.c
index 436b647..d741634 100644 (file)
 #include "probe-event.h"
 #include "probe-file.h"
 #include "session.h"
+#include "perf_regs.h"
 
-#define MAX_CMDLEN 256
+/* 4096 - 2 ('\n' + '\0') */
+#define MAX_CMDLEN 4094
 
 static void print_open_warning(int err, bool uprobe)
 {
@@ -70,7 +72,7 @@ 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];
        int ret;
@@ -92,12 +94,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)
@@ -687,6 +689,166 @@ 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"
+};
+
+static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
+{
+       char *tmp, *desc = strdup(arg);
+       const char *prefix = "", *suffix = "";
+       int ret = -1;
+
+       if (desc == NULL) {
+               pr_debug4("Allocation error\n");
+               return ret;
+       }
+
+       tmp = strchr(desc, '@');
+       if (tmp) {
+               long type_idx;
+               /*
+                * Isolate the string number and convert it into a
+                * binary value; this will be an index to get suffix
+                * of the uprobe name (defining the type)
+                */
+               tmp[0] = '\0';
+               type_idx = strtol(desc, NULL, 10);
+               /* Check that the conversion went OK */
+               if (type_idx == LONG_MIN || type_idx == LONG_MAX) {
+                       pr_debug4("Failed to parse sdt type\n");
+                       goto error;
+               }
+               /* Check that the converted value is OK */
+               if (type_idx < -8 || type_idx > 8) {
+                       pr_debug4("Failed to get a valid sdt type\n");
+                       goto error;
+               }
+               suffix = type_to_suffix[type_idx + 8];
+               /* Get rid of the sdt prefix which is now useless */
+               tmp++;
+               memmove(desc, tmp, strlen(tmp) + 1);
+       }
+
+       /*
+        * The uprobe tracer format does not support all the
+        * addressing modes (notably: in x86 the scaled mode); so, we
+        * detect ',' characters, if there is just one, there is no
+        * use converting the sdt arg into a uprobe one.
+        */
+       if (strchr(desc, ',')) {
+               pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
+               goto out;
+       }
+
+       /*
+        * If the argument addressing mode is indirect, we must check
+        * a few things...
+        */
+       tmp = strchr(desc, '(');
+       if (tmp) {
+               int j;
+
+               /*
+                * ...if the addressing mode is indirect with a
+                * positive offset (ex.: "1608(%ax)"), we need to add
+                * a '+' prefix so as to be compliant with uprobe
+                * format.
+                */
+               if (desc[0] != '+' && desc[0] != '-')
+                       prefix = "+";
+
+               /*
+                * ...or if the addressing mode is indirect with a symbol
+                * as offset, the argument will not be supported by
+                * the uprobe tracer format; so, let's skip this one.
+                */
+               for (j = 0; j < tmp - desc; j++) {
+                       if (desc[j] != '+' && desc[j] != '-' &&
+                               !isdigit(desc[j])) {
+                               pr_debug4("Skipping unsupported SDT argument; "
+                                       "%s\n", desc);
+                               goto out;
+                       }
+               }
+       }
+
+       /*
+        * The uprobe tracer format does not support constants; if we
+        * find one in the current argument, let's skip the argument.
+        */
+       if (strchr(desc, '$')) {
+               pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
+               goto out;
+       }
+
+       /*
+        * The uprobe parser does not support all gas register names;
+        * so, we have to replace them (ex. for x86_64: %rax -> %ax);
+        * the loop below looks for the register names (starting with
+        * a '%' and tries to perform the needed renamings.
+        */
+       tmp = strchr(desc, '%');
+       while (tmp) {
+               size_t offset = tmp - desc;
+
+               ret = sdt_rename_register(&desc, desc + offset);
+               if (ret < 0)
+                       goto error;
+
+               /*
+                * The desc pointer might have changed; so, let's not
+                * try to reuse tmp for next lookup
+                */
+               tmp = strchr(desc + offset + 1, '%');
+       }
+
+       if (strbuf_addf(buf, " arg%d=%s%s%s", i + 1, prefix, desc, suffix) < 0)
+               goto error;
+
+out:
+       ret = 0;
+error:
+       free(desc);
+       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;
@@ -723,11 +885,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,59 +1040,72 @@ 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;
-       bool    checked;
-} probe_type_table[] = {
-#define DEFINE_TYPE(idx, pat, def_avail)       \
-       [idx] = {.pattern = pat, .avail = (def_avail)}
-       DEFINE_TYPE(PROBE_TYPE_U, "* u8/16/32/64,*", true),
-       DEFINE_TYPE(PROBE_TYPE_S, "* s8/16/32/64,*", true),
-       DEFINE_TYPE(PROBE_TYPE_X, "* x8/16/32/64,*", false),
-       DEFINE_TYPE(PROBE_TYPE_STRING, "* string,*", true),
-       DEFINE_TYPE(PROBE_TYPE_BITFIELD,
-                   "* b<bit-width>@<bit-offset>/<container-size>", true),
+       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): *"),
 };
 
-bool probe_type_is_available(enum probe_type type)
+static bool scan_ftrace_readme(enum ftrace_readme type)
 {
+       int fd;
        FILE *fp;
        char *buf = NULL;
        size_t len = 0;
-       bool target_line = false;
-       bool ret = probe_type_table[type].avail;
+       bool ret = false;
+       static bool scanned = false;
 
-       if (type >= PROBE_TYPE_END)
-               return false;
-       /* We don't have to check the type which supported by default */
-       if (ret || probe_type_table[type].checked)
-               return ret;
+       if (scanned)
+               goto result;
 
-       if (asprintf(&buf, "%s/README", tracing_path) < 0)
+       fd = open_trace_file("README", false);
+       if (fd < 0)
                return ret;
 
-       fp = fopen(buf, "r");
-       if (!fp)
-               goto end;
-
-       zfree(&buf);
-       while (getline(&buf, &len, fp) > 0 && !ret) {
-               if (!target_line) {
-                       target_line = !!strstr(buf, " type: ");
-                       if (!target_line)
-                               continue;
-               } else if (strstr(buf, "\t          ") != buf)
-                       break;
-               ret = strglobmatch(buf, probe_type_table[type].pattern);
+       fp = fdopen(fd, "r");
+       if (!fp) {
+               close(fd);
+               return ret;
        }
-       /* Cache the result */
-       probe_type_table[type].checked = true;
-       probe_type_table[type].avail = 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);
-end:
        free(buf);
 
-       return ret;
+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);
 }