perf trace: Separate tp syscall field caching into init routine to be reused
[firefly-linux-kernel-4.4.55.git] / tools / perf / builtin-trace.c
index dc3da654ff12d5d3aa8fa901c24b617b1ef97fda..3fa1dce6d43e1250f3fab3f4413ae460bf7e7cc5 100644 (file)
 # define MADV_UNMERGEABLE      13
 #endif
 
+struct tp_field {
+       int offset;
+       union {
+               u64 (*integer)(struct tp_field *field, struct perf_sample *sample);
+               void *(*pointer)(struct tp_field *field, struct perf_sample *sample);
+       };
+};
+
+#define TP_UINT_FIELD(bits) \
+static u64 tp_field__u##bits(struct tp_field *field, struct perf_sample *sample) \
+{ \
+       return *(u##bits *)(sample->raw_data + field->offset); \
+}
+
+TP_UINT_FIELD(8);
+TP_UINT_FIELD(16);
+TP_UINT_FIELD(32);
+TP_UINT_FIELD(64);
+
+#define TP_UINT_FIELD__SWAPPED(bits) \
+static u64 tp_field__swapped_u##bits(struct tp_field *field, struct perf_sample *sample) \
+{ \
+       u##bits value = *(u##bits *)(sample->raw_data + field->offset); \
+       return bswap_##bits(value);\
+}
+
+TP_UINT_FIELD__SWAPPED(16);
+TP_UINT_FIELD__SWAPPED(32);
+TP_UINT_FIELD__SWAPPED(64);
+
+static int tp_field__init_uint(struct tp_field *field,
+                              struct format_field *format_field,
+                              bool needs_swap)
+{
+       field->offset = format_field->offset;
+
+       switch (format_field->size) {
+       case 1:
+               field->integer = tp_field__u8;
+               break;
+       case 2:
+               field->integer = needs_swap ? tp_field__swapped_u16 : tp_field__u16;
+               break;
+       case 4:
+               field->integer = needs_swap ? tp_field__swapped_u32 : tp_field__u32;
+               break;
+       case 8:
+               field->integer = needs_swap ? tp_field__swapped_u64 : tp_field__u64;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static void *tp_field__ptr(struct tp_field *field, struct perf_sample *sample)
+{
+       return sample->raw_data + field->offset;
+}
+
+static int tp_field__init_ptr(struct tp_field *field, struct format_field *format_field)
+{
+       field->offset = format_field->offset;
+       field->pointer = tp_field__ptr;
+       return 0;
+}
+
+struct syscall_tp {
+       struct tp_field id;
+       union {
+               struct tp_field args, ret;
+       };
+};
+
+static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel,
+                                         struct tp_field *field,
+                                         const char *name)
+{
+       struct format_field *format_field = perf_evsel__field(evsel, name);
+
+       if (format_field == NULL)
+               return -1;
+
+       return tp_field__init_uint(field, format_field, evsel->needs_swap);
+}
+
+#define perf_evsel__init_sc_tp_uint_field(evsel, name) \
+       ({ struct syscall_tp *sc = evsel->priv;\
+          perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); })
+
+static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel,
+                                        struct tp_field *field,
+                                        const char *name)
+{
+       struct format_field *format_field = perf_evsel__field(evsel, name);
+
+       if (format_field == NULL)
+               return -1;
+
+       return tp_field__init_ptr(field, format_field);
+}
+
+#define perf_evsel__init_sc_tp_ptr_field(evsel, name) \
+       ({ struct syscall_tp *sc = evsel->priv;\
+          perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); })
+
+static void perf_evsel__delete_priv(struct perf_evsel *evsel)
+{
+       free(evsel->priv);
+       evsel->priv = NULL;
+       perf_evsel__delete(evsel);
+}
+
+static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler)
+{
+       evsel->priv = malloc(sizeof(struct syscall_tp));
+       if (evsel->priv != NULL) {
+               if (perf_evsel__init_sc_tp_uint_field(evsel, id))
+                       goto out_delete;
+
+               evsel->handler = handler;
+               return 0;
+       }
+
+       return -ENOMEM;
+
+out_delete:
+       free(evsel->priv);
+       evsel->priv = NULL;
+       return -ENOENT;
+}
+
+static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void *handler)
+{
+       struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
+
+       if (evsel) {
+               if (perf_evsel__init_syscall_tp(evsel, handler))
+                       goto out_delete;
+       }
+
+       return evsel;
+
+out_delete:
+       perf_evsel__delete_priv(evsel);
+       return NULL;
+}
+
+#define perf_evsel__sc_tp_uint(evsel, name, sample) \
+       ({ struct syscall_tp *fields = evsel->priv; \
+          fields->name.integer(&fields->name, sample); })
+
+#define perf_evsel__sc_tp_ptr(evsel, name, sample) \
+       ({ struct syscall_tp *fields = evsel->priv; \
+          fields->name.pointer(&fields->name, sample); })
+
+static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist,
+                                         void *sys_enter_handler,
+                                         void *sys_exit_handler)
+{
+       int ret = -1;
+       struct perf_evsel *sys_enter, *sys_exit;
+
+       sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler);
+       if (sys_enter == NULL)
+               goto out;
+
+       if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args))
+               goto out_delete_sys_enter;
+
+       sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler);
+       if (sys_exit == NULL)
+               goto out_delete_sys_enter;
+
+       if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret))
+               goto out_delete_sys_exit;
+
+       perf_evlist__add(evlist, sys_enter);
+       perf_evlist__add(evlist, sys_exit);
+
+       ret = 0;
+out:
+       return ret;
+
+out_delete_sys_exit:
+       perf_evsel__delete_priv(sys_exit);
+out_delete_sys_enter:
+       perf_evsel__delete_priv(sys_enter);
+       goto out;
+}
+
+
 struct syscall_arg {
        unsigned long val;
        struct thread *thread;
@@ -770,7 +963,8 @@ static struct syscall_fmt {
        { .name     = "mmap",       .hexret = true,
          .arg_scnprintf = { [0] = SCA_HEX,       /* addr */
                             [2] = SCA_MMAP_PROT, /* prot */
-                            [3] = SCA_MMAP_FLAGS, /* flags */ }, },
+                            [3] = SCA_MMAP_FLAGS, /* flags */
+                            [4] = SCA_FD,        /* fd */ }, },
        { .name     = "mprotect",   .errmsg = true,
          .arg_scnprintf = { [0] = SCA_HEX, /* start */
                             [2] = SCA_MMAP_PROT, /* prot */ }, },
