]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/trace/trace_probe.c
Merge branches 'pm-opp' and 'pm-tools'
[linux.git] / kernel / trace / trace_probe.c
index 3ef15a6683c002bc2c5402b5be8ad07c903021bc..9962cb5da8acdebb43e247c1e55df86a19038a51 100644 (file)
@@ -154,24 +154,52 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset)
        return 0;
 }
 
+/* @buf must has MAX_EVENT_NAME_LEN size */
+int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
+                               char *buf)
+{
+       const char *slash, *event = *pevent;
+
+       slash = strchr(event, '/');
+       if (slash) {
+               if (slash == event) {
+                       pr_info("Group name is not specified\n");
+                       return -EINVAL;
+               }
+               if (slash - event + 1 > MAX_EVENT_NAME_LEN) {
+                       pr_info("Group name is too long\n");
+                       return -E2BIG;
+               }
+               strlcpy(buf, event, slash - event + 1);
+               *pgroup = buf;
+               *pevent = slash + 1;
+       }
+       if (strlen(event) == 0) {
+               pr_info("Event name is not specified\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
 #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
 
 static int parse_probe_vars(char *arg, const struct fetch_type *t,
                            struct fetch_insn *code, unsigned int flags)
 {
-       int ret = 0;
        unsigned long param;
+       int ret = 0;
+       int len;
 
        if (strcmp(arg, "retval") == 0) {
                if (flags & TPARG_FL_RETURN)
                        code->op = FETCH_OP_RETVAL;
                else
                        ret = -EINVAL;
-       } else if (strncmp(arg, "stack", 5) == 0) {
-               if (arg[5] == '\0') {
+       } else if ((len = str_has_prefix(arg, "stack"))) {
+               if (arg[len] == '\0') {
                        code->op = FETCH_OP_STACKP;
-               } else if (isdigit(arg[5])) {
-                       ret = kstrtoul(arg + 5, 10, &param);
+               } else if (isdigit(arg[len])) {
+                       ret = kstrtoul(arg + len, 10, &param);
                        if (ret || ((flags & TPARG_FL_KERNEL) &&
                                    param > PARAM_MAX_STACK))
                                ret = -EINVAL;
@@ -186,10 +214,10 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
 #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
        } else if (((flags & TPARG_FL_MASK) ==
                    (TPARG_FL_KERNEL | TPARG_FL_FENTRY)) &&
-                  strncmp(arg, "arg", 3) == 0) {
-               if (!isdigit(arg[3]))
+                  (len = str_has_prefix(arg, "arg"))) {
+               if (!isdigit(arg[len]))
                        return -EINVAL;
-               ret = kstrtoul(arg + 3, 10, &param);
+               ret = kstrtoul(arg + len, 10, &param);
                if (ret || !param || param > PARAM_MAX_STACK)
                        return -EINVAL;
                code->op = FETCH_OP_ARG;
@@ -348,7 +376,7 @@ static int __parse_bitfield_probe_arg(const char *bf,
 }
 
 /* String length checking wrapper */
-int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
+static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
                struct probe_arg *parg, unsigned int flags)
 {
        struct fetch_insn *code, *scode, *tmp = NULL;
@@ -491,8 +519,8 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 }
 
 /* Return 1 if name is reserved or already used by another argument */
-int traceprobe_conflict_field_name(const char *name,
-                              struct probe_arg *args, int narg)
+static int traceprobe_conflict_field_name(const char *name,
+                                         struct probe_arg *args, int narg)
 {
        int i;
 
@@ -507,6 +535,47 @@ int traceprobe_conflict_field_name(const char *name,
        return 0;
 }
 
+int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, char *arg,
+                               unsigned int flags)
+{
+       struct probe_arg *parg = &tp->args[i];
+       char *body;
+       int ret;
+
+       /* Increment count for freeing args in error case */
+       tp->nr_args++;
+
+       body = strchr(arg, '=');
+       if (body) {
+               parg->name = kmemdup_nul(arg, body - arg, GFP_KERNEL);
+               body++;
+       } else {
+               /* If argument name is omitted, set "argN" */
+               parg->name = kasprintf(GFP_KERNEL, "arg%d", i + 1);
+               body = arg;
+       }
+       if (!parg->name)
+               return -ENOMEM;
+
+       if (!is_good_name(parg->name)) {
+               pr_info("Invalid argument[%d] name: %s\n",
+                       i, parg->name);
+               return -EINVAL;
+       }
+
+       if (traceprobe_conflict_field_name(parg->name, tp->args, i)) {
+               pr_info("Argument[%d]: '%s' conflicts with another field.\n",
+                       i, parg->name);
+               return -EINVAL;
+       }
+
+       /* Parse fetch argument */
+       ret = traceprobe_parse_probe_arg_body(body, &tp->size, parg, flags);
+       if (ret)
+               pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
+       return ret;
+}
+
 void traceprobe_free_probe_arg(struct probe_arg *arg)
 {
        struct fetch_insn *code = arg->code;
@@ -535,7 +604,7 @@ int traceprobe_update_arg(struct probe_arg *arg)
                        if (code[1].op != FETCH_OP_IMM)
                                return -EINVAL;
 
-                       tmp = strpbrk("+-", code->data);
+                       tmp = strpbrk(code->data, "+-");
                        if (tmp)
                                c = *tmp;
                        ret = traceprobe_split_symbol_offset(code->data,