]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/trace/trace_probe.c
tracing: Fix clang -Wint-in-bool-context warnings in IF_ASSIGN macro
[linux.git] / kernel / trace / trace_probe.c
index dbef0d1350754c5404995811f31358bc39087400..905b10af5d5c5470b0b317aadc03937eb76f48ab 100644 (file)
@@ -178,6 +178,16 @@ void __trace_probe_log_err(int offset, int err_type)
        if (!command)
                return;
 
+       if (trace_probe_log.index >= trace_probe_log.argc) {
+               /**
+                * Set the error position is next to the last arg + space.
+                * Note that len includes the terminal null and the cursor
+                * appaers at pos + 1.
+                */
+               pos = len;
+               offset = 0;
+       }
+
        /* And make a command string from argv array */
        p = command;
        for (i = 0; i < trace_probe_log.argc; i++) {
@@ -316,6 +326,29 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
        return -EINVAL;
 }
 
+static int str_to_immediate(char *str, unsigned long *imm)
+{
+       if (isdigit(str[0]))
+               return kstrtoul(str, 0, imm);
+       else if (str[0] == '-')
+               return kstrtol(str, 0, (long *)imm);
+       else if (str[0] == '+')
+               return kstrtol(str + 1, 0, (long *)imm);
+       return -EINVAL;
+}
+
+static int __parse_imm_string(char *str, char **pbuf, int offs)
+{
+       size_t len = strlen(str);
+
+       if (str[len - 1] != '"') {
+               trace_probe_log_err(offs + len, IMMSTR_NO_CLOSE);
+               return -EINVAL;
+       }
+       *pbuf = kstrndup(str, len - 1, GFP_KERNEL);
+       return 0;
+}
+
 /* Recursive argument parser */
 static int
 parse_probe_arg(char *arg, const struct fetch_type *type,
@@ -430,7 +463,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
                        ret = parse_probe_arg(arg, t2, &code, end, flags, offs);
                        if (ret)
                                break;
-                       if (code->op == FETCH_OP_COMM) {
+                       if (code->op == FETCH_OP_COMM ||
+                           code->op == FETCH_OP_DATA) {
                                trace_probe_log_err(offs, COMM_CANT_DEREF);
                                return -EINVAL;
                        }
@@ -444,6 +478,21 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
                        code->offset = offset;
                }
                break;
+       case '\\':      /* Immediate value */
+               if (arg[1] == '"') {    /* Immediate string */
+                       ret = __parse_imm_string(arg + 2, &tmp, offs + 2);
+                       if (ret)
+                               break;
+                       code->op = FETCH_OP_DATA;
+                       code->data = tmp;
+               } else {
+                       ret = str_to_immediate(arg + 1, &code->immediate);
+                       if (ret)
+                               trace_probe_log_err(offs + 1, BAD_IMM);
+                       else
+                               code->op = FETCH_OP_IMM;
+               }
+               break;
        }
        if (!ret && code->op == FETCH_OP_NOP) {
                /* Parsed, but do not find fetch method */
@@ -542,8 +591,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
                }
        }
 
