]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/trace/trace_probe.c
Merge branch 'for-linus-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/ibft
[linux.git] / kernel / trace / trace_probe.c
index a347faced9595092464744b9729c80145ea8e0c8..dbef0d1350754c5404995811f31358bc39087400 100644 (file)
@@ -78,6 +78,8 @@ static const struct fetch_type probe_fetch_types[] = {
        /* Special types */
        __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1,
                            "__data_loc char[]"),
+       __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1,
+                           "__data_loc char[]"),
        /* Basic types */
        ASSIGN_FETCH_TYPE(u8,  u8,  0),
        ASSIGN_FETCH_TYPE(u16, u16, 0),
@@ -322,6 +324,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
 {
        struct fetch_insn *code = *pcode;
        unsigned long param;
+       int deref = FETCH_OP_DEREF;
        long offset = 0;
        char *tmp;
        int ret = 0;
@@ -394,9 +397,14 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
                break;
 
        case '+':       /* deref memory */
-               arg++;  /* Skip '+', because kstrtol() rejects it. */
-               /* fall through */
        case '-':
+               if (arg[1] == 'u') {
+                       deref = FETCH_OP_UDEREF;
+                       arg[1] = arg[0];
+                       arg++;
+               }
+               if (arg[0] == '+')
+                       arg++;  /* Skip '+', because kstrtol() rejects it. */
                tmp = strchr(arg, '(');
                if (!tmp) {
                        trace_probe_log_err(offs, DEREF_NEED_BRACE);
@@ -432,7 +440,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
                        }
                        *pcode = code;
 
-                       code->op = FETCH_OP_DEREF;
+                       code->op = deref;
                        code->offset = offset;
                }
                break;
@@ -569,15 +577,17 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
                goto fail;
 
        /* Store operation */
-       if (!strcmp(parg->type->name, "string")) {
-               if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM &&
-                   code->op != FETCH_OP_COMM) {
+       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) {
                        trace_probe_log_err(offset + (t ? (t - arg) : 0),
                                            BAD_STRING);
                        ret = -EINVAL;
                        goto fail;
                }
-               if (code->op != FETCH_OP_DEREF || parg->count) {
+               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
@@ -590,12 +600,20 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
                                goto fail;
                        }
                }
-               code->op = FETCH_OP_ST_STRING;  /* In DEREF case, replace it */
+               /* If op == DEREF, replace it with STRING */
+               if (!strcmp(parg->type->name, "ustring") ||
+                   code->op == FETCH_OP_UDEREF)
+                       code->op = FETCH_OP_ST_USTRING;
+               else
+                       code->op = FETCH_OP_ST_STRING;
                code->size = parg->type->size;
                parg->dynamic = true;
        } else if (code->op == FETCH_OP_DEREF) {
                code->op = FETCH_OP_ST_MEM;
                code->size = parg->type->size;
+       } else if (code->op == FETCH_OP_UDEREF) {
+               code->op = FETCH_OP_ST_UMEM;
+               code->size = parg->type->size;
        } else {
                code++;
                if (code->op != FETCH_OP_NOP) {
@@ -618,7 +636,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
        /* Loop(Array) operation */
        if (parg->count) {
                if (scode->op != FETCH_OP_ST_MEM &&
-                   scode->op != FETCH_OP_ST_STRING) {
+                   scode->op != FETCH_OP_ST_STRING &&
+                   scode->op != FETCH_OP_ST_USTRING) {
                        trace_probe_log_err(offset + (t ? (t - arg) : 0),
                                            BAD_STRING);
                        ret = -EINVAL;
@@ -825,6 +844,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
 
 int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return)
 {
+       struct trace_event_call *call = trace_probe_event_call(tp);
        int len;
        char *print_fmt;
 
@@ -836,7 +856,7 @@ int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return)
 
        /* Second: actually write the @print_fmt */
        __set_print_fmt(tp, print_fmt, len + 1, is_return);
-       tp->call.print_fmt = print_fmt;
+       call->print_fmt = print_fmt;
 
        return 0;
 }
@@ -865,3 +885,105 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
        }
        return 0;
 }
+
+
+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);
+}
+
+int trace_probe_init(struct trace_probe *tp, const char *event,
+                    const char *group)
+{
+       struct trace_event_call *call = trace_probe_event_call(tp);
+
+       if (!event || !group)
+               return -EINVAL;
+
+       call->class = &tp->class;
+       call->name = kstrdup(event, GFP_KERNEL);
+       if (!call->name)
+               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->files);
+       INIT_LIST_HEAD(&tp->class.fields);
+
+       return 0;
+}
+
+int trace_probe_register_event_call(struct trace_probe *tp)
+{
+       struct trace_event_call *call = trace_probe_event_call(tp);
+       int ret;
+
+       ret = register_trace_event(&call->event);
+       if (!ret)
+               return -ENODEV;
+
+       ret = trace_add_event_call(call);
+       if (ret)
+               unregister_trace_event(&call->event);
+
+       return ret;
+}
+
+int trace_probe_add_file(struct trace_probe *tp, struct trace_event_file *file)
+{
+       struct event_file_link *link;
+
+       link = kmalloc(sizeof(*link), GFP_KERNEL);
+       if (!link)
+               return -ENOMEM;
+
+       link->file = file;
+       INIT_LIST_HEAD(&link->list);
+       list_add_tail_rcu(&link->list, &tp->files);
+       trace_probe_set_flag(tp, TP_FLAG_TRACE);
+       return 0;
+}
+
+struct event_file_link *trace_probe_get_file_link(struct trace_probe *tp,
+                                                 struct trace_event_file *file)
+{
+       struct event_file_link *link;
+
+       trace_probe_for_each_link(link, tp) {
+               if (link->file == file)
+                       return link;
+       }
+
+       return NULL;
+}
+
+int trace_probe_remove_file(struct trace_probe *tp,
+                           struct trace_event_file *file)
+{
+       struct event_file_link *link;
+
+       link = trace_probe_get_file_link(tp, file);
+       if (!link)
+               return -ENOENT;
+
+       list_del_rcu(&link->list);
+       synchronize_rcu();
+       kfree(link);
+
+       if (list_empty(&tp->files))
+               trace_probe_clear_flag(tp, TP_FLAG_TRACE);
+
+       return 0;
+}