@@ -1114,7 +1308,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre
 
        if (trace->multiple_threads) {
                if (trace->show_comm)
-                       printed += fprintf(fp, "%.14s/", thread->comm);
+                       printed += fprintf(fp, "%.14s/", thread__comm_str(thread));
                printed += fprintf(fp, "%d ", thread->tid);
        }
 
@@ -1122,7 +1316,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre
 }
 
 static int trace__process_event(struct trace *trace, struct machine *machine,
-                               union perf_event *event)
+                               union perf_event *event, struct perf_sample *sample)
 {
        int ret = 0;
 
@@ -1130,9 +1324,9 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
        case PERF_RECORD_LOST:
                color_fprintf(trace->output, PERF_COLOR_RED,
                              "LOST %" PRIu64 " events!\n", event->lost.lost);
-               ret = machine__process_lost_event(machine, event);
+               ret = machine__process_lost_event(machine, event, sample);
        default:
-               ret = machine__process_event(machine, event);
+               ret = machine__process_event(machine, event, sample);
                break;
        }
 
@@ -1141,11 +1335,11 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
 
 static int trace__tool_process(struct perf_tool *tool,
                               union perf_event *event,
-                              struct perf_sample *sample __maybe_unused,
+                              struct perf_sample *sample,
                               struct machine *machine)
 {
        struct trace *trace = container_of(tool, struct trace, tool);
-       return trace__process_event(trace, machine, event);
+       return trace__process_event(trace, machine, event, sample);
 }
 
 static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
@@ -1159,15 +1353,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
        if (trace->host == NULL)
                return -ENOMEM;
 
