* Use is subject to license terms.
*/
+#include <linux/ctype.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/sdt.h>
int sdt_probetab_size;
int sdt_probetab_mask;
-static sdt_argdesc_t sdt_args[] = {
+/*
+ * Return, in newly-allocated space, a version of the passed-in type which has
+ * been cleaned up suitably for CTF: leading and trailing spaces (if any)
+ * removed, and optionally a trailing argument removed as well.
+ *
+ * Type strings look like either
+ *
+ * type (for SDT, as in function prototypes), or
+ *
+ * type argname (for perf: as in function declarations).
+ *
+ * Translator components ": (foo, foo)", if any, have been removed by this
+ * stage.
+ */
+static char *cleanup_type(const char *type, int arg_strip)
+{
+ const char *cleaned;
+ const char *p;
+
+ cleaned = type + strspn(type, " \t");
+ for (p = cleaned + strlen(cleaned) - 1; p > cleaned &&
+ isspace(*p); p--);
+ if (arg_strip) {
+ for (; p > cleaned && (isalnum(*p) || *p == '_'); p--);
+ for (; p > cleaned && isspace(*p); p--);
+ }
+ p++;
+
+ return kstrndup(cleaned, p - cleaned, GFP_KERNEL);
+}
+
+/*
+ * Set up the args lists, extracting them from their sdpd entry and parsing them
+ * into an sdt_argdesc array for each probe.
+ */
+static sdt_argdesc_t *sdt_setup_args(sdt_probedesc_t *sdpd, size_t *sdp_nargdesc)
+{
+ sdt_argdesc_t *args;
+ char *argstr;
+ char *p;
+ int arg_strip = 0;
+ char *next_arg = NULL;
+ size_t arg = 0, sarg = 0, i;
+
+ *sdp_nargdesc = 0;
+
+ if ((sdpd->sdpd_args == NULL) || (sdpd->sdpd_args[0] == '\0'))
+ return NULL;
+
/*
- * { name, provider, ndx, mapping, native, xlate }
+ * Take a copy of the string so we can mutate it without causing trouble
+ * on module reload.
*/
- { "io", "done", 0, 0, "struct buffer_head *", "bufinfo_t *" },
- { "io", "done", 1, 0, "struct buffer_head *", "devinfo_t *" },
- { "io", "done", 2, 0, "struct buffer_head *", "fileinfo_t *" },
- { "io", "start", 0, 0, "struct buffer_head *", "bufinfo_t *" },
- { "io", "start", 1, 0, "struct buffer_head *", "devinfo_t *" },
- { "io", "start", 2, 0, "struct buffer_head *", "fileinfo_t *" },
- { "io", "wait-done", 0, 0, "struct buffer_head *", "bufinfo_t *" },
- { "io", "wait-done", 1, 0, "struct buffer_head *", "devinfo_t *" },
- { "io", "wait-done", 2, 0, "struct buffer_head *", "fileinfo_t *" },
- { "io", "wait-start", 0, 0, "struct buffer_head *", "bufinfo_t *" },
- { "io", "wait-start", 1, 0, "struct buffer_head *", "devinfo_t *" },
- { "io", "wait-start", 2, 0, "struct buffer_head *", "fileinfo_t *" },
-
- { "proc", "create", 0, 0, "struct task_struct *", "psinfo_t *" },
- { "proc", "exec", 0, 0, "char *", },
- { "proc", "exec-failure", 0, 0, "int", },
- { "proc", "exit", 0, 0, "int", },
-#if 0
- { "proc", "fault", 0, 0, "int", },
- { "proc", "fault", 1, 1, "siginfo_t", },
-#endif
- { "proc", "lwp-create", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "proc", "lwp-create", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "proc", "signal-clear", 0, 0, "int", },
- { "proc", "signal-discard", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "proc", "signal-discard", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "proc", "signal-discard", 2, 1, "int", },
- { "proc", "signal-handle", 0, 0, "int" },
- { "proc", "signal-handle", 1, 1, "siginfo_t *" },
- { "proc", "signal-handle", 2, 2, "void (*)(void)" },
- { "proc", "signal-send", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "proc", "signal-send", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "proc", "signal-send", 2, 1, "int", },
-
- { "sched", "change-pri", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "change-pri", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "sched", "change-pri", 2, 1, "int", },
- { "sched", "dequeue", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "dequeue", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "sched", "dequeue", 2, 1, "cpuinfo_t *", },
- { "sched", "dequeue", 3, 2, "int", },
- { "sched", "enqueue", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "enqueue", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "sched", "enqueue", 2, 1, "cpuinfo_t *", },
- { "sched", "off-cpu", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "off-cpu", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "sched", "surrender", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "surrender", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "sched", "tick", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "tick", 1, 0, "struct task_struct *", "psinfo_t *" },
- { "sched", "wakeup", 0, 0, "struct task_struct *", "lwpsinfo_t *" },
- { "sched", "wakeup", 1, 0, "struct task_struct *", "psinfo_t *" },
-
- { NULL, }
-};
+ argstr = kstrdup(sdpd->sdpd_args, GFP_KERNEL);
+ if (argstr == NULL)
+ goto oom;
+
+ /*
+ * Handle the common case of a trailing comma before we allocate space,
+ * and elide it.
+ */
+ p = argstr + strlen(argstr) - 1;
+ if (p[0] == ',' && p[1] == '\0')
+ *p = '\0';
+
+ /*
+ * This works for counting the number of args even though translator
+ * strings can contain commas, because each comma denotes a new probe
+ * argument. It may overcount in the case of elided arguments
+ * ("foo : ,"): we compensate for that further down, and ignore the tiny
+ * memory leak that results.
+ */
+ for (p = argstr; p != NULL; p = strchr(p + 1, ','))
+ (*sdp_nargdesc)++;
+
+ args = kzalloc(*sdp_nargdesc * sizeof (struct sdt_argdesc),
+ GFP_KERNEL);
+ if (args == NULL)
+ goto oom_argstr;
+
+ /*
+ * We need to transform each arg (stripping off a terminal argument
+ * name) if this is a perf probe.
+ */
+ if (strncmp(sdpd->sdpd_name, "__perf_", strlen("__perf_")) == 0)
+ arg_strip = 1;
+
+ next_arg = argstr;
+ do {
+ char *tok;
+ char *xlator = NULL, *p;
+ char *native;
+ int parens = 0;
+ int empty_xlation;
+
+ /*
+ * Find the end of this arg, and figure out if it has any
+ * translators. Clean up the type of the arg (or native type,
+ * if this is a translated type).
+ */
+ tok = next_arg;
+ next_arg = NULL;
+ p = strpbrk(tok, "():,");
+ while (p && !next_arg) {
+ switch(*p) {
+ case '(': parens++;
+ break;
+ case ')': if (parens > 0)
+ parens--;
+ break;
+ case ':': *p = '\0';
+ xlator = p + 1;
+ break;
+ case ',': if (parens == 0) {
+ *p = '\0';
+ next_arg = p + 1;
+ }
+ break;
+ }
+ p = strpbrk(p + 1, "():,");
+ }
+
+ native = cleanup_type(tok, arg_strip);
+ if (native == NULL) {
+ args[arg].sda_native = args[arg].sda_xlate = NULL;
+ goto full_oom;
+ }
+
+ /*
+ * Special case: perf's DECLARE_TRACE_NOARGS passes a single arg
+ * 'void'. Spot and skip it.
+ */
+ if (!xlator && arg_strip && strcmp(native, "void") == 0) {
+ kfree(native);
+ (*sdp_nargdesc)--;
+ sarg++;
+ continue;
+ }
+
+ /*
+ * No translator: straight mapping.
+ */
+ if (xlator == NULL) {
+ ASSERT(arg < *sdp_nargdesc);
+ args[arg].sda_mapping = sarg;
+ args[arg].sda_native = native;
+ args[arg].sda_xlate = NULL;
+ arg++;
+ sarg++;
+ continue;
+ }
+
+ /*
+ * If this is a perf probe, warn: translations cannot exist for
+ * these, and have no defined format yet in any case. We can
+ * struggle on by assuming they look like SDT translations.
+ */
+ if (arg_strip)
+ pr_warn("Perf probe %s has at least one SDT translation, "
+ "which should be impossible.", sdpd->sdpd_name);
+
+ /*
+ * Zero or more translations. (If there are zero, i.e. a pair
+ * of empty parentheses or a colon with nothing after it, we
+ * have to decrement the nargdesc.)
+ */
+
+ empty_xlation = 1;
+ while ((p = strsep(&xlator, "(,)")) != NULL) {
+ /*
+ * Skip the empty space before the ( or after the ).
+ */
+ if (strspn(p, " \t") == strlen(p))
+ continue;
+
+ ASSERT(arg < *sdp_nargdesc);
+
+ empty_xlation = 0;
+ args[arg].sda_mapping = sarg;
+ args[arg].sda_native = kstrdup(native, GFP_KERNEL);
+ args[arg].sda_xlate = cleanup_type(p, 0);
+ if ((args[arg].sda_native == NULL) ||
+ (args[arg].sda_xlate == NULL)) {
+ pr_warn("Unable to create argdesc list for "
+ "probe %s: out-of-memory\n",
+ sdpd->sdpd_name);
+ kfree(native);
+ goto full_oom;
+ }
+ arg++;
+ }
+ if (empty_xlation)
+ (*sdp_nargdesc)--;
+
+ kfree(native);
+ sarg++;
+ } while (next_arg != NULL);
+
+ kfree(argstr);
+ return args;
+
+full_oom:
+ for (i = 0; i < arg; i++) {
+ kfree(args[i].sda_native);
+ kfree(args[i].sda_xlate);
+ }
+ kfree(args);
+oom_argstr:
+ kfree(argstr);
+oom:
+ *sdp_nargdesc = 0;
+ pr_warn("Unable to create argdesc list for probe %s: "
+ "out-of-memory\n", sdpd->sdpd_name);
+ return NULL;
+}
void sdt_provide_module(void *arg, struct module *mp)
{
sdp->sdp_namelen = len;
sdp->sdp_provider = prov;
+ sdp->sdp_argdesc = sdt_setup_args(sdpd, &sdp->sdp_nargdesc);
+
if ((id = dtrace_probe_lookup(prov->dtmp_id, modname,
sdpd->sdpd_func, nname)) !=
DTRACE_IDNONE) {
dtrace_argdesc_t *desc)
{
sdt_probe_t *sdp = parg;
- int i;
desc->dtargd_native[0] = '\0';
desc->dtargd_xlate[0] = '\0';
- for (i = 0; sdt_args[i].sda_provider != NULL; i++) {
- sdt_argdesc_t *a = &sdt_args[i];
-
- if (strcmp(sdp->sdp_provider->dtmp_name, a->sda_provider) != 0)
- continue;
-
- if (a->sda_name != NULL &&
- strcmp(sdp->sdp_name, a->sda_name) != 0)
- continue;
-
- if (desc->dtargd_ndx != a->sda_ndx)
- continue;
-
- if (a->sda_native != NULL)
- strcpy(desc->dtargd_native, a->sda_native);
-
- if (a->sda_xlate != NULL)
- strcpy(desc->dtargd_xlate, a->sda_xlate);
-
- desc->dtargd_mapping = a->sda_mapping;
-
+ if (sdp->sdp_nargdesc <= desc->dtargd_ndx) {
+ desc->dtargd_ndx = DTRACE_ARGNONE;
return;
}
- desc->dtargd_ndx = DTRACE_ARGNONE;
+ if (sdp->sdp_argdesc[desc->dtargd_ndx].sda_native != NULL)
+ strlcpy(desc->dtargd_native,
+ sdp->sdp_argdesc[desc->dtargd_ndx].sda_native,
+ sizeof(desc->dtargd_native));
+
+ if (sdp->sdp_argdesc[desc->dtargd_ndx].sda_xlate != NULL)
+ strlcpy(desc->dtargd_xlate,
+ sdp->sdp_argdesc[desc->dtargd_ndx].sda_xlate,
+ sizeof(desc->dtargd_xlate));
+
+ desc->dtargd_mapping = sdp->sdp_argdesc[desc->dtargd_ndx].sda_mapping;
}
void sdt_destroy(void *arg, dtrace_id_t id, void *parg)
while (sdp != NULL) {
sdt_probe_t *old = sdp, *last, *hash;
int ndx;
+ size_t i;
ndx = SDT_ADDR2NDX(sdp->sdp_patchpoint);
last = NULL;
else
sdt_probetab[ndx] = sdp->sdp_hashnext;
+ for (i = 0; i < sdp->sdp_nargdesc; i++) {
+ kfree(sdp->sdp_argdesc[i].sda_native);
+ kfree(sdp->sdp_argdesc[i].sda_xlate);
+ }
+ kfree(sdp->sdp_argdesc);
kfree(sdp->sdp_name);
sdp = sdp->sdp_next;
kfree(old);