-       /* Since $comm can not be dereferred, we can find $comm by strcmp */
-       if (strcmp(arg, "$comm") == 0) {
+       /*
+        * Since $comm and immediate string can not be dereferred,
+        * we can find those by strcmp.
+        */
+       if (strcmp(arg, "$comm") == 0 || strncmp(arg, "\\\"", 2) == 0) {
                /* The type of $comm must be "string", and not an array. */
                if (parg->count || (t && strcmp(t, "string")))
                        return -EINVAL;
@@ -580,7 +632,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
        if (!strcmp(parg->type->name, "string") ||
            !strcmp(parg->type->name, "ustring")) {
                if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
-                   code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM) {
+                   code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
+                   code->op != FETCH_OP_DATA) {
                        trace_probe_log_err(offset + (t ? (t - arg) : 0),
                                            BAD_STRING);
                        ret = -EINVAL;
@@ -589,9 +642,10 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
                if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) ||
                     parg->count) {
                        /*
-                        * IMM and COMM is pointing actual address, those must
-                        * be kept, and if parg->count != 0, this is an array
-                        * of string pointers instead of string address itself.
+                        * IMM, DATA and COMM is pointing actual address, those
+                        * must be kept, and if parg->count != 0, this is an
+                        * array of string pointers instead of string address
+                        * itself.
                         */
                        code++;
                        if (code->op != FETCH_OP_NOP) {
@@ -665,7 +719,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
 fail:
        if (ret) {
                for (code = tmp; code < tmp + FETCH_INSN_MAX; code++)
-                       if (code->op == FETCH_NOP_SYMBOL)
+                       if (code->op == FETCH_NOP_SYMBOL ||
+                           code->op == FETCH_OP_DATA)
                                kfree(code->data);
        }
        kfree(tmp);
@@ -736,7 +791,8 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
        struct fetch_insn *code = arg->code;
 
        while (code && code->op != FETCH_OP_END) {
-               if (code->op == FETCH_NOP_SYMBOL)
+               if (code->op == FETCH_NOP_SYMBOL ||
+                   code->op == FETCH_OP_DATA)
                        kfree(code->data);
                code++;
        }
@@ -886,43 +942,85 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
        return 0;
 }
 
+static void trace_probe_event_free(struct trace_probe_event *tpe)
+{
+       kfree(tpe->class.system);
+       kfree(tpe->call.name);
+       kfree(tpe->call.print_fmt);
+       kfree(tpe);
+}
+
+int trace_probe_append(struct trace_probe *tp, struct trace_probe *to)
+{
+       if (trace_probe_has_sibling(tp))
+               return -EBUSY;
+
+       list_del_init(&tp->list);
+       trace_probe_event_free(tp->event);
+
+       tp->event = to->event;
+       list_add_tail(&tp->list, trace_probe_probe_list(to));
+
+       return 0;
+}
+
+void trace_probe_unlink(struct trace_probe *tp)
+{
+       list_del_init(&tp->list);
+       if (list_empty(trace_probe_probe_list(tp)))
+               trace_probe_event_free(tp->event);
+       tp->event = NULL;
+}
 
 void trace_probe_cleanup(struct trace_probe *tp)
 {
-       struct trace_event_call *call = trace_probe_event_call(tp);
        int i;
 
        for (i = 0; i < tp->nr_args; i++)
                traceprobe_free_probe_arg(&tp->args[i]);
 
-       kfree(call->class->system);
-       kfree(call->name);
-       kfree(call->print_fmt);
+       if (tp->event)
+               trace_probe_unlink(tp);
 }
 
 int trace_probe_init(struct trace_probe *tp, const char *event,
                     const char *group)
 {
-       struct trace_event_call *call = trace_probe_event_call(tp);
+       struct trace_event_call *call;
+       int ret = 0;
 
        if (!event || !group)
                return -EINVAL;
 
-       call->class = &tp->class;
-       call->name = kstrdup(event, GFP_KERNEL);
-       if (!call->name)
+       tp->event = kzalloc(sizeof(struct trace_probe_event), GFP_KERNEL);
+       if (!tp->event)
                return -ENOMEM;
 
-       tp->class.system = kstrdup(group, GFP_KERNEL);
-       if (!tp->class.system) {
-               kfree(call->name);
-               call->name = NULL;
-               return -ENOMEM;
+       INIT_LIST_HEAD(&tp->event->files);
+       INIT_LIST_HEAD(&tp->event->class.fields);
+       INIT_LIST_HEAD(&tp->event->probes);
+       INIT_LIST_HEAD(&tp->list);
+       list_add(&tp->event->probes, &tp->list);
+
+       call = trace_probe_event_call(tp);
+       call->class = &tp->event->class;
+       call->name = kstrdup(event, GFP_KERNEL);
+       if (!call->name) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       tp->event->class.system = kstrdup(group, GFP_KERNEL);
+       if (!tp->event->class.system) {
+               ret = -ENOMEM;
+               goto error;
        }
-       INIT_LIST_HEAD(&tp->files);
-       INIT_LIST_HEAD(&tp->class.fields);
 
        return 0;
+
+error:
+       trace_probe_cleanup(tp);
+       return ret;
 }
 
 int trace_probe_register_event_call(struct trace_probe *tp)
@@ -951,7 +1049,7 @@ int trace_probe_add_file(struct trace_probe *tp, struct trace_event_file *file)
 
        link->file = file;
        INIT_LIST_HEAD(&link->list);
-       list_add_tail_rcu(&link->list, &tp->files);
+       list_add_tail_rcu(&link->list, &tp->event->files);
        trace_probe_set_flag(tp, TP_FLAG_TRACE);
        return 0;
 }
@@ -982,8 +1080,51 @@ int trace_probe_remove_file(struct trace_probe *tp,
        synchronize_rcu();
        kfree(link);
 
-       if (list_empty(&tp->files))
+       if (list_empty(&tp->event->files))
                trace_probe_clear_flag(tp, TP_FLAG_TRACE);
 
        return 0;
 }
+
+/*
+ * Return the smallest index of different type argument (start from 1).
+ * If all argument types and name are same, return 0.
+ */
+int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b)
+{
+       int i;
+
+       /* In case of more arguments */
+       if (a->nr_args < b->nr_args)
+               return a->nr_args + 1;
+       if (a->nr_args > b->nr_args)
+               return b->nr_args + 1;
+
+       for (i = 0; i < a->nr_args; i++) {
+               if ((b->nr_args <= i) ||
+                   ((a->args[i].type != b->args[i].type) ||
+                    (a->args[i].count != b->args[i].count) ||
+                    strcmp(a->args[i].name, b->args[i].name)))
+                       return i + 1;
+       }
+
+       return 0;
+}
+
+bool trace_probe_match_command_args(struct trace_probe *tp,
+                                   int argc, const char **argv)
+{
+       char buf[MAX_ARGSTR_LEN + 1];
+       int i;
+
+       if (tp->nr_args < argc)
+               return false;
+
+       for (i = 0; i < argc; i++) {
+               snprintf(buf, sizeof(buf), "%s=%s",
+                        tp->args[i].name, tp->args[i].comm);
+               if (strcmp(buf, argv[i]))
+                       return false;
+       }
+       return true;
+}