-       if (perf_target__has_task(&trace->opts.target)) {
-               err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads,
-                                                       trace__tool_process,
-                                                       trace->host);
-       } else {
-               err = perf_event__synthesize_threads(&trace->tool, trace__tool_process,
-                                                    trace->host);
-       }
-
+       err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target,
+                                           evlist->threads, trace__tool_process, false);
        if (err)
                symbol__exit();
 
@@ -1392,7 +1579,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
        void *args;
        size_t printed = 0;
        struct thread *thread;
-       int id = perf_evsel__intval(evsel, sample, "id");
+       int id = perf_evsel__sc_tp_uint(evsel, id, sample);
        struct syscall *sc = trace__syscall_info(trace, evsel, id);
        struct thread_trace *ttrace;
 
@@ -1407,12 +1594,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
        if (ttrace == NULL)
                return -1;
 
-       args = perf_evsel__rawptr(evsel, sample, "args");
-       if (args == NULL) {
-               fprintf(trace->output, "Problems reading syscall arguments\n");
-               return -1;
-       }
-
+       args = perf_evsel__sc_tp_ptr(evsel, args, sample);
        ttrace = thread->priv;
 
        if (ttrace->entry_str == NULL) {
@@ -1445,7 +1627,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
        int ret;
        u64 duration = 0;
        struct thread *thread;
-       int id = perf_evsel__intval(evsel, sample, "id");
+       int id = perf_evsel__sc_tp_uint(evsel, id, sample);
        struct syscall *sc = trace__syscall_info(trace, evsel, id);
        struct thread_trace *ttrace;
 
@@ -1463,7 +1645,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
        if (trace->summary)
                thread__update_stats(ttrace, id, sample);
 
-       ret = perf_evsel__intval(evsel, sample, "ret");
+       ret = perf_evsel__sc_tp_uint(evsel, ret, sample);
 
        if (id == trace->audit.open_id && ret >= 0 && trace->last_vfs_getname) {
                trace__set_fd_pathname(thread, ret, trace->last_vfs_getname);
@@ -1570,7 +1752,7 @@ static int trace__process_sample(struct perf_tool *tool,
        struct trace *trace = container_of(tool, struct trace, tool);
        int err = 0;
 
-       tracepoint_handler handler = evsel->handler.func;
+       tracepoint_handler handler = evsel->handler;
 
        if (skip_sample(trace, sample))
                return 0;
@@ -1646,8 +1828,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
 
 static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
 {
-       struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname",
-                                                    evlist->nr_entries);
+       struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
        if (evsel == NULL)
                return;
 
@@ -1656,7 +1837,7 @@ static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
                return;
        }
 
-       evsel->handler.func = trace__vfs_getname;
+       evsel->handler = trace__vfs_getname;
        perf_evlist__add(evlist, evsel);
 }
 
@@ -1675,8 +1856,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
                goto out;
        }
 
-       if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) ||
-               perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit))
+       if (perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, trace__sys_exit))
                goto out_error_tp;
 
        perf_evlist__add_vfs_getname(evlist);
@@ -1751,7 +1931,7 @@ again:
                                trace->base_time = sample.time;
 
                        if (type != PERF_RECORD_SAMPLE) {
-                               trace__process_event(trace, trace->host, event);
+                               trace__process_event(trace, trace->host, event, &sample);
                                continue;
                        }
 
@@ -1768,7 +1948,7 @@ again:
                                goto next_event;
                        }
 
-                       handler = evsel->handler.func;
+                       handler = evsel->handler;
                        handler(trace, evsel, &sample);
 next_event:
                        perf_evlist__mmap_consume(evlist, i);
@@ -1986,7 +2166,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
        else if (ratio > 5.0)
                color = PERF_COLOR_YELLOW;
 
-       printed += color_fprintf(fp, color, "%20s", thread->comm);
+       printed += color_fprintf(fp, color, "%20s", thread__comm_str(thread));
        printed += fprintf(fp, " - %-5d :%11lu   [", thread->tid, ttrace->nr_events);
        printed += color_fprintf(fp, color, "%5.1f%%", ratio);
        printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms);