Merge branch 'perf/urgent' into perf/core, to merge fixes before pulling more changes
authorIngo Molnar <mingo@kernel.org>
Fri, 31 Jul 2015 07:59:28 +0000 (09:59 +0200)
committerIngo Molnar <mingo@kernel.org>
Fri, 31 Jul 2015 07:59:28 +0000 (09:59 +0200)
Signed-off-by: Ingo Molnar <mingo@kernel.org>
63 files changed:
include/uapi/linux/perf_event.h
kernel/events/core.c
tools/lib/api/fs/debugfs.c
tools/lib/traceevent/event-parse.c
tools/lib/traceevent/event-parse.h
tools/perf/Build
tools/perf/Documentation/perf-bench.txt
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-script.txt
tools/perf/Makefile.perf
tools/perf/bench/Build
tools/perf/bench/bench.h
tools/perf/bench/futex-lock-pi.c [new file with mode: 0644]
tools/perf/bench/futex.h
tools/perf/builtin-bench.c
tools/perf/builtin-buildid-cache.c
tools/perf/builtin-buildid-list.c
tools/perf/builtin-inject.c
tools/perf/builtin-probe.c
tools/perf/builtin-record.c
tools/perf/builtin-script.c
tools/perf/builtin-trace.c
tools/perf/config/Makefile
tools/perf/perf.h
tools/perf/tests/thread-map.c
tools/perf/trace/strace/groups/file [new file with mode: 0644]
tools/perf/util/Build
tools/perf/util/build-id.c
tools/perf/util/build-id.h
tools/perf/util/dso.h
tools/perf/util/event.c
tools/perf/util/event.h
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/header.c
tools/perf/util/header.h
tools/perf/util/machine.c
tools/perf/util/machine.h
tools/perf/util/map.c
tools/perf/util/map.h
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h
tools/perf/util/probe-file.c [new file with mode: 0644]
tools/perf/util/probe-file.h [new file with mode: 0644]
tools/perf/util/probe-finder.c
tools/perf/util/record.c
tools/perf/util/session.c
tools/perf/util/string.c
tools/perf/util/strlist.c
tools/perf/util/strlist.h
tools/perf/util/symbol.c
tools/perf/util/thread_map.c
tools/perf/util/tool.h
tools/perf/util/trace-event-info.c
tools/perf/util/trace-event-parse.c
tools/perf/util/trace-event-read.c
tools/perf/util/trace-event.c
tools/perf/util/trace-event.h
tools/perf/util/util.h

index d97f84c080daefb3e8789a59b98cedbbe387cc56..022d0acf7df086b47650e98d714b0a966cb1dde7 100644 (file)
@@ -330,7 +330,8 @@ struct perf_event_attr {
                                mmap2          :  1, /* include mmap with inode data     */
                                comm_exec      :  1, /* flag comm events that are due to an exec */
                                use_clockid    :  1, /* use @clockid for time fields */
-                               __reserved_1   : 38;
+                               context_switch :  1, /* context switch data */
+                               __reserved_1   : 37;
 
        union {
                __u32           wakeup_events;    /* wakeup every n events */
@@ -572,9 +573,11 @@ struct perf_event_mmap_page {
 /*
  * PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
  * different events so can reuse the same bit position.
+ * Ditto PERF_RECORD_MISC_SWITCH_OUT.
  */
 #define PERF_RECORD_MISC_MMAP_DATA             (1 << 13)
 #define PERF_RECORD_MISC_COMM_EXEC             (1 << 13)
+#define PERF_RECORD_MISC_SWITCH_OUT            (1 << 13)
 /*
  * Indicates that the content of PERF_SAMPLE_IP points to
  * the actual instruction that triggered the event. See also
@@ -818,6 +821,32 @@ enum perf_event_type {
         */
        PERF_RECORD_LOST_SAMPLES                = 13,
 
+       /*
+        * Records a context switch in or out (flagged by
+        * PERF_RECORD_MISC_SWITCH_OUT). See also
+        * PERF_RECORD_SWITCH_CPU_WIDE.
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_SWITCH                      = 14,
+
+       /*
+        * CPU-wide version of PERF_RECORD_SWITCH with next_prev_pid and
+        * next_prev_tid that are the next (switching out) or previous
+        * (switching in) pid/tid.
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             next_prev_pid;
+        *      u32                             next_prev_tid;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_SWITCH_CPU_WIDE             = 15,
+
        PERF_RECORD_MAX,                        /* non-ABI */
 };
 
index 10d076b2572c0d24cb4275328250118dd6c0bc9e..a9796c8ff7e055d0fee3f4c9d24a711ff3807ca4 100644 (file)
@@ -163,6 +163,7 @@ static atomic_t nr_mmap_events __read_mostly;
 static atomic_t nr_comm_events __read_mostly;
 static atomic_t nr_task_events __read_mostly;
 static atomic_t nr_freq_events __read_mostly;
+static atomic_t nr_switch_events __read_mostly;
 
 static LIST_HEAD(pmus);
 static DEFINE_MUTEX(pmus_lock);
@@ -2619,6 +2620,9 @@ static void perf_pmu_sched_task(struct task_struct *prev,
        local_irq_restore(flags);
 }
 
+static void perf_event_switch(struct task_struct *task,
+                             struct task_struct *next_prev, bool sched_in);
+
 #define for_each_task_context_nr(ctxn)                                 \
        for ((ctxn) = 0; (ctxn) < perf_nr_task_contexts; (ctxn)++)
 
@@ -2641,6 +2645,9 @@ void __perf_event_task_sched_out(struct task_struct *task,
        if (__this_cpu_read(perf_sched_cb_usages))
                perf_pmu_sched_task(task, next, false);
 
+       if (atomic_read(&nr_switch_events))
+               perf_event_switch(task, next, false);
+
        for_each_task_context_nr(ctxn)
                perf_event_context_sched_out(task, ctxn, next);
 
@@ -2831,6 +2838,9 @@ void __perf_event_task_sched_in(struct task_struct *prev,
        if (atomic_read(this_cpu_ptr(&perf_cgroup_events)))
                perf_cgroup_sched_in(prev, task);
 
+       if (atomic_read(&nr_switch_events))
+               perf_event_switch(task, prev, true);
+
        if (__this_cpu_read(perf_sched_cb_usages))
                perf_pmu_sched_task(prev, task, true);
 }
@@ -3454,6 +3464,10 @@ static void unaccount_event(struct perf_event *event)
                atomic_dec(&nr_task_events);
        if (event->attr.freq)
                atomic_dec(&nr_freq_events);
+       if (event->attr.context_switch) {
+               static_key_slow_dec_deferred(&perf_sched_events);
+               atomic_dec(&nr_switch_events);
+       }
        if (is_cgroup_event(event))
                static_key_slow_dec_deferred(&perf_sched_events);
        if (has_branch_stack(event))
@@ -5981,6 +5995,91 @@ void perf_log_lost_samples(struct perf_event *event, u64 lost)
        perf_output_end(&handle);
 }
 
+/*
+ * context_switch tracking
+ */
+
+struct perf_switch_event {
+       struct task_struct      *task;
+       struct task_struct      *next_prev;
+
+       struct {
+               struct perf_event_header        header;
+               u32                             next_prev_pid;
+               u32                             next_prev_tid;
+       } event_id;
+};
+
+static int perf_event_switch_match(struct perf_event *event)
+{
+       return event->attr.context_switch;
+}
+
+static void perf_event_switch_output(struct perf_event *event, void *data)
+{
+       struct perf_switch_event *se = data;
+       struct perf_output_handle handle;
+       struct perf_sample_data sample;
+       int ret;
+
+       if (!perf_event_switch_match(event))
+               return;
+
+       /* Only CPU-wide events are allowed to see next/prev pid/tid */
+       if (event->ctx->task) {
+               se->event_id.header.type = PERF_RECORD_SWITCH;
+               se->event_id.header.size = sizeof(se->event_id.header);
+       } else {
+               se->event_id.header.type = PERF_RECORD_SWITCH_CPU_WIDE;
+               se->event_id.header.size = sizeof(se->event_id);
+               se->event_id.next_prev_pid =
+                                       perf_event_pid(event, se->next_prev);
+               se->event_id.next_prev_tid =
+                                       perf_event_tid(event, se->next_prev);
+       }
+
+       perf_event_header__init_id(&se->event_id.header, &sample, event);
+
+       ret = perf_output_begin(&handle, event, se->event_id.header.size);
+       if (ret)
+               return;
+
+       if (event->ctx->task)
+               perf_output_put(&handle, se->event_id.header);
+       else
+               perf_output_put(&handle, se->event_id);
+
+       perf_event__output_id_sample(event, &handle, &sample);
+
+       perf_output_end(&handle);
+}
+
+static void perf_event_switch(struct task_struct *task,
+                             struct task_struct *next_prev, bool sched_in)
+{
+       struct perf_switch_event switch_event;
+
+       /* N.B. caller checks nr_switch_events != 0 */
+
+       switch_event = (struct perf_switch_event){
+               .task           = task,
+               .next_prev      = next_prev,
+               .event_id       = {
+                       .header = {
+                               /* .type */
+                               .misc = sched_in ? 0 : PERF_RECORD_MISC_SWITCH_OUT,
+                               /* .size */
+                       },
+                       /* .next_prev_pid */
+                       /* .next_prev_tid */
+               },
+       };
+
+       perf_event_aux(perf_event_switch_output,
+                      &switch_event,
+                      NULL);
+}
+
 /*
  * IRQ throttle logging
  */
@@ -7479,6 +7578,10 @@ static void account_event(struct perf_event *event)
                if (atomic_inc_return(&nr_freq_events) == 1)
                        tick_nohz_full_kick_all();
        }
+       if (event->attr.context_switch) {
+               atomic_inc(&nr_switch_events);
+               static_key_slow_inc(&perf_sched_events.key);
+       }
        if (has_branch_stack(event))
                static_key_slow_inc(&perf_sched_events.key);
        if (is_cgroup_event(event))
index 8305b3e9d48e29da0152592524af2e364a3b92f9..eb7cf4d18f8a0e60e04ab2799aa921586f800b24 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/kernel.h>
 
 #include "debugfs.h"
+#include "tracefs.h"
 
 #ifndef DEBUGFS_DEFAULT_PATH
 #define DEBUGFS_DEFAULT_PATH           "/sys/kernel/debug"
@@ -94,11 +95,21 @@ int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename
                         "Hint:\tIs the debugfs filesystem mounted?\n"
                         "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
                break;
-       case EACCES:
+       case EACCES: {
+               const char *mountpoint = debugfs_mountpoint;
+
+               if (!access(debugfs_mountpoint, R_OK) && strncmp(filename, "tracing/", 8) == 0) {
+                       const char *tracefs_mntpoint = tracefs_find_mountpoint();
+
+                       if (tracefs_mntpoint)
+                               mountpoint = tracefs_mntpoint;
+               }
+
                snprintf(buf, size,
                         "Error:\tNo permissions to read %s/%s\n"
                         "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
-                        debugfs_mountpoint, filename, debugfs_mountpoint);
+                        debugfs_mountpoint, filename, mountpoint);
+       }
                break;
        default:
                snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
index cc25f059ab3dfcc5368804597120f96d20dabfdf..fcd8a9e3d2e13ca3ee73812d339e9d5d10480d54 100644 (file)
@@ -418,7 +418,7 @@ static int func_map_init(struct pevent *pevent)
 }
 
 static struct func_map *
-find_func(struct pevent *pevent, unsigned long long addr)
+__find_func(struct pevent *pevent, unsigned long long addr)
 {
        struct func_map *func;
        struct func_map key;
@@ -434,6 +434,71 @@ find_func(struct pevent *pevent, unsigned long long addr)
        return func;
 }
 
+struct func_resolver {
+       pevent_func_resolver_t *func;
+       void                   *priv;
+       struct func_map        map;
+};
+
+/**
+ * pevent_set_function_resolver - set an alternative function resolver
+ * @pevent: handle for the pevent
+ * @resolver: function to be used
+ * @priv: resolver function private state.
+ *
+ * Some tools may have already a way to resolve kernel functions, allow them to
+ * keep using it instead of duplicating all the entries inside
+ * pevent->funclist.
+ */
+int pevent_set_function_resolver(struct pevent *pevent,
+                                pevent_func_resolver_t *func, void *priv)
+{
+       struct func_resolver *resolver = malloc(sizeof(*resolver));
+
+       if (resolver == NULL)
+               return -1;
+
+       resolver->func = func;
+       resolver->priv = priv;
+
+       free(pevent->func_resolver);
+       pevent->func_resolver = resolver;
+
+       return 0;
+}
+
+/**
+ * pevent_reset_function_resolver - reset alternative function resolver
+ * @pevent: handle for the pevent
+ *
+ * Stop using whatever alternative resolver was set, use the default
+ * one instead.
+ */
+void pevent_reset_function_resolver(struct pevent *pevent)
+{
+       free(pevent->func_resolver);
+       pevent->func_resolver = NULL;
+}
+
+static struct func_map *
+find_func(struct pevent *pevent, unsigned long long addr)
+{
+       struct func_map *map;
+
+       if (!pevent->func_resolver)
+               return __find_func(pevent, addr);
+
+       map = &pevent->func_resolver->map;
+       map->mod  = NULL;
+       map->addr = addr;
+       map->func = pevent->func_resolver->func(pevent->func_resolver->priv,
+                                               &map->addr, &map->mod);
+       if (map->func == NULL)
+               return NULL;
+
+       return map;
+}
+
 /**
  * pevent_find_function - find a function by a given address
  * @pevent: handle for the pevent
@@ -6564,6 +6629,7 @@ void pevent_free(struct pevent *pevent)
        free(pevent->trace_clock);
        free(pevent->events);
        free(pevent->sort_events);
+       free(pevent->func_resolver);
 
        free(pevent);
 }
index 063b1971eb35288ae1a8d72cab18a5df815126a4..204befb05a173754869f4dc00f58c48a87331091 100644 (file)
@@ -453,6 +453,10 @@ struct cmdline_list;
 struct func_map;
 struct func_list;
 struct event_handler;
+struct func_resolver;
+
+typedef char *(pevent_func_resolver_t)(void *priv,
+                                      unsigned long long *addrp, char **modp);
 
 struct pevent {
        int ref_count;
@@ -481,6 +485,7 @@ struct pevent {
        int cmdline_count;
 
        struct func_map *func_map;
+       struct func_resolver *func_resolver;
        struct func_list *funclist;
        unsigned int func_count;
 
@@ -611,6 +616,9 @@ enum trace_flag_type {
        TRACE_FLAG_SOFTIRQ              = 0x10,
 };
 
+int pevent_set_function_resolver(struct pevent *pevent,
+                                pevent_func_resolver_t *func, void *priv);
+void pevent_reset_function_resolver(struct pevent *pevent);
 int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
 int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock);
 int pevent_register_function(struct pevent *pevent, char *name,
index b77370ef70058d2245f4d53f2e47132a7b458e64..72237455b4003aeb72fccf3afdf40f2f5588f1d2 100644 (file)
@@ -35,6 +35,7 @@ paths += -DPERF_MAN_PATH="BUILD_STR($(mandir_SQ))"
 CFLAGS_builtin-help.o      += $(paths)
 CFLAGS_builtin-timechart.o += $(paths)
 CFLAGS_perf.o              += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" -include $(OUTPUT)PERF-VERSION-FILE
+CFLAGS_builtin-trace.o    += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))"
 
 libperf-y += util/
 libperf-y += arch/
index bf3d0644bf1066677ae6ced215f82ecbc55f409d..ab632d9fbd7d09c8947f824dddddcfd1d1cd0837 100644 (file)
@@ -216,6 +216,10 @@ Suite for evaluating parallel wake calls.
 *requeue*::
 Suite for evaluating requeue calls.
 
+*lock-pi*::
+Suite for evaluating futex lock_pi calls.
+
+
 SEE ALSO
 --------
 linkperf:perf[1]
index 9b9d9d086680ae82885d2603d2b90435132234bc..63ee0408761d2f16aa1aacd64a374e5ca5859a94 100644 (file)
@@ -45,6 +45,14 @@ OPTIONS
           param1 and param2 are defined as formats for the PMU in:
           /sys/bus/event_sources/devices/<pmu>/format/*
 
+         There are also some params which are not defined in .../<pmu>/format/*.
+         These params can be used to set event defaults.
+         Here is a list of the params.
+         - 'period': Set event sampling period
+
+         Note: If user explicitly sets options which conflict with the params,
+         the value set by the params will be overridden.
+
         - a hardware breakpoint event in the form of '\mem:addr[/len][:access]'
           where addr is the address in memory you want to break in.
           Access is the memory access type (read, write, execute) it can
@@ -61,7 +69,16 @@ OPTIONS
          "perf report" to view group events together.
 
 --filter=<filter>::
-        Event filter.
+        Event filter. This option should follow a event selector (-e) which
+       selects tracepoint event(s). Multiple '--filter' options are combined
+       using '&&'.
+
+--exclude-perf::
+       Don't record events issued by perf itself. This option should follow
+       a event selector (-e) which selects tracepoint event(s). It adds a
+       filter expression 'common_pid != $PERFPID' to filters. If other
+       '--filter' exists, the new filter expression will be combined with
+       them by '&&'.
 
 -a::
 --all-cpus::
@@ -276,6 +293,10 @@ When processing pre-existing threads /proc/XXX/mmap, it may take a long time,
 because the file may be huge. A time out is needed in such cases.
 This option sets the time out limit. The default value is 500 ms.
 
+--switch-events::
+Record context switch events i.e. events of type PERF_RECORD_SWITCH or
+PERF_RECORD_SWITCH_CPU_WIDE.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1]
index c82df572fac2ed4b4285487485d0ed33492e36c5..e2fec5fc21e718b16e6b66d898376e92e9ac67b0 100644 (file)
@@ -222,6 +222,10 @@ OPTIONS
 --show-mmap-events
        Display mmap related events (e.g. MMAP, MMAP2).
 
+--show-switch-events
+       Display context switch events i.e. events of type PERF_RECORD_SWITCH or
+       PERF_RECORD_SWITCH_CPU_WIDE.
+
 --header
        Show perf.data header.
 
index bba34636b7334b334bdb520d3ebd8925f86f84c0..4b58daeff881e54fdab34b49b10c473d67aafc7e 100644 (file)
@@ -507,6 +507,11 @@ endif
                $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
        $(call QUIET_INSTALL, perf-with-kcore) \
                $(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ifndef NO_LIBAUDIT
+       $(call QUIET_INSTALL, strace/groups) \
+               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(STRACE_GROUPS_INSTDIR_SQ)'; \
+               $(INSTALL) trace/strace/groups/* -t '$(DESTDIR_SQ)$(STRACE_GROUPS_INSTDIR_SQ)'
+endif
 ifndef NO_LIBPERL
        $(call QUIET_INSTALL, perl-scripts) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
index c3ab760e06b4d7627896723dc9fe2b15c5327a0b..573e28896038d2f9045eb1eb700e77cbe5c66cbe 100644 (file)
@@ -5,6 +5,7 @@ perf-y += futex-hash.o
 perf-y += futex-wake.o
 perf-y += futex-wake-parallel.o
 perf-y += futex-requeue.o
+perf-y += futex-lock-pi.o
 
 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
 perf-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o
index 70b2f718cc217976ee73bd656d66b9aa884dfded..a50df86f2b9bc1035805fdc66699cf41bbfc6293 100644 (file)
@@ -36,6 +36,8 @@ extern int bench_futex_wake(int argc, const char **argv, const char *prefix);
 extern int bench_futex_wake_parallel(int argc, const char **argv,
                                     const char *prefix);
 extern int bench_futex_requeue(int argc, const char **argv, const char *prefix);
+/* pi futexes */
+extern int bench_futex_lock_pi(int argc, const char **argv, const char *prefix);
 
 #define BENCH_FORMAT_DEFAULT_STR       "default"
 #define BENCH_FORMAT_DEFAULT           0
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
new file mode 100644 (file)
index 0000000..bc6a16a
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2015 Davidlohr Bueso.
+ */
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/stat.h"
+#include "../util/parse-options.h"
+#include "../util/header.h"
+#include "bench.h"
+#include "futex.h"
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+struct worker {
+       int tid;
+       u_int32_t *futex;
+       pthread_t thread;
+       unsigned long ops;
+};
+
+static u_int32_t global_futex = 0;
+static struct worker *worker;
+static unsigned int nsecs = 10;
+static bool silent = false, multi = false;
+static bool done = false, fshared = false;
+static unsigned int ncpus, nthreads = 0;
+static int futex_flag = 0;
+struct timeval start, end, runtime;
+static pthread_mutex_t thread_lock;
+static unsigned int threads_starting;
+static struct stats throughput_stats;
+static pthread_cond_t thread_parent, thread_worker;
+
+static const struct option options[] = {
+       OPT_UINTEGER('t', "threads",  &nthreads, "Specify amount of threads"),
+       OPT_UINTEGER('r', "runtime", &nsecs,     "Specify runtime (in seconds)"),
+       OPT_BOOLEAN( 'M', "multi",   &multi,     "Use multiple futexes"),
+       OPT_BOOLEAN( 's', "silent",  &silent,    "Silent mode: do not display data/details"),
+       OPT_BOOLEAN( 'S', "shared",  &fshared,   "Use shared futexes instead of private ones"),
+       OPT_END()
+};
+
+static const char * const bench_futex_lock_pi_usage[] = {
+       "perf bench futex requeue <options>",
+       NULL
+};
+
+static void print_summary(void)
+{
+       unsigned long avg = avg_stats(&throughput_stats);
+       double stddev = stddev_stats(&throughput_stats);
+
+       printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
+              !silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
+              (int) runtime.tv_sec);
+}
+
+static void toggle_done(int sig __maybe_unused,
+                       siginfo_t *info __maybe_unused,
+                       void *uc __maybe_unused)
+{
+       /* inform all threads that we're done for the day */
+       done = true;
+       gettimeofday(&end, NULL);
+       timersub(&end, &start, &runtime);
+}
+
+static void *workerfn(void *arg)
+{
+       struct worker *w = (struct worker *) arg;
+
+       pthread_mutex_lock(&thread_lock);
+       threads_starting--;
+       if (!threads_starting)
+               pthread_cond_signal(&thread_parent);
+       pthread_cond_wait(&thread_worker, &thread_lock);
+       pthread_mutex_unlock(&thread_lock);
+
+       do {
+               int ret;
+       again:
+               ret = futex_lock_pi(w->futex, NULL, 0, futex_flag);
+
+               if (ret) { /* handle lock acquisition */
+                       if (!silent)
+                               warn("thread %d: Could not lock pi-lock for %p (%d)",
+                                    w->tid, w->futex, ret);
+                       if (done)
+                               break;
+
+                       goto again;
+               }
+
+               usleep(1);
+               ret = futex_unlock_pi(w->futex, futex_flag);
+               if (ret && !silent)
+                       warn("thread %d: Could not unlock pi-lock for %p (%d)",
+                            w->tid, w->futex, ret);
+               w->ops++; /* account for thread's share of work */
+       }  while (!done);
+
+       return NULL;
+}
+
+static void create_threads(struct worker *w, pthread_attr_t thread_attr)
+{
+       cpu_set_t cpu;
+       unsigned int i;
+
+       threads_starting = nthreads;
+
+       for (i = 0; i < nthreads; i++) {
+               worker[i].tid = i;
+
+               if (multi) {
+                       worker[i].futex = calloc(1, sizeof(u_int32_t));
+                       if (!worker[i].futex)
+                               err(EXIT_FAILURE, "calloc");
+               } else
+                       worker[i].futex = &global_futex;
+
+               CPU_ZERO(&cpu);
+               CPU_SET(i % ncpus, &cpu);
+
+               if (pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpu))
+                       err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
+
+               if (pthread_create(&w[i].thread, &thread_attr, workerfn, &worker[i]))
+                       err(EXIT_FAILURE, "pthread_create");
+       }
+}
+
+int bench_futex_lock_pi(int argc, const char **argv,
+                       const char *prefix __maybe_unused)
+{
+       int ret = 0;
+       unsigned int i;
+       struct sigaction act;
+       pthread_attr_t thread_attr;
+
+       argc = parse_options(argc, argv, options, bench_futex_lock_pi_usage, 0);
+       if (argc)
+               goto err;
+
+       ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+       sigfillset(&act.sa_mask);
+       act.sa_sigaction = toggle_done;
+       sigaction(SIGINT, &act, NULL);
+
+       if (!nthreads)
+               nthreads = ncpus;
+
+       worker = calloc(nthreads, sizeof(*worker));
+       if (!worker)
+               err(EXIT_FAILURE, "calloc");
+
+       if (!fshared)
+               futex_flag = FUTEX_PRIVATE_FLAG;
+
+       printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n",
+              getpid(), nthreads, nsecs);
+
+       init_stats(&throughput_stats);
+       pthread_mutex_init(&thread_lock, NULL);
+       pthread_cond_init(&thread_parent, NULL);
+       pthread_cond_init(&thread_worker, NULL);
+
+       threads_starting = nthreads;
+       pthread_attr_init(&thread_attr);
+       gettimeofday(&start, NULL);
+
+       create_threads(worker, thread_attr);
+       pthread_attr_destroy(&thread_attr);
+
+       pthread_mutex_lock(&thread_lock);
+       while (threads_starting)
+               pthread_cond_wait(&thread_parent, &thread_lock);
+       pthread_cond_broadcast(&thread_worker);
+       pthread_mutex_unlock(&thread_lock);
+
+       sleep(nsecs);
+       toggle_done(0, NULL, NULL);
+
+       for (i = 0; i < nthreads; i++) {
+               ret = pthread_join(worker[i].thread, NULL);
+               if (ret)
+                       err(EXIT_FAILURE, "pthread_join");
+       }
+
+       /* cleanup & report results */
+       pthread_cond_destroy(&thread_parent);
+       pthread_cond_destroy(&thread_worker);
+       pthread_mutex_destroy(&thread_lock);
+
+       for (i = 0; i < nthreads; i++) {
+               unsigned long t = worker[i].ops/runtime.tv_sec;
+
+               update_stats(&throughput_stats, t);
+               if (!silent)
+                       printf("[thread %3d] futex: %p [ %ld ops/sec ]\n",
+                              worker[i].tid, worker[i].futex, t);
+
+               if (multi)
+                       free(worker[i].futex);
+       }
+
+       print_summary();
+
+       free(worker);
+       return ret;
+err:
+       usage_with_options(bench_futex_lock_pi_usage, options);
+       exit(EXIT_FAILURE);
+}
index 7ed22ff1e1acd7cc86a97376076b135dc20642b7..d44de9f44281b11cbd7fa9740424b8b33acb4e93 100644 (file)
@@ -55,6 +55,26 @@ futex_wake(u_int32_t *uaddr, int nr_wake, int opflags)
        return futex(uaddr, FUTEX_WAKE, nr_wake, NULL, NULL, 0, opflags);
 }
 
+/**
+ * futex_lock_pi() - block on uaddr as a PI mutex
+ * @detect:    whether (1) or not (0) to perform deadlock detection
+ */
+static inline int
+futex_lock_pi(u_int32_t *uaddr, struct timespec *timeout, int detect,
+             int opflags)
+{
+       return futex(uaddr, FUTEX_LOCK_PI, detect, timeout, NULL, 0, opflags);
+}
+
+/**
+ * futex_unlock_pi() - release uaddr as a PI mutex, waking the top waiter
+ */
+static inline int
+futex_unlock_pi(u_int32_t *uaddr, int opflags)
+{
+       return futex(uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0, opflags);
+}
+
 /**
 * futex_cmp_requeue() - requeue tasks from uaddr to uaddr2
 * @nr_wake:        wake up to this many tasks
index b5314e452ec7f24a2e9e4e6b8473d39b8ed7a4a7..f67934d46d4098773a746f0039f1c3d1f1097097 100644 (file)
@@ -60,6 +60,8 @@ static struct bench futex_benchmarks[] = {
        { "wake",       "Benchmark for futex wake calls",               bench_futex_wake        },
        { "wake-parallel", "Benchmark for parallel futex wake calls",   bench_futex_wake_parallel },
        { "requeue",    "Benchmark for futex requeue calls",            bench_futex_requeue     },
+       /* pi-futexes */
+       { "lock-pi",    "Benchmark for futex lock_pi calls",            bench_futex_lock_pi     },
        { "all",        "Test all futex benchmarks",                    NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };
index d47a0cdc71c92a10040e34832886ba8157147349..65b4835309c7c0a1d7fe69701821d306e0249469 100644 (file)
@@ -127,7 +127,7 @@ static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
 
 static int build_id_cache__add_kcore(const char *filename, bool force)
 {
-       char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1];
+       char dir[32], sbuildid[SBUILD_ID_SIZE];
        char from_dir[PATH_MAX], to_dir[PATH_MAX];
        char *p;
 
@@ -184,7 +184,7 @@ static int build_id_cache__add_kcore(const char *filename, bool force)
 
 static int build_id_cache__add_file(const char *filename)
 {
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       char sbuild_id[SBUILD_ID_SIZE];
        u8 build_id[BUILD_ID_SIZE];
        int err;
 
@@ -204,7 +204,7 @@ static int build_id_cache__add_file(const char *filename)
 static int build_id_cache__remove_file(const char *filename)
 {
        u8 build_id[BUILD_ID_SIZE];
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       char sbuild_id[SBUILD_ID_SIZE];
 
        int err;
 
@@ -276,7 +276,7 @@ static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *f
 static int build_id_cache__update_file(const char *filename)
 {
        u8 build_id[BUILD_ID_SIZE];
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       char sbuild_id[SBUILD_ID_SIZE];
 
        int err = 0;
 
@@ -363,7 +363,7 @@ int cmd_buildid_cache(int argc, const char **argv,
        setup_pager();
 
        if (add_name_list_str) {
-               list = strlist__new(true, add_name_list_str);
+               list = strlist__new(add_name_list_str, NULL);
                if (list) {
                        strlist__for_each(pos, list)
                                if (build_id_cache__add_file(pos->s)) {
@@ -381,7 +381,7 @@ int cmd_buildid_cache(int argc, const char **argv,
        }
 
        if (remove_name_list_str) {
-               list = strlist__new(true, remove_name_list_str);
+               list = strlist__new(remove_name_list_str, NULL);
                if (list) {
                        strlist__for_each(pos, list)
                                if (build_id_cache__remove_file(pos->s)) {
@@ -399,7 +399,7 @@ int cmd_buildid_cache(int argc, const char **argv,
        }
 
        if (purge_name_list_str) {
-               list = strlist__new(true, purge_name_list_str);
+               list = strlist__new(purge_name_list_str, NULL);
                if (list) {
                        strlist__for_each(pos, list)
                                if (build_id_cache__purge_path(pos->s)) {
@@ -420,7 +420,7 @@ int cmd_buildid_cache(int argc, const char **argv,
                ret = build_id_cache__fprintf_missing(session, stdout);
 
        if (update_name_list_str) {
-               list = strlist__new(true, update_name_list_str);
+               list = strlist__new(update_name_list_str, NULL);
                if (list) {
                        strlist__for_each(pos, list)
                                if (build_id_cache__update_file(pos->s)) {
index 9fe93c8d4fcff11b3c2638d901d8c62da5b921d2..b5ca988ebfbe29d305519bf58b0ae519e2f374e9 100644 (file)
@@ -20,7 +20,7 @@
 static int sysfs__fprintf_build_id(FILE *fp)
 {
        u8 kallsyms_build_id[BUILD_ID_SIZE];
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       char sbuild_id[SBUILD_ID_SIZE];
 
        if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
                                 sizeof(kallsyms_build_id)) != 0)
@@ -35,7 +35,7 @@ static int sysfs__fprintf_build_id(FILE *fp)
 static int filename__fprintf_build_id(const char *name, FILE *fp)
 {
        u8 build_id[BUILD_ID_SIZE];
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       char sbuild_id[SBUILD_ID_SIZE];
 
        if (filename__read_build_id(name, build_id,
                                    sizeof(build_id)) != sizeof(build_id))
index 01b06492bd6a9cd74eb3d71dce8a57e7df71c593..f62c49b35be04ec698fc51e2b73ded6bb9dfbccf 100644 (file)
@@ -561,6 +561,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
                        .lost           = perf_event__repipe,
                        .aux            = perf_event__repipe,
                        .itrace_start   = perf_event__repipe,
+                       .context_switch = perf_event__repipe,
                        .read           = perf_event__repipe_sample,
                        .throttle       = perf_event__repipe,
                        .unthrottle     = perf_event__repipe,
index 1272559fa22d9eb60367f34594ebd18a7e76e8d3..b81cec33b4b2e4e07cac866a514e6369499b0c16 100644 (file)
@@ -297,8 +297,7 @@ static void cleanup_params(void)
                clear_perf_probe_event(params.events + i);
        line_range__clear(&params.line_range);
        free(params.target);
-       if (params.filter)
-               strfilter__delete(params.filter);
+       strfilter__delete(params.filter);
        memset(&params, 0, sizeof(params));
 }
 
index de165a1b92402ac7a6267bd0a0c5aa30a0053c92..445a64d19625493be60b7603fa795a617a68b5b9 100644 (file)
@@ -992,6 +992,9 @@ struct option __record_options[] = {
                     parse_events_option),
        OPT_CALLBACK(0, "filter", &record.evlist, "filter",
                     "event filter", parse_filter),
+       OPT_CALLBACK_NOOPT(0, "exclude-perf", &record.evlist,
+                          NULL, "don't record events from perf itself",
+                          exclude_perf),
        OPT_STRING('p', "pid", &record.opts.target.pid, "pid",
                    "record events on existing process id"),
        OPT_STRING('t', "tid", &record.opts.target.tid, "tid",
@@ -1030,7 +1033,9 @@ struct option __record_options[] = {
        OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
                    "per thread counts"),
        OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"),
-       OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Record the sample timestamps"),
+       OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time,
+                       &record.opts.sample_time_set,
+                       "Record the sample timestamps"),
        OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"),
        OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
                    "don't sample"),
@@ -1070,6 +1075,8 @@ struct option __record_options[] = {
                          "opts", "AUX area tracing Snapshot Mode", ""),
        OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout,
                        "per thread proc mmap processing timeout in ms"),
+       OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
+                   "Record context switch events"),
        OPT_END()
 };
 
@@ -1097,6 +1104,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
                          " system-wide mode\n");
                usage_with_options(record_usage, record_options);
        }
+       if (rec->opts.record_switch_events &&
+           !perf_can_record_switch_events()) {
+               ui__error("kernel does not support recording context switch events (--switch-events option)\n");
+               usage_with_options(record_usage, record_options);
+       }
 
        if (!rec->itr) {
                rec->itr = auxtrace_record__init(rec->evlist, &err);
index 24809787369f5a1303451de5264798d0e31792b6..bd31380122f96cdbabcba5451f2e246cef559802 100644 (file)
@@ -623,6 +623,7 @@ struct perf_script {
        struct perf_session     *session;
        bool                    show_task_events;
        bool                    show_mmap_events;
+       bool                    show_switch_events;
 };
 
 static int process_attr(struct perf_tool *tool, union perf_event *event,
@@ -661,7 +662,7 @@ static int process_comm_event(struct perf_tool *tool,
        struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
-       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
        int ret = -1;
 
        thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid);
@@ -695,7 +696,7 @@ static int process_fork_event(struct perf_tool *tool,
        struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
-       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
 
        if (perf_event__process_fork(tool, event, sample, machine) < 0)
                return -1;
@@ -727,7 +728,7 @@ static int process_exit_event(struct perf_tool *tool,
        struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
-       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
 
        thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
        if (thread == NULL) {
@@ -759,7 +760,7 @@ static int process_mmap_event(struct perf_tool *tool,
        struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
-       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
 
        if (perf_event__process_mmap(tool, event, sample, machine) < 0)
                return -1;
@@ -790,7 +791,7 @@ static int process_mmap2_event(struct perf_tool *tool,
        struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
-       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
 
        if (perf_event__process_mmap2(tool, event, sample, machine) < 0)
                return -1;
@@ -813,6 +814,32 @@ static int process_mmap2_event(struct perf_tool *tool,
        return 0;
 }
 
+static int process_switch_event(struct perf_tool *tool,
+                               union perf_event *event,
+                               struct perf_sample *sample,
+                               struct machine *machine)
+{
+       struct thread *thread;
+       struct perf_script *script = container_of(tool, struct perf_script, tool);
+       struct perf_session *session = script->session;
+       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+
+       if (perf_event__process_switch(tool, event, sample, machine) < 0)
+               return -1;
+
+       thread = machine__findnew_thread(machine, sample->pid,
+                                        sample->tid);
+       if (thread == NULL) {
+               pr_debug("problem processing SWITCH event, skipping it.\n");
+               return -1;
+       }
+
+       print_sample_start(sample, thread, evsel);
+       perf_event__fprintf(event, stdout);
+       thread__put(thread);
+       return 0;
+}
+
 static void sig_handler(int sig __maybe_unused)
 {
        session_done = 1;
@@ -834,6 +861,8 @@ static int __cmd_script(struct perf_script *script)
                script->tool.mmap = process_mmap_event;
                script->tool.mmap2 = process_mmap2_event;
        }
+       if (script->show_switch_events)
+               script->tool.context_switch = process_switch_event;
 
        ret = perf_session__process_events(script->session);
 
@@ -1618,6 +1647,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                    "Show the fork/comm/exit events"),
        OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events,
                    "Show the mmap events"),
+       OPT_BOOLEAN('\0', "show-switch-events", &script.show_switch_events,
+                   "Show context switch events (if recorded)"),
        OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
        OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
                            "Instruction Tracing options",
@@ -1830,6 +1861,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
        else
                symbol_conf.use_callchain = false;
 
+       if (pevent_set_function_resolver(session->tevent.pevent,
+                                        machine__resolve_kernel_addr,
+                                        &session->machines.host) < 0) {
+               pr_err("%s: failed to set libtraceevent function resolver\n", __func__);
+               return -1;
+       }
+
        if (generate_script_lang) {
                struct stat perf_stat;
                int input;
index 39ad4d0ca88427cb222fa3ce7c0a35c7b77ed243..282841b10f24a1922c21c06961c7f8862527504e 100644 (file)
@@ -3,6 +3,7 @@
 #include "util/color.h"
 #include "util/debug.h"
 #include "util/evlist.h"
+#include "util/exec_cmd.h"
 #include "util/machine.h"
 #include "util/session.h"
 #include "util/thread.h"
@@ -247,42 +248,6 @@ out_delete:
        ({ 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;
@@ -1223,7 +1188,6 @@ struct syscall {
        int                 nr_args;
        struct format_field *args;
        const char          *name;
-       bool                filtered;
        bool                is_exit;
        struct syscall_fmt  *fmt;
        size_t              (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg);
@@ -1307,6 +1271,10 @@ struct trace {
        struct {
                int             max;
                struct syscall  *table;
+               struct {
+                       struct perf_evsel *sys_enter,
+                                         *sys_exit;
+               }               events;
        } syscalls;
        struct record_opts      opts;
        struct perf_evlist      *evlist;
@@ -1316,6 +1284,10 @@ struct trace {
        FILE                    *output;
        unsigned long           nr_events;
        struct strlist          *ev_qualifier;
+       struct {
+               size_t          nr;
+               int             *entries;
+       }                       ev_qualifier_ids;
        const char              *last_vfs_getname;
        struct intlist          *tid_list;
        struct intlist          *pid_list;
@@ -1517,6 +1489,9 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
        if (trace->host == NULL)
                return -ENOMEM;
 
+       if (trace_event__register_resolver(trace->host) < 0)
+               return -errno;
+
        err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target,
                                            evlist->threads, trace__tool_process, false,
                                            trace->opts.proc_map_timeout);
@@ -1578,19 +1553,6 @@ static int trace__read_syscall_info(struct trace *trace, int id)
        sc = trace->syscalls.table + id;
        sc->name = name;
 
-       if (trace->ev_qualifier) {
-               bool in = strlist__find(trace->ev_qualifier, name) != NULL;
-
-               if (!(in ^ trace->not_ev_qualifier)) {
-                       sc->filtered = true;
-                       /*
-                        * No need to do read tracepoint information since this will be
-                        * filtered out.
-                        */
-                       return 0;
-               }
-       }
-
        sc->fmt  = syscall_fmt__find(sc->name);
 
        snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
@@ -1619,13 +1581,27 @@ static int trace__read_syscall_info(struct trace *trace, int id)
 
 static int trace__validate_ev_qualifier(struct trace *trace)
 {
-       int err = 0;
+       int err = 0, i;
        struct str_node *pos;
 
+       trace->ev_qualifier_ids.nr = strlist__nr_entries(trace->ev_qualifier);
+       trace->ev_qualifier_ids.entries = malloc(trace->ev_qualifier_ids.nr *
+                                                sizeof(trace->ev_qualifier_ids.entries[0]));
+
+       if (trace->ev_qualifier_ids.entries == NULL) {
+               fputs("Error:\tNot enough memory for allocating events qualifier ids\n",
+                      trace->output);
+               err = -EINVAL;
+               goto out;
+       }
+
+       i = 0;
+
        strlist__for_each(pos, trace->ev_qualifier) {
                const char *sc = pos->s;
+               int id = audit_name_to_syscall(sc, trace->audit.machine);
 
-               if (audit_name_to_syscall(sc, trace->audit.machine) < 0) {
+               if (id < 0) {
                        if (err == 0) {
                                fputs("Error:\tInvalid syscall ", trace->output);
                                err = -EINVAL;
@@ -1635,13 +1611,17 @@ static int trace__validate_ev_qualifier(struct trace *trace)
 
                        fputs(sc, trace->output);
                }
+
+               trace->ev_qualifier_ids.entries[i++] = id;
        }
 
        if (err < 0) {
                fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'"
                      "\nHint:\tand: 'man syscalls'\n", trace->output);
+               zfree(&trace->ev_qualifier_ids.entries);
+               trace->ev_qualifier_ids.nr = 0;
        }
-
+out:
        return err;
 }
 
@@ -1833,9 +1813,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
        if (sc == NULL)
                return -1;
 
-       if (sc->filtered)
-               return 0;
-
        thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
        ttrace = thread__trace(thread, trace->output);
        if (ttrace == NULL)
@@ -1891,9 +1868,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
        if (sc == NULL)
                return -1;
 
-       if (sc->filtered)
-               return 0;
-
        thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
        ttrace = thread__trace(thread, trace->output);
        if (ttrace == NULL)
@@ -2283,9 +2257,68 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
        }
 }
 
+static int trace__add_syscall_newtp(struct trace *trace)
+{
+       int ret = -1;
+       struct perf_evlist *evlist = trace->evlist;
+       struct perf_evsel *sys_enter, *sys_exit;
+
+       sys_enter = perf_evsel__syscall_newtp("sys_enter", trace__sys_enter);
+       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", trace__sys_exit);
+       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);
+
+       trace->syscalls.events.sys_enter = sys_enter;
+       trace->syscalls.events.sys_exit  = 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;
+}
+
+static int trace__set_ev_qualifier_filter(struct trace *trace)
+{
+       int err = -1;
+       char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier,
+                                               trace->ev_qualifier_ids.nr,
+                                               trace->ev_qualifier_ids.entries);
+
+       if (filter == NULL)
+               goto out_enomem;
+
+       if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, "&&", filter))
+               err = perf_evsel__append_filter(trace->syscalls.events.sys_exit, "&&", filter);
+
+       free(filter);
+out:
+       return err;
+out_enomem:
+       errno = ENOMEM;
+       goto out;
+}
+
 static int trace__run(struct trace *trace, int argc, const char **argv)
 {
        struct perf_evlist *evlist = trace->evlist;
+       struct perf_evsel *evsel;
        int err = -1, i;
        unsigned long before;
        const bool forks = argc > 0;
@@ -2293,9 +2326,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
 
        trace->live = true;
 
-       if (trace->trace_syscalls &&
-           perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
-                                          trace__sys_exit))
+       if (trace->trace_syscalls && trace__add_syscall_newtp(trace))
                goto out_error_raw_syscalls;
 
        if (trace->trace_syscalls)
@@ -2356,11 +2387,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
        else if (thread_map__pid(evlist->threads, 0) == -1)
                err = perf_evlist__set_filter_pid(evlist, getpid());
 
-       if (err < 0) {
-               printf("err=%d,%s\n", -err, strerror(-err));
-               exit(1);
+       if (err < 0)
+               goto out_error_mem;
+
+       if (trace->ev_qualifier_ids.nr > 0) {
+               err = trace__set_ev_qualifier_filter(trace);
+               if (err < 0)
+                       goto out_errno;
        }
 
+       pr_debug("%s\n", trace->syscalls.events.sys_exit->filter);
+
+       err = perf_evlist__apply_filters(evlist, &evsel);
+       if (err < 0)
+               goto out_error_apply_filters;
+
        err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
        if (err < 0)
                goto out_error_mmap;
@@ -2462,10 +2503,21 @@ out_error_open:
 out_error:
        fprintf(trace->output, "%s\n", errbuf);
        goto out_delete_evlist;
+
+out_error_apply_filters:
+       fprintf(trace->output,
+               "Failed to set filter \"%s\" on event %s with %d (%s)\n",
+               evsel->filter, perf_evsel__name(evsel), errno,
+               strerror_r(errno, errbuf, sizeof(errbuf)));
+       goto out_delete_evlist;
 }
 out_error_mem:
        fprintf(trace->output, "Not enough memory to run!\n");
        goto out_delete_evlist;
+
+out_errno:
+       fprintf(trace->output, "errno=%d,%s\n", errno, strerror(errno));
+       goto out_delete_evlist;
 }
 
 static int trace__replay(struct trace *trace)
@@ -2879,11 +2931,14 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
 
        if (ev_qualifier_str != NULL) {
                const char *s = ev_qualifier_str;
+               struct strlist_config slist_config = {
+                       .dirname = system_path(STRACE_GROUPS_DIR),
+               };
 
                trace.not_ev_qualifier = *s == '!';
                if (trace.not_ev_qualifier)
                        ++s;
-               trace.ev_qualifier = strlist__new(true, s);
+               trace.ev_qualifier = strlist__new(s, &slist_config);
                if (trace.ev_qualifier == NULL) {
                        fputs("Not enough memory to parse event qualifier",
                              trace.output);
index d31fac19c30b2d298ab2cf3a710b9b27c5764144..823195aa6d4b6e53bfe3a9693e11568809fe8236 100644 (file)
@@ -11,7 +11,7 @@ ifneq ($(obj-perf),)
 obj-perf := $(abspath $(obj-perf))/
 endif
 
-$(shell echo -n > $(OUTPUT).config-detected)
+$(shell printf "" > $(OUTPUT).config-detected)
 detected     = $(shell echo "$(1)=y"       >> $(OUTPUT).config-detected)
 detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected)
 
@@ -644,6 +644,7 @@ infodir = share/info
 perfexecdir = libexec/perf-core
 sharedir = $(prefix)/share
 template_dir = share/perf-core/templates
+STRACE_GROUPS_DIR = share/perf-core/strace/groups
 htmldir = share/doc/perf-doc
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
@@ -663,6 +664,7 @@ libdir = $(prefix)/$(lib)
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
 ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG))
+STRACE_GROUPS_DIR_SQ = $(subst ','\'',$(STRACE_GROUPS_DIR))
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
 mandir_SQ = $(subst ','\'',$(mandir))
@@ -676,10 +678,13 @@ libdir_SQ = $(subst ','\'',$(libdir))
 
 ifneq ($(filter /%,$(firstword $(perfexecdir))),)
 perfexec_instdir = $(perfexecdir)
+STRACE_GROUPS_INSTDIR = $(STRACE_GROUPS_DIR)
 else
 perfexec_instdir = $(prefix)/$(perfexecdir)
+STRACE_GROUPS_INSTDIR = $(prefix)/$(STRACE_GROUPS_DIR)
 endif
 perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
+STRACE_GROUPS_INSTDIR_SQ = $(subst ','\'',$(STRACE_GROUPS_INSTDIR))
 
 # If we install to $(HOME) we keep the traceevent default:
 # $(HOME)/.traceevent/plugins
@@ -713,6 +718,7 @@ $(call detected_var,htmldir_SQ)
 $(call detected_var,infodir_SQ)
 $(call detected_var,mandir_SQ)
 $(call detected_var,ETC_PERFCONFIG_SQ)
+$(call detected_var,STRACE_GROUPS_DIR_SQ)
 $(call detected_var,prefix_SQ)
 $(call detected_var,perfexecdir_SQ)
 $(call detected_var,LIBDIR)
index 4a5827fff7993d2bbe87666af51aa939f6f9dd1d..cf459f89fc9bd94cfe8e1d684c78bd00402f2855 100644 (file)
@@ -51,11 +51,13 @@ struct record_opts {
        bool         sample_address;
        bool         sample_weight;
        bool         sample_time;
+       bool         sample_time_set;
        bool         period;
        bool         sample_intr_regs;
        bool         running_time;
        bool         full_auxtrace;
        bool         auxtrace_snapshot_mode;
+       bool         record_switch_events;
        unsigned int freq;
        unsigned int mmap_pages;
        unsigned int auxtrace_mmap_pages;
index 5acf000939ea5178fa7b98fb0a3d35ff0a9301af..138a0e3431fafd7ae0b6441fa24b7845f7b51ea0 100644 (file)
@@ -20,6 +20,8 @@ int test__thread_map(void)
        TEST_ASSERT_VAL("wrong comm",
                        thread_map__comm(map, 0) &&
                        !strcmp(thread_map__comm(map, 0), "perf"));
+       TEST_ASSERT_VAL("wrong refcnt",
+                       atomic_read(&map->refcnt) == 1);
        thread_map__put(map);
 
        /* test dummy pid */
@@ -33,6 +35,8 @@ int test__thread_map(void)
        TEST_ASSERT_VAL("wrong comm",
                        thread_map__comm(map, 0) &&
                        !strcmp(thread_map__comm(map, 0), "dummy"));
+       TEST_ASSERT_VAL("wrong refcnt",
+                       atomic_read(&map->refcnt) == 1);
        thread_map__put(map);
        return 0;
 }
diff --git a/tools/perf/trace/strace/groups/file b/tools/perf/trace/strace/groups/file
new file mode 100644 (file)
index 0000000..62378a8
--- /dev/null
@@ -0,0 +1,18 @@
+access
+chmod
+creat
+execve
+faccessat
+getcwd
+lstat
+mkdir
+open
+openat
+quotactl
+readlink
+rename
+rmdir
+stat
+statfs
+symlink
+unlink
index d2d318c59b379531903f775abd9f842a02e01ca8..a1e5168dc1fb91bcb0c6b04840d82f9704f90e35 100644 (file)
@@ -79,6 +79,7 @@ libperf-$(CONFIG_AUXTRACE) += auxtrace.o
 libperf-y += parse-branch-options.o
 
 libperf-$(CONFIG_LIBELF) += symbol-elf.o
+libperf-$(CONFIG_LIBELF) += probe-file.o
 libperf-$(CONFIG_LIBELF) += probe-event.o
 
 ifndef CONFIG_LIBELF
index 1f6fc2323ef97d5e9fdea6f70a9028db1d14a37e..4a2c2f0ead415e51be5a5cf992b3d327169706bc 100644 (file)
@@ -124,7 +124,7 @@ static char *build_id__filename(const char *sbuild_id, char *bf, size_t size)
 
 char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
 {
-       char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+       char build_id_hex[SBUILD_ID_SIZE];
 
        if (!dso->has_build_id)
                return NULL;
@@ -291,7 +291,7 @@ int build_id_cache__list_build_ids(const char *pathname,
        struct dirent *d;
        int ret = 0;
 
-       list = strlist__new(true, NULL);
+       list = strlist__new(NULL, NULL);
        dir_name = build_id_cache__dirname_from_path(pathname, false, false);
        if (!list || !dir_name) {
                ret = -ENOMEM;
@@ -384,7 +384,7 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
                                 const char *name, bool is_kallsyms,
                                 bool is_vdso)
 {
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       char sbuild_id[SBUILD_ID_SIZE];
 
        build_id__sprintf(build_id, build_id_size, sbuild_id);
 
index 85011222cc14a68f05d2b3451ef381263d293efc..ce2f493f057a3629f26888098cee4467f1fed64e 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef PERF_BUILD_ID_H_
 #define PERF_BUILD_ID_H_ 1
 
-#define BUILD_ID_SIZE 20
+#define BUILD_ID_SIZE  20
+#define SBUILD_ID_SIZE (BUILD_ID_SIZE * 2 + 1)
 
 #include "tool.h"
 #include "strlist.h"
index 2fe98bb0e95b0d4b88fb58f2fc28daee875a13bb..c73276db6d6f2de3fff7abe40c99ee0c6560d520 100644 (file)
@@ -137,6 +137,10 @@ struct dso {
        struct rb_node   rb_node;       /* rbtree node sorted by long name */
        struct rb_root   symbols[MAP__NR_TYPES];
        struct rb_root   symbol_names[MAP__NR_TYPES];
+       struct {
+               u64             addr;
+               struct symbol   *symbol;
+       } last_find_result[MAP__NR_TYPES];
        void             *a2l;
        char             *symsrc_filename;
        unsigned int     a2l_fails;
index 67a977e5d0abee03a97eb74f914f9329163a6ca7..7ff61274ed57f5219416c69767fac9acca21e091 100644 (file)
@@ -26,6 +26,8 @@ static const char *perf_event__names[] = {
        [PERF_RECORD_AUX]                       = "AUX",
        [PERF_RECORD_ITRACE_START]              = "ITRACE_START",
        [PERF_RECORD_LOST_SAMPLES]              = "LOST_SAMPLES",
+       [PERF_RECORD_SWITCH]                    = "SWITCH",
+       [PERF_RECORD_SWITCH_CPU_WIDE]           = "SWITCH_CPU_WIDE",
        [PERF_RECORD_HEADER_ATTR]               = "ATTR",
        [PERF_RECORD_HEADER_EVENT_TYPE]         = "EVENT_TYPE",
        [PERF_RECORD_HEADER_TRACING_DATA]       = "TRACING_DATA",
@@ -749,6 +751,14 @@ int perf_event__process_lost_samples(struct perf_tool *tool __maybe_unused,
        return machine__process_lost_samples_event(machine, event, sample);
 }
 
+int perf_event__process_switch(struct perf_tool *tool __maybe_unused,
+                              union perf_event *event,
+                              struct perf_sample *sample __maybe_unused,
+                              struct machine *machine)
+{
+       return machine__process_switch_event(machine, event);
+}
+
 size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
 {
        return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n",
@@ -827,6 +837,20 @@ size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp)
                       event->itrace_start.pid, event->itrace_start.tid);
 }
 
+size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp)
+{
+       bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
+       const char *in_out = out ? "OUT" : "IN ";
+
+       if (event->header.type == PERF_RECORD_SWITCH)
+               return fprintf(fp, " %s\n", in_out);
+
+       return fprintf(fp, " %s  %s pid/tid: %5u/%-5u\n",
+                      in_out, out ? "next" : "prev",
+                      event->context_switch.next_prev_pid,
+                      event->context_switch.next_prev_tid);
+}
+
 size_t perf_event__fprintf(union perf_event *event, FILE *fp)
 {
        size_t ret = fprintf(fp, "PERF_RECORD_%s",
@@ -852,6 +876,10 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
        case PERF_RECORD_ITRACE_START:
                ret += perf_event__fprintf_itrace_start(event, fp);
                break;
+       case PERF_RECORD_SWITCH:
+       case PERF_RECORD_SWITCH_CPU_WIDE:
+               ret += perf_event__fprintf_switch(event, fp);
+               break;
        default:
                ret += fprintf(fp, "\n");
        }
index c53f36384b64532abec852c9e6d7a496a0d982a6..4bb2ae894c78c04cf99946f7add1e98b53fb1d11 100644 (file)
@@ -348,6 +348,12 @@ struct itrace_start_event {
        u32 pid, tid;
 };
 
+struct context_switch_event {
+       struct perf_event_header header;
+       u32 next_prev_pid;
+       u32 next_prev_tid;
+};
+
 union perf_event {
        struct perf_event_header        header;
        struct mmap_event               mmap;
@@ -369,6 +375,7 @@ union perf_event {
        struct auxtrace_error_event     auxtrace_error;
        struct aux_event                aux;
        struct itrace_start_event       itrace_start;
+       struct context_switch_event     context_switch;
 };
 
 void perf_event__print_totals(void);
@@ -418,6 +425,10 @@ int perf_event__process_itrace_start(struct perf_tool *tool,
                                     union perf_event *event,
                                     struct perf_sample *sample,
                                     struct machine *machine);
+int perf_event__process_switch(struct perf_tool *tool,
+                              union perf_event *event,
+                              struct perf_sample *sample,
+                              struct machine *machine);
 int perf_event__process_mmap(struct perf_tool *tool,
                             union perf_event *event,
                             struct perf_sample *sample,
@@ -480,6 +491,7 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf(union perf_event *event, FILE *fp);
 
 u64 kallsyms__get_function_start(const char *kallsyms_filename,
index 6cfdee68e76398cdb2d53c6950d8c34ea969484a..3b9f411a6b461272903e9b8559ada718d2e57867 100644 (file)
@@ -1102,7 +1102,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
 }
 
 static int perf_evlist__propagate_maps(struct perf_evlist *evlist,
-                                      struct target *target)
+                                      bool has_user_cpus)
 {
        struct perf_evsel *evsel;
 
@@ -1111,15 +1111,16 @@ static int perf_evlist__propagate_maps(struct perf_evlist *evlist,
                 * We already have cpus for evsel (via PMU sysfs) so
                 * keep it, if there's no target cpu list defined.
                 */
-               if (evsel->cpus && target->cpu_list)
+               if (evsel->cpus && has_user_cpus)
                        cpu_map__put(evsel->cpus);
 
-               if (!evsel->cpus || target->cpu_list)
+               if (!evsel->cpus || has_user_cpus)
                        evsel->cpus = cpu_map__get(evlist->cpus);
 
                evsel->threads = thread_map__get(evlist->threads);
 
-               if (!evsel->cpus || !evsel->threads)
+               if ((evlist->cpus && !evsel->cpus) ||
+                   (evlist->threads && !evsel->threads))
                        return -ENOMEM;
        }
 
@@ -1142,7 +1143,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
        if (evlist->cpus == NULL)
                goto out_delete_threads;
 
-       return perf_evlist__propagate_maps(evlist, target);
+       return perf_evlist__propagate_maps(evlist, !!target->cpu_list);
 
 out_delete_threads:
        thread_map__put(evlist->threads);
@@ -1150,6 +1151,23 @@ out_delete_threads:
        return -1;
 }
 
+int perf_evlist__set_maps(struct perf_evlist *evlist,
+                         struct cpu_map *cpus,
+                         struct thread_map *threads)
+{
+       if (evlist->cpus)
+               cpu_map__put(evlist->cpus);
+
+       evlist->cpus = cpus;
+
+       if (evlist->threads)
+               thread_map__put(evlist->threads);
+
+       evlist->threads = threads;
+
+       return perf_evlist__propagate_maps(evlist, false);
+}
+
 int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel)
 {
        struct perf_evsel *evsel;
@@ -1161,7 +1179,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
                if (evsel->filter == NULL)
                        continue;
 
-               err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter);
+               err = perf_evsel__apply_filter(evsel, ncpus, nthreads, evsel->filter);
                if (err) {
                        *err_evsel = evsel;
                        break;
@@ -1175,11 +1193,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
 {
        struct perf_evsel *evsel;
        int err = 0;
-       const int ncpus = cpu_map__nr(evlist->cpus),
-                 nthreads = thread_map__nr(evlist->threads);
 
        evlist__for_each(evlist, evsel) {
-               err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);
+               err = perf_evsel__set_filter(evsel, filter);
                if (err)
                        break;
        }
index 037633c1da9d0c42402b908294724f92e6caddc8..a8930b68456b07e1ee05317deb375e1788b29e53 100644 (file)
@@ -114,6 +114,7 @@ void perf_evlist__close(struct perf_evlist *evlist);
 
 void perf_evlist__set_id_pos(struct perf_evlist *evlist);
 bool perf_can_sample_identifier(void);
+bool perf_can_record_switch_events(void);
 void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts);
 int record_opts__config(struct record_opts *opts);
 
@@ -152,14 +153,9 @@ int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
 void perf_evlist__set_selected(struct perf_evlist *evlist,
                               struct perf_evsel *evsel);
 
-static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
-                                        struct cpu_map *cpus,
-                                        struct thread_map *threads)
-{
-       evlist->cpus    = cpus;
-       evlist->threads = threads;
-}
-
+int perf_evlist__set_maps(struct perf_evlist *evlist,
+                         struct cpu_map *cpus,
+                         struct thread_map *threads);
 int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);
 int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel);
 
index 2936b308072200b79f54820a4bf63b0895704bbf..71f6905c7cb9f5ef85e4b19e290082f752d35756 100644 (file)
@@ -210,6 +210,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
        perf_evsel__object.init(evsel);
        evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
        perf_evsel__calc_id_pos(evsel);
+       evsel->cmdline_group_boundary = false;
 }
 
 struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
@@ -707,7 +708,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
         */
        if (opts->sample_time &&
            (!perf_missing_features.sample_id_all &&
-           (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu)))
+           (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu ||
+            opts->sample_time_set)))
                perf_evsel__set_sample_bit(evsel, TIME);
 
        if (opts->raw_samples && !evsel->no_aux_samples) {
@@ -736,6 +738,9 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
        attr->mmap2 = track && !perf_missing_features.mmap2;
        attr->comm  = track;
 
+       if (opts->record_switch_events)
+               attr->context_switch = track;
+
        if (opts->sample_transaction)
                perf_evsel__set_sample_bit(evsel, TRANSACTION);
 
@@ -815,14 +820,44 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea
        return 0;
 }
 
-int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
-                          const char *filter)
+int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+                            const char *filter)
 {
        return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
                                     PERF_EVENT_IOC_SET_FILTER,
                                     (void *)filter);
 }
 
+int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter)
+{
+       char *new_filter = strdup(filter);
+
+       if (new_filter != NULL) {
+               free(evsel->filter);
+               evsel->filter = new_filter;
+               return 0;
+       }
+
+       return -1;
+}
+
+int perf_evsel__append_filter(struct perf_evsel *evsel,
+                             const char *op, const char *filter)
+{
+       char *new_filter;
+
+       if (evsel->filter == NULL)
+               return perf_evsel__set_filter(evsel, filter);
+
+       if (asprintf(&new_filter,"(%s) %s (%s)", evsel->filter, op, filter) > 0) {
+               free(evsel->filter);
+               evsel->filter = new_filter;
+               return 0;
+       }
+
+       return -1;
+}
+
 int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
 {
        return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
@@ -1095,6 +1130,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
        PRINT_ATTRf(mmap2, p_unsigned);
        PRINT_ATTRf(comm_exec, p_unsigned);
        PRINT_ATTRf(use_clockid, p_unsigned);
+       PRINT_ATTRf(context_switch, p_unsigned);
 
        PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned);
        PRINT_ATTRf(bp_type, p_unsigned);
index 4a7ed5656cf0165ffcdd52a1dba063aff6051c8e..1fc263a80d91669dcc854bcbac0fb1bccaf0e6a1 100644 (file)
@@ -86,6 +86,7 @@ struct perf_evsel {
        unsigned long           *per_pkg_mask;
        struct perf_evsel       *leader;
        char                    *group_name;
+       bool                    cmdline_group_boundary;
 };
 
 union u64_swap {
@@ -182,8 +183,11 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
 void perf_evsel__set_sample_id(struct perf_evsel *evsel,
                               bool use_sample_identifier);
 
-int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
-                          const char *filter);
+int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter);
+int perf_evsel__append_filter(struct perf_evsel *evsel,
+                             const char *op, const char *filter);
+int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+                            const char *filter);
 int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads);
 
 int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
index 03ace57a800c58fcc5fb3b2be8cd8bfbf8f50977..179b2bdd157dac0adc33faf62fbe85c3853aa19f 100644 (file)
@@ -923,17 +923,13 @@ static void print_cmdline(struct perf_header *ph, int fd __maybe_unused,
                          FILE *fp)
 {
        int nr, i;
-       char *str;
 
        nr = ph->env.nr_cmdline;
-       str = ph->env.cmdline;
 
        fprintf(fp, "# cmdline : ");
 
-       for (i = 0; i < nr; i++) {
-               fprintf(fp, "%s ", str);
-               str += strlen(str) + 1;
-       }
+       for (i = 0; i < nr; i++)
+               fprintf(fp, "%s ", ph->env.cmdline_argv[i]);
        fputc('\n', fp);
 }
 
@@ -1541,14 +1537,13 @@ process_event_desc(struct perf_file_section *section __maybe_unused,
        return 0;
 }
 
-static int process_cmdline(struct perf_file_section *section __maybe_unused,
+static int process_cmdline(struct perf_file_section *section,
                           struct perf_header *ph, int fd,
                           void *data __maybe_unused)
 {
        ssize_t ret;
-       char *str;
-       u32 nr, i;
-       struct strbuf sb;
+       char *str, *cmdline = NULL, **argv = NULL;
+       u32 nr, i, len = 0;
 
        ret = readn(fd, &nr, sizeof(nr));
        if (ret != sizeof(nr))
@@ -1558,22 +1553,32 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused,
                nr = bswap_32(nr);
 
        ph->env.nr_cmdline = nr;
-       strbuf_init(&sb, 128);
+
+       cmdline = zalloc(section->size + nr + 1);
+       if (!cmdline)
+               return -1;
+
+       argv = zalloc(sizeof(char *) * (nr + 1));
+       if (!argv)
+               goto error;
 
        for (i = 0; i < nr; i++) {
                str = do_read_string(fd, ph);
                if (!str)
                        goto error;
 
-               /* include a NULL character at the end */
-               strbuf_add(&sb, str, strlen(str) + 1);
+               argv[i] = cmdline + len;
+               memcpy(argv[i], str, strlen(str) + 1);
+               len += strlen(str) + 1;
                free(str);
        }
-       ph->env.cmdline = strbuf_detach(&sb, NULL);
+       ph->env.cmdline = cmdline;
+       ph->env.cmdline_argv = (const char **) argv;
        return 0;
 
 error:
-       strbuf_release(&sb);
+       free(argv);
+       free(cmdline);
        return -1;
 }
 
index d4d57962c59129d6121b61921b1c1bf58fe2d12a..9b53b6525ce8ac00e712ef90736f7a9e1b95a306 100644 (file)
@@ -84,6 +84,7 @@ struct perf_session_env {
        int                     nr_pmu_mappings;
        int                     nr_groups;
        char                    *cmdline;
+       const char              **cmdline_argv;
        char                    *sibling_cores;
        char                    *sibling_threads;
        char                    *numa_nodes;
index 7ff682770fdb16e71b368af16efad0edd9443d87..be3e00891d225c12cc3aae999db4433a279c5bfe 100644 (file)
@@ -250,7 +250,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid)
                        static struct strlist *seen;
 
                        if (!seen)
-                               seen = strlist__new(true, NULL);
+                               seen = strlist__new(NULL, NULL);
 
                        if (!strlist__has_entry(seen, path)) {
                                pr_err("Can't access file %s\n", path);
@@ -550,6 +550,14 @@ int machine__process_itrace_start_event(struct machine *machine __maybe_unused,
        return 0;
 }
 
+int machine__process_switch_event(struct machine *machine __maybe_unused,
+                                 union perf_event *event)
+{
+       if (dump_trace)
+               perf_event__fprintf_switch(event, stdout);
+       return 0;
+}
+
 struct map *machine__findnew_module_map(struct machine *machine, u64 start,
                                        const char *filename)
 {
@@ -1451,6 +1459,9 @@ int machine__process_event(struct machine *machine, union perf_event *event,
                ret = machine__process_itrace_start_event(machine, event); break;
        case PERF_RECORD_LOST_SAMPLES:
                ret = machine__process_lost_samples_event(machine, event, sample); break;
+       case PERF_RECORD_SWITCH:
+       case PERF_RECORD_SWITCH_CPU_WIDE:
+               ret = machine__process_switch_event(machine, event); break;
        default:
                ret = -1;
                break;
@@ -1993,3 +2004,17 @@ struct dso *machine__findnew_dso(struct machine *machine, const char *filename)
 {
        return dsos__findnew(&machine->dsos, filename);
 }
+
+char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp)
+{
+       struct machine *machine = vmachine;
+       struct map *map;
+       struct symbol *sym = map_groups__find_symbol(&machine->kmaps, MAP__FUNCTION, *addrp, &map,  NULL);
+
+       if (sym == NULL)
+               return NULL;
+
+       *modp = __map__is_kmodule(map) ? (char *)map->dso->short_name : NULL;
+       *addrp = map->unmap_ip(map, sym->start);
+       return sym->name;
+}
index 887798e511e9f3dd382109927860269b03744246..ea5cb4a621db80056f008d3e3f71ddecbda761d8 100644 (file)
@@ -87,6 +87,8 @@ int machine__process_aux_event(struct machine *machine,
                               union perf_event *event);
 int machine__process_itrace_start_event(struct machine *machine,
                                        union perf_event *event);
+int machine__process_switch_event(struct machine *machine __maybe_unused,
+                                 union perf_event *event);
 int machine__process_mmap_event(struct machine *machine, union perf_event *event,
                                struct perf_sample *sample);
 int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
@@ -237,5 +239,9 @@ int machine__synthesize_threads(struct machine *machine, struct target *target,
 pid_t machine__get_current_tid(struct machine *machine, int cpu);
 int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
                             pid_t tid);
+/*
+ * For use with libtraceevent's pevent_set_function_resolver()
+ */
+char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp);
 
 #endif /* __PERF_MACHINE_H */
index b5a5e9c024379652fccd722d7da2739169e0ab5d..ce37e95bc51347cf2e3d0f650a64ca4b405c8d2e 100644 (file)
@@ -224,6 +224,20 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
        return map;
 }
 
+/*
+ * Use this and __map__is_kmodule() for map instances that are in
+ * machine->kmaps, and thus have map->groups->machine all properly set, to
+ * disambiguate between the kernel and modules.
+ *
+ * When the need arises, introduce map__is_{kernel,kmodule)() that
+ * checks (map->groups != NULL && map->groups->machine != NULL &&
+ * map->dso->kernel) before calling __map__is_{kernel,kmodule}())
+ */
+bool __map__is_kernel(const struct map *map)
+{
+       return map->groups->machine->vmlinux_maps[map->type] == map;
+}
+
 static void map__exit(struct map *map)
 {
        BUG_ON(!RB_EMPTY_NODE(&map->rb_node));
index d73e687b224e4e0d3b427f1244695ea4e19c4fcb..57829e89b78b2f22e1977bb7c2ca2c315bcfa5f0 100644 (file)
@@ -256,4 +256,11 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
 struct map *map_groups__find_by_name(struct map_groups *mg,
                                     enum map_type type, const char *name);
 
+bool __map__is_kernel(const struct map *map);
+
+static inline bool __map__is_kmodule(const struct map *map)
+{
+       return !__map__is_kernel(map);
+}
+
 #endif /* __PERF_MAP_H */
index 09f8d23571082ef6698f37eefcd8f9d9815f1fdc..4f807fc1b14a4783d5be8ec3e94b17b2253b3ab4 100644 (file)
@@ -1065,8 +1065,13 @@ int parse_events(struct perf_evlist *evlist, const char *str,
        perf_pmu__parse_cleanup();
        if (!ret) {
                int entries = data.idx - evlist->nr_entries;
+               struct perf_evsel *last;
+
                perf_evlist__splice_list_tail(evlist, &data.list, entries);
                evlist->nr_groups += data.nr_groups;
+               last = perf_evlist__last(evlist);
+               last->cmdline_group_boundary = true;
+
                return 0;
        }
 
@@ -1162,30 +1167,93 @@ int parse_events_option(const struct option *opt, const char *str,
        return ret;
 }
 
-int parse_filter(const struct option *opt, const char *str,
-                int unset __maybe_unused)
+static int
+foreach_evsel_in_last_glob(struct perf_evlist *evlist,
+                          int (*func)(struct perf_evsel *evsel,
+                                      const void *arg),
+                          const void *arg)
 {
-       struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
        struct perf_evsel *last = NULL;
+       int err;
 
        if (evlist->nr_entries > 0)
                last = perf_evlist__last(evlist);
 
-       if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
+       do {
+               err = (*func)(last, arg);
+               if (err)
+                       return -1;
+               if (!last)
+                       return 0;
+
+               if (last->node.prev == &evlist->entries)
+                       return 0;
+               last = list_entry(last->node.prev, struct perf_evsel, node);
+       } while (!last->cmdline_group_boundary);
+
+       return 0;
+}
+
+static int set_filter(struct perf_evsel *evsel, const void *arg)
+{
+       const char *str = arg;
+
+       if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
                fprintf(stderr,
                        "--filter option should follow a -e tracepoint option\n");
                return -1;
        }
 
-       last->filter = strdup(str);
-       if (last->filter == NULL) {
-               fprintf(stderr, "not enough memory to hold filter string\n");
+       if (perf_evsel__append_filter(evsel, "&&", str) < 0) {
+               fprintf(stderr,
+                       "not enough memory to hold filter string\n");
                return -1;
        }
 
        return 0;
 }
 
+int parse_filter(const struct option *opt, const char *str,
+                int unset __maybe_unused)
+{
+       struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+
+       return foreach_evsel_in_last_glob(evlist, set_filter,
+                                         (const void *)str);
+}
+
+static int add_exclude_perf_filter(struct perf_evsel *evsel,
+                                  const void *arg __maybe_unused)
+{
+       char new_filter[64];
+
+       if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+               fprintf(stderr,
+                       "--exclude-perf option should follow a -e tracepoint option\n");
+               return -1;
+       }
+
+       snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid());
+
+       if (perf_evsel__append_filter(evsel, "&&", new_filter) < 0) {
+               fprintf(stderr,
+                       "not enough memory to hold filter string\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+int exclude_perf(const struct option *opt,
+                const char *arg __maybe_unused,
+                int unset __maybe_unused)
+{
+       struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+
+       return foreach_evsel_in_last_glob(evlist, add_exclude_perf_filter,
+                                         NULL);
+}
+
 static const char * const event_type_descriptors[] = {
        "Hardware event",
        "Software event",
index 131f29b2f13258d647276820026207526a08ef02..2063048a435416ef80dd8dccd79d4048a8034763 100644 (file)
@@ -34,6 +34,7 @@ extern int parse_events(struct perf_evlist *evlist, const char *str,
                        struct parse_events_error *error);
 extern int parse_events_terms(struct list_head *terms, const char *str);
 extern int parse_filter(const struct option *opt, const char *str, int unset);
+extern int exclude_perf(const struct option *opt, const char *arg, int unset);
 
 #define EVENTS_HELP_MAX (128*1024)
 
index 381f23a443c7e71a78c008cb2e56a88326830d98..fe4941a94a250dd03cd262963eb4bb1ef281dc7f 100644 (file)
@@ -45,6 +45,7 @@
 #include "trace-event.h"       /* For __maybe_unused */
 #include "probe-event.h"
 #include "probe-finder.h"
+#include "probe-file.h"
 #include "session.h"
 
 #define MAX_CMDLEN 256
@@ -55,11 +56,7 @@ struct probe_conf probe_conf;
 
 #define semantic_error(msg ...) pr_err("Semantic error :" msg)
 
-/* If there is no space to write, returns -E2BIG. */
-static int e_snprintf(char *str, size_t size, const char *format, ...)
-       __attribute__((format(printf, 3, 4)));
-
-static int e_snprintf(char *str, size_t size, const char *format, ...)
+int e_snprintf(char *str, size_t size, const char *format, ...)
 {
        int ret;
        va_list ap;
@@ -72,7 +69,6 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
 }
 
 static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
-static void clear_probe_trace_event(struct probe_trace_event *tev);
 static struct machine *host_machine;
 
 /* Initialize symbol maps and path of vmlinux/modules */
@@ -1467,8 +1463,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
 }
 
 /* Parse probe_events event into struct probe_point */
-static int parse_probe_trace_command(const char *cmd,
-                                    struct probe_trace_event *tev)
+int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
 {
        struct probe_trace_point *tp = &tev->point;
        char pr;
@@ -1951,7 +1946,7 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
        memset(pev, 0, sizeof(*pev));
 }
 
-static void clear_probe_trace_event(struct probe_trace_event *tev)
+void clear_probe_trace_event(struct probe_trace_event *tev)
 {
        struct probe_trace_arg_ref *ref, *next;
        int i;
@@ -1976,119 +1971,6 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
        memset(tev, 0, sizeof(*tev));
 }
 
-static void print_open_warning(int err, bool is_kprobe)
-{
-       char sbuf[STRERR_BUFSIZE];
-
-       if (err == -ENOENT) {
-               const char *config;
-
-               if (!is_kprobe)
-                       config = "CONFIG_UPROBE_EVENTS";
-               else
-                       config = "CONFIG_KPROBE_EVENTS";
-
-               pr_warning("%cprobe_events file does not exist"
-                          " - please rebuild kernel with %s.\n",
-                          is_kprobe ? 'k' : 'u', config);
-       } else if (err == -ENOTSUP)
-               pr_warning("Tracefs or debugfs is not mounted.\n");
-       else
-               pr_warning("Failed to open %cprobe_events: %s\n",
-                          is_kprobe ? 'k' : 'u',
-                          strerror_r(-err, sbuf, sizeof(sbuf)));
-}
-
-static void print_both_open_warning(int kerr, int uerr)
-{
-       /* Both kprobes and uprobes are disabled, warn it. */
-       if (kerr == -ENOTSUP && uerr == -ENOTSUP)
-               pr_warning("Tracefs or debugfs is not mounted.\n");
-       else if (kerr == -ENOENT && uerr == -ENOENT)
-               pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS "
-                          "or/and CONFIG_UPROBE_EVENTS.\n");
-       else {
-               char sbuf[STRERR_BUFSIZE];
-               pr_warning("Failed to open kprobe events: %s.\n",
-                          strerror_r(-kerr, sbuf, sizeof(sbuf)));
-               pr_warning("Failed to open uprobe events: %s.\n",
-                          strerror_r(-uerr, sbuf, sizeof(sbuf)));
-       }
-}
-
-static int open_probe_events(const char *trace_file, bool readwrite)
-{
-       char buf[PATH_MAX];
-       const char *__debugfs;
-       const char *tracing_dir = "";
-       int ret;
-
-       __debugfs = tracefs_find_mountpoint();
-       if (__debugfs == NULL) {
-               tracing_dir = "tracing/";
-
-               __debugfs = debugfs_find_mountpoint();
-               if (__debugfs == NULL)
-                       return -ENOTSUP;
-       }
-
-       ret = e_snprintf(buf, PATH_MAX, "%s/%s%s",
-                        __debugfs, tracing_dir, trace_file);
-       if (ret >= 0) {
-               pr_debug("Opening %s write=%d\n", buf, readwrite);
-               if (readwrite && !probe_event_dry_run)
-                       ret = open(buf, O_RDWR | O_APPEND, 0);
-               else
-                       ret = open(buf, O_RDONLY, 0);
-
-               if (ret < 0)
-                       ret = -errno;
-       }
-       return ret;
-}
-
-static int open_kprobe_events(bool readwrite)
-{
-       return open_probe_events("kprobe_events", readwrite);
-}
-
-static int open_uprobe_events(bool readwrite)
-{
-       return open_probe_events("uprobe_events", readwrite);
-}
-
-/* Get raw string list of current kprobe_events  or uprobe_events */
-static struct strlist *get_probe_trace_command_rawlist(int fd)
-{
-       int ret, idx;
-       FILE *fp;
-       char buf[MAX_CMDLEN];
-       char *p;
-       struct strlist *sl;
-
-       sl = strlist__new(true, NULL);
-
-       fp = fdopen(dup(fd), "r");
-       while (!feof(fp)) {
-               p = fgets(buf, MAX_CMDLEN, fp);
-               if (!p)
-                       break;
-
-               idx = strlen(p) - 1;
-               if (p[idx] == '\n')
-                       p[idx] = '\0';
-               ret = strlist__add(sl, buf);
-               if (ret < 0) {
-                       pr_debug("strlist__add failed (%d)\n", ret);
-                       strlist__delete(sl);
-                       return NULL;
-               }
-       }
-       fclose(fp);
-
-       return sl;
-}
-
 struct kprobe_blacklist_node {
        struct list_head list;
        unsigned long start;
@@ -2284,7 +2166,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe,
        memset(&tev, 0, sizeof(tev));
        memset(&pev, 0, sizeof(pev));
 
-       rawlist = get_probe_trace_command_rawlist(fd);
+       rawlist = probe_file__get_rawlist(fd);
        if (!rawlist)
                return -ENOMEM;
 
@@ -2325,89 +2207,20 @@ int show_perf_probe_events(struct strfilter *filter)
        if (ret < 0)
                return ret;
 
-       kp_fd = open_kprobe_events(false);
-       if (kp_fd >= 0) {
-               ret = __show_perf_probe_events(kp_fd, true, filter);
-               close(kp_fd);
-               if (ret < 0)
-                       goto out;
-       }
-
-       up_fd = open_uprobe_events(false);
-       if (kp_fd < 0 && up_fd < 0) {
-               print_both_open_warning(kp_fd, up_fd);
-               ret = kp_fd;
-               goto out;
-       }
+       ret = probe_file__open_both(&kp_fd, &up_fd, 0);
+       if (ret < 0)
+               return ret;
 
-       if (up_fd >= 0) {
+       if (kp_fd >= 0)
+               ret = __show_perf_probe_events(kp_fd, true, filter);
+       if (up_fd >= 0 && ret >= 0)
                ret = __show_perf_probe_events(up_fd, false, filter);
+       if (kp_fd > 0)
+               close(kp_fd);
+       if (up_fd > 0)
                close(up_fd);
-       }
-out:
        exit_symbol_maps();
-       return ret;
-}
-
-/* Get current perf-probe event names */
-static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
-{
-       char buf[128];
-       struct strlist *sl, *rawlist;
-       struct str_node *ent;
-       struct probe_trace_event tev;
-       int ret = 0;
-
-       memset(&tev, 0, sizeof(tev));
-       rawlist = get_probe_trace_command_rawlist(fd);
-       if (!rawlist)
-               return NULL;
-       sl = strlist__new(true, NULL);
-       strlist__for_each(ent, rawlist) {
-               ret = parse_probe_trace_command(ent->s, &tev);
-               if (ret < 0)
-                       break;
-               if (include_group) {
-                       ret = e_snprintf(buf, 128, "%s:%s", tev.group,
-                                       tev.event);
-                       if (ret >= 0)
-                               ret = strlist__add(sl, buf);
-               } else
-                       ret = strlist__add(sl, tev.event);
-               clear_probe_trace_event(&tev);
-               if (ret < 0)
-                       break;
-       }
-       strlist__delete(rawlist);
-
-       if (ret < 0) {
-               strlist__delete(sl);
-               return NULL;
-       }
-       return sl;
-}
-
-static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
-{
-       int ret = 0;
-       char *buf = synthesize_probe_trace_command(tev);
-       char sbuf[STRERR_BUFSIZE];
-
-       if (!buf) {
-               pr_debug("Failed to synthesize probe trace event.\n");
-               return -EINVAL;
-       }
 
-       pr_debug("Writing event: %s\n", buf);
-       if (!probe_event_dry_run) {
-               ret = write(fd, buf, strlen(buf));
-               if (ret <= 0) {
-                       ret = -errno;
-                       pr_warning("Failed to write event: %s\n",
-                                  strerror_r(errno, sbuf, sizeof(sbuf)));
-               }
-       }
-       free(buf);
        return ret;
 }
 
@@ -2478,36 +2291,67 @@ out:
        free(buf);
 }
 
+/* Set new name from original perf_probe_event and namelist */
+static int probe_trace_event__set_name(struct probe_trace_event *tev,
+                                      struct perf_probe_event *pev,
+                                      struct strlist *namelist,
+                                      bool allow_suffix)
+{
+       const char *event, *group;
+       char buf[64];
+       int ret;
+
+       if (pev->event)
+               event = pev->event;
+       else
+               if (pev->point.function && !strisglob(pev->point.function))
+                       event = pev->point.function;
+               else
+                       event = tev->point.realname;
+       if (pev->group)
+               group = pev->group;
+       else
+               group = PERFPROBE_GROUP;
+
+       /* Get an unused new event name */
+       ret = get_new_event_name(buf, 64, event,
+                                namelist, allow_suffix);
+       if (ret < 0)
+               return ret;
+
+       event = buf;
+
+       tev->event = strdup(event);
+       tev->group = strdup(group);
+       if (tev->event == NULL || tev->group == NULL)
+               return -ENOMEM;
+
+       /* Add added event name to namelist */
+       strlist__add(namelist, event);
+       return 0;
+}
+
 static int __add_probe_trace_events(struct perf_probe_event *pev,
                                     struct probe_trace_event *tevs,
                                     int ntevs, bool allow_suffix)
 {
        int i, fd, ret;
        struct probe_trace_event *tev = NULL;
-       char buf[64];
        const char *event = NULL, *group = NULL;
        struct strlist *namelist;
-       bool safename;
-
-       if (pev->uprobes)
-               fd = open_uprobe_events(true);
-       else
-               fd = open_kprobe_events(true);
 
-       if (fd < 0) {
-               print_open_warning(fd, !pev->uprobes);
+       fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
+       if (fd < 0)
                return fd;
-       }
 
        /* Get current event names */
-       namelist = get_probe_trace_event_names(fd, false);
+       namelist = probe_file__get_namelist(fd);
        if (!namelist) {
                pr_debug("Failed to get current event list.\n");
                ret = -ENOMEM;
                goto close_out;
        }
 
-       safename = (pev->point.function && !strisglob(pev->point.function));
        ret = 0;
        pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
        for (i = 0; i < ntevs; i++) {
@@ -2516,36 +2360,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
                if (!tev->point.symbol)
                        continue;
 
-               if (pev->event)
-                       event = pev->event;
-               else
-                       if (safename)
-                               event = pev->point.function;
-                       else
-                               event = tev->point.realname;
-               if (pev->group)
-                       group = pev->group;
-               else
-                       group = PERFPROBE_GROUP;
-
-               /* Get an unused new event name */
-               ret = get_new_event_name(buf, 64, event,
-                                        namelist, allow_suffix);
+               /* Set new name for tev (and update namelist) */
+               ret = probe_trace_event__set_name(tev, pev, namelist,
+                                                 allow_suffix);
                if (ret < 0)
                        break;
-               event = buf;
 
-               tev->event = strdup(event);
-               tev->group = strdup(group);
-               if (tev->event == NULL || tev->group == NULL) {
-                       ret = -ENOMEM;
-                       break;
-               }
-               ret = write_probe_trace_event(fd, tev);
+               ret = probe_file__add_event(fd, tev);
                if (ret < 0)
                        break;
-               /* Add added event name to namelist */
-               strlist__add(namelist, event);
 
                /* We use tev's name for showing new events */
                show_perf_probe_event(tev->group, tev->event, pev,
@@ -2838,68 +2661,9 @@ end:
        return ret;
 }
 
-static int __del_trace_probe_event(int fd, struct str_node *ent)
-{
-       char *p;
-       char buf[128];
-       int ret;
-
-       /* Convert from perf-probe event to trace-probe event */
-       ret = e_snprintf(buf, 128, "-:%s", ent->s);
-       if (ret < 0)
-               goto error;
-
-       p = strchr(buf + 2, ':');
-       if (!p) {
-               pr_debug("Internal error: %s should have ':' but not.\n",
-                        ent->s);
-               ret = -ENOTSUP;
-               goto error;
-       }
-       *p = '/';
-
-       pr_debug("Writing event: %s\n", buf);
-       ret = write(fd, buf, strlen(buf));
-       if (ret < 0) {
-               ret = -errno;
-               goto error;
-       }
-
-       pr_info("Removed event: %s\n", ent->s);
-       return 0;
-error:
-       pr_warning("Failed to delete event: %s\n",
-                  strerror_r(-ret, buf, sizeof(buf)));
-       return ret;
-}
-
-static int del_trace_probe_events(int fd, struct strfilter *filter,
-                                 struct strlist *namelist)
-{
-       struct str_node *ent;
-       const char *p;
-       int ret = -ENOENT;
-
-       if (!namelist)
-               return -ENOENT;
-
-       strlist__for_each(ent, namelist) {
-               p = strchr(ent->s, ':');
-               if ((p && strfilter__compare(filter, p + 1)) ||
-                   strfilter__compare(filter, ent->s)) {
-                       ret = __del_trace_probe_event(fd, ent);
-                       if (ret < 0)
-                               break;
-               }
-       }
-
-       return ret;
-}
-
 int del_perf_probe_events(struct strfilter *filter)
 {
        int ret, ret2, ufd = -1, kfd = -1;
-       struct strlist *namelist = NULL, *unamelist = NULL;
        char *str = strfilter__string(filter);
 
        if (!str)
@@ -2908,25 +2672,15 @@ int del_perf_probe_events(struct strfilter *filter)
        pr_debug("Delete filter: \'%s\'\n", str);
 
        /* Get current event names */
-       kfd = open_kprobe_events(true);
-       if (kfd >= 0)
-               namelist = get_probe_trace_event_names(kfd, true);
-
-       ufd = open_uprobe_events(true);
-       if (ufd >= 0)
-               unamelist = get_probe_trace_event_names(ufd, true);
-
-       if (kfd < 0 && ufd < 0) {
-               print_both_open_warning(kfd, ufd);
-               ret = kfd;
-               goto error;
-       }
+       ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
+       if (ret < 0)
+               goto out;
 
-       ret = del_trace_probe_events(kfd, filter, namelist);
+       ret = probe_file__del_events(kfd, filter);
        if (ret < 0 && ret != -ENOENT)
                goto error;
 
-       ret2 = del_trace_probe_events(ufd, filter, unamelist);
+       ret2 = probe_file__del_events(ufd, filter);
        if (ret2 < 0 && ret2 != -ENOENT) {
                ret = ret2;
                goto error;
@@ -2937,15 +2691,11 @@ int del_perf_probe_events(struct strfilter *filter)
        ret = 0;
 
 error:
-       if (kfd >= 0) {
-               strlist__delete(namelist);
+       if (kfd >= 0)
                close(kfd);
-       }
-
-       if (ufd >= 0) {
-               strlist__delete(unamelist);
+       if (ufd >= 0)
                close(ufd);
-       }
+out:
        free(str);
 
        return ret;
index 31db6ee7db5478139dabfc97537568c0ec104736..20f555d1ae1c5142dbe7b3382d319397ac1d4284 100644 (file)
@@ -109,6 +109,8 @@ struct variable_list {
 /* Command string to events */
 extern int parse_perf_probe_command(const char *cmd,
                                    struct perf_probe_event *pev);
+extern int parse_probe_trace_command(const char *cmd,
+                                    struct probe_trace_event *tev);
 
 /* Events to command string */
 extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
@@ -121,6 +123,7 @@ extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
 
 /* Release event contents */
 extern void clear_perf_probe_event(struct perf_probe_event *pev);
+extern void clear_probe_trace_event(struct probe_trace_event *tev);
 
 /* Command string to line-range */
 extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
@@ -144,6 +147,10 @@ bool arch__prefers_symtab(void);
 void arch__fix_tev_from_maps(struct perf_probe_event *pev,
                             struct probe_trace_event *tev, struct map *map);
 
+/* If there is no space to write, returns -E2BIG. */
+int e_snprintf(char *str, size_t size, const char *format, ...)
+       __attribute__((format(printf, 3, 4)));
+
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX        1024
 
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
new file mode 100644 (file)
index 0000000..bbb2437
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * probe-file.c : operate ftrace k/uprobe events files
+ *
+ * Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "util.h"
+#include "event.h"
+#include "strlist.h"
+#include "debug.h"
+#include "cache.h"
+#include "color.h"
+#include "symbol.h"
+#include "thread.h"
+#include <api/fs/debugfs.h>
+#include <api/fs/tracefs.h>
+#include "probe-event.h"
+#include "probe-file.h"
+#include "session.h"
+
+#define MAX_CMDLEN 256
+
+static void print_open_warning(int err, bool uprobe)
+{
+       char sbuf[STRERR_BUFSIZE];
+
+       if (err == -ENOENT) {
+               const char *config;
+
+               if (uprobe)
+                       config = "CONFIG_UPROBE_EVENTS";
+               else
+                       config = "CONFIG_KPROBE_EVENTS";
+
+               pr_warning("%cprobe_events file does not exist"
+                          " - please rebuild kernel with %s.\n",
+                          uprobe ? 'u' : 'k', config);
+       } else if (err == -ENOTSUP)
+               pr_warning("Tracefs or debugfs is not mounted.\n");
+       else
+               pr_warning("Failed to open %cprobe_events: %s\n",
+                          uprobe ? 'u' : 'k',
+                          strerror_r(-err, sbuf, sizeof(sbuf)));
+}
+
+static void print_both_open_warning(int kerr, int uerr)
+{
+       /* Both kprobes and uprobes are disabled, warn it. */
+       if (kerr == -ENOTSUP && uerr == -ENOTSUP)
+               pr_warning("Tracefs or debugfs is not mounted.\n");
+       else if (kerr == -ENOENT && uerr == -ENOENT)
+               pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS "
+                          "or/and CONFIG_UPROBE_EVENTS.\n");
+       else {
+               char sbuf[STRERR_BUFSIZE];
+               pr_warning("Failed to open kprobe events: %s.\n",
+                          strerror_r(-kerr, sbuf, sizeof(sbuf)));
+               pr_warning("Failed to open uprobe events: %s.\n",
+                          strerror_r(-uerr, sbuf, sizeof(sbuf)));
+       }
+}
+
+static int open_probe_events(const char *trace_file, bool readwrite)
+{
+       char buf[PATH_MAX];
+       const char *__debugfs;
+       const char *tracing_dir = "";
+       int ret;
+
+       __debugfs = tracefs_find_mountpoint();
+       if (__debugfs == NULL) {
+               tracing_dir = "tracing/";
+
+               __debugfs = debugfs_find_mountpoint();
+               if (__debugfs == NULL)
+                       return -ENOTSUP;
+       }
+
+       ret = e_snprintf(buf, PATH_MAX, "%s/%s%s",
+                        __debugfs, tracing_dir, trace_file);
+       if (ret >= 0) {
+               pr_debug("Opening %s write=%d\n", buf, readwrite);
+               if (readwrite && !probe_event_dry_run)
+                       ret = open(buf, O_RDWR | O_APPEND, 0);
+               else
+                       ret = open(buf, O_RDONLY, 0);
+
+               if (ret < 0)
+                       ret = -errno;
+       }
+       return ret;
+}
+
+static int open_kprobe_events(bool readwrite)
+{
+       return open_probe_events("kprobe_events", readwrite);
+}
+
+static int open_uprobe_events(bool readwrite)
+{
+       return open_probe_events("uprobe_events", readwrite);
+}
+
+int probe_file__open(int flag)
+{
+       int fd;
+
+       if (flag & PF_FL_UPROBE)
+               fd = open_uprobe_events(flag & PF_FL_RW);
+       else
+               fd = open_kprobe_events(flag & PF_FL_RW);
+       if (fd < 0)
+               print_open_warning(fd, flag & PF_FL_UPROBE);
+
+       return fd;
+}
+
+int probe_file__open_both(int *kfd, int *ufd, int flag)
+{
+       if (!kfd || !ufd)
+               return -EINVAL;
+
+       *kfd = open_kprobe_events(flag & PF_FL_RW);
+       *ufd = open_uprobe_events(flag & PF_FL_RW);
+       if (*kfd < 0 && *ufd < 0) {
+               print_both_open_warning(*kfd, *ufd);
+               return *kfd;
+       }
+
+       return 0;
+}
+
+/* Get raw string list of current kprobe_events  or uprobe_events */
+struct strlist *probe_file__get_rawlist(int fd)
+{
+       int ret, idx;
+       FILE *fp;
+       char buf[MAX_CMDLEN];
+       char *p;
+       struct strlist *sl;
+
+       sl = strlist__new(NULL, NULL);
+
+       fp = fdopen(dup(fd), "r");
+       while (!feof(fp)) {
+               p = fgets(buf, MAX_CMDLEN, fp);
+               if (!p)
+                       break;
+
+               idx = strlen(p) - 1;
+               if (p[idx] == '\n')
+                       p[idx] = '\0';
+               ret = strlist__add(sl, buf);
+               if (ret < 0) {
+                       pr_debug("strlist__add failed (%d)\n", ret);
+                       strlist__delete(sl);
+                       return NULL;
+               }
+       }
+       fclose(fp);
+
+       return sl;
+}
+
+static struct strlist *__probe_file__get_namelist(int fd, bool include_group)
+{
+       char buf[128];
+       struct strlist *sl, *rawlist;
+       struct str_node *ent;
+       struct probe_trace_event tev;
+       int ret = 0;
+
+       memset(&tev, 0, sizeof(tev));
+       rawlist = probe_file__get_rawlist(fd);
+       if (!rawlist)
+               return NULL;
+       sl = strlist__new(NULL, NULL);
+       strlist__for_each(ent, rawlist) {
+               ret = parse_probe_trace_command(ent->s, &tev);
+               if (ret < 0)
+                       break;
+               if (include_group) {
+                       ret = e_snprintf(buf, 128, "%s:%s", tev.group,
+                                       tev.event);
+                       if (ret >= 0)
+                               ret = strlist__add(sl, buf);
+               } else
+                       ret = strlist__add(sl, tev.event);
+               clear_probe_trace_event(&tev);
+               if (ret < 0)
+                       break;
+       }
+       strlist__delete(rawlist);
+
+       if (ret < 0) {
+               strlist__delete(sl);
+               return NULL;
+       }
+       return sl;
+}
+
+/* Get current perf-probe event names */
+struct strlist *probe_file__get_namelist(int fd)
+{
+       return __probe_file__get_namelist(fd, false);
+}
+
+int probe_file__add_event(int fd, struct probe_trace_event *tev)
+{
+       int ret = 0;
+       char *buf = synthesize_probe_trace_command(tev);
+       char sbuf[STRERR_BUFSIZE];
+
+       if (!buf) {
+               pr_debug("Failed to synthesize probe trace event.\n");
+               return -EINVAL;
+       }
+
+       pr_debug("Writing event: %s\n", buf);
+       if (!probe_event_dry_run) {
+               ret = write(fd, buf, strlen(buf));
+               if (ret <= 0) {
+                       ret = -errno;
+                       pr_warning("Failed to write event: %s\n",
+                                  strerror_r(errno, sbuf, sizeof(sbuf)));
+               }
+       }
+       free(buf);
+
+       return ret;
+}
+
+static int __del_trace_probe_event(int fd, struct str_node *ent)
+{
+       char *p;
+       char buf[128];
+       int ret;
+
+       /* Convert from perf-probe event to trace-probe event */
+       ret = e_snprintf(buf, 128, "-:%s", ent->s);
+       if (ret < 0)
+               goto error;
+
+       p = strchr(buf + 2, ':');
+       if (!p) {
+               pr_debug("Internal error: %s should have ':' but not.\n",
+                        ent->s);
+               ret = -ENOTSUP;
+               goto error;
+       }
+       *p = '/';
+
+       pr_debug("Writing event: %s\n", buf);
+       ret = write(fd, buf, strlen(buf));
+       if (ret < 0) {
+               ret = -errno;
+               goto error;
+       }
+
+       pr_info("Removed event: %s\n", ent->s);
+       return 0;
+error:
+       pr_warning("Failed to delete event: %s\n",
+                  strerror_r(-ret, buf, sizeof(buf)));
+       return ret;
+}
+
+int probe_file__del_events(int fd, struct strfilter *filter)
+{
+       struct strlist *namelist;
+       struct str_node *ent;
+       const char *p;
+       int ret = -ENOENT;
+
+       namelist = __probe_file__get_namelist(fd, true);
+       if (!namelist)
+               return -ENOENT;
+
+       strlist__for_each(ent, namelist) {
+               p = strchr(ent->s, ':');
+               if ((p && strfilter__compare(filter, p + 1)) ||
+                   strfilter__compare(filter, ent->s)) {
+                       ret = __del_trace_probe_event(fd, ent);
+                       if (ret < 0)
+                               break;
+               }
+       }
+       strlist__delete(namelist);
+
+       return ret;
+}
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
new file mode 100644 (file)
index 0000000..ada94a2
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __PROBE_FILE_H
+#define __PROBE_FILE_H
+
+#include "strlist.h"
+#include "strfilter.h"
+#include "probe-event.h"
+
+#define PF_FL_UPROBE   1
+#define PF_FL_RW       2
+
+int probe_file__open(int flag);
+int probe_file__open_both(int *kfd, int *ufd, int flag);
+struct strlist *probe_file__get_namelist(int fd);
+struct strlist *probe_file__get_rawlist(int fd);
+int probe_file__add_event(int fd, struct probe_trace_event *tev);
+int probe_file__del_events(int fd, struct strfilter *filter);
+
+#endif
index 2da65a7108932857bd585681eb0fac195f0c850b..7b80f8cb62b95d6aade0f5a88a73ce31f6a6304c 100644 (file)
@@ -1355,7 +1355,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
                 vl->point.offset);
 
        /* Find local variables */
-       vl->vars = strlist__new(true, NULL);
+       vl->vars = strlist__new(NULL, NULL);
        if (vl->vars == NULL)
                return -ENOMEM;
        af->child = true;
index 1f7becbe5e182a4f560dd5f435148d302a5aa787..0d228a29526dded5d329e20ca6fe4f51bb94cf85 100644 (file)
@@ -85,6 +85,11 @@ static void perf_probe_comm_exec(struct perf_evsel *evsel)
        evsel->attr.comm_exec = 1;
 }
 
+static void perf_probe_context_switch(struct perf_evsel *evsel)
+{
+       evsel->attr.context_switch = 1;
+}
+
 bool perf_can_sample_identifier(void)
 {
        return perf_probe_api(perf_probe_sample_identifier);
@@ -95,6 +100,11 @@ static bool perf_can_comm_exec(void)
        return perf_probe_api(perf_probe_comm_exec);
 }
 
+bool perf_can_record_switch_events(void)
+{
+       return perf_probe_api(perf_probe_context_switch);
+}
+
 void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)
 {
        struct perf_evsel *evsel;
index ed9dc2555ec7277d01560c1133718e1066dc5630..2d9574710543dc79e0e8be14607e289f18a6d406 100644 (file)
@@ -180,6 +180,7 @@ static void perf_session_env__delete(struct perf_session_env *env)
        zfree(&env->cpuid);
 
        zfree(&env->cmdline);
+       zfree(&env->cmdline_argv);
        zfree(&env->sibling_cores);
        zfree(&env->sibling_threads);
        zfree(&env->numa_nodes);
@@ -332,6 +333,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
                tool->aux = perf_event__process_aux;
        if (tool->itrace_start == NULL)
                tool->itrace_start = perf_event__process_itrace_start;
+       if (tool->context_switch == NULL)
+               tool->context_switch = perf_event__process_switch;
        if (tool->read == NULL)
                tool->read = process_event_sample_stub;
        if (tool->throttle == NULL)
@@ -470,6 +473,19 @@ static void perf_event__itrace_start_swap(union perf_event *event,
                swap_sample_id_all(event, &event->itrace_start + 1);
 }
 
+static void perf_event__switch_swap(union perf_event *event, bool sample_id_all)
+{
+       if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) {
+               event->context_switch.next_prev_pid =
+                               bswap_32(event->context_switch.next_prev_pid);
+               event->context_switch.next_prev_tid =
+                               bswap_32(event->context_switch.next_prev_tid);
+       }
+
+       if (sample_id_all)
+               swap_sample_id_all(event, &event->context_switch + 1);
+}
+
 static void perf_event__throttle_swap(union perf_event *event,
                                      bool sample_id_all)
 {
@@ -632,6 +648,8 @@ static perf_event__swap_op perf_event__swap_ops[] = {
        [PERF_RECORD_AUX]                 = perf_event__aux_swap,
        [PERF_RECORD_ITRACE_START]        = perf_event__itrace_start_swap,
        [PERF_RECORD_LOST_SAMPLES]        = perf_event__all64_swap,
+       [PERF_RECORD_SWITCH]              = perf_event__switch_swap,
+       [PERF_RECORD_SWITCH_CPU_WIDE]     = perf_event__switch_swap,
        [PERF_RECORD_HEADER_ATTR]         = perf_event__hdr_attr_swap,
        [PERF_RECORD_HEADER_EVENT_TYPE]   = perf_event__event_type_swap,
        [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
@@ -1093,6 +1111,9 @@ static int machines__deliver_event(struct machines *machines,
                return tool->aux(tool, event, sample, machine);
        case PERF_RECORD_ITRACE_START:
                return tool->itrace_start(tool, event, sample, machine);
+       case PERF_RECORD_SWITCH:
+       case PERF_RECORD_SWITCH_CPU_WIDE:
+               return tool->context_switch(tool, event, sample, machine);
        default:
                ++evlist->stats.nr_unknown_events;
                return -1;
index 6afd6106ceb51c8d5d3997acb161a255cacffa84..fc8781de62dbbed618e1f220225dfaeb195ef7f0 100644 (file)
@@ -357,3 +357,42 @@ void *memdup(const void *src, size_t len)
 
        return p;
 }
+
+char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
+{
+       /*
+        * FIXME: replace this with an expression using log10() when we
+        * find a suitable implementation, maybe the one in the dvb drivers...
+        *
+        * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
+        */
+       size_t size = nints * 28 + 1; /* \0 */
+       size_t i, printed = 0;
+       char *expr = malloc(size);
+
+       if (expr) {
+               const char *or_and = "||", *eq_neq = "==";
+               char *e = expr;
+
+               if (!in) {
+                       or_and = "&&";
+                       eq_neq = "!=";
+               }
+
+               for (i = 0; i < nints; ++i) {
+                       if (printed == size)
+                               goto out_err_overflow;
+
+                       if (i > 0)
+                               printed += snprintf(e + printed, size - printed, " %s ", or_and);
+                       printed += scnprintf(e + printed, size - printed,
+                                            "%s %s %d", var, eq_neq, ints[i]);
+               }
+       }
+
+       return expr;
+
+out_err_overflow:
+       free(expr);
+       return NULL;
+}
index 71f9d102b96fc4aa9a94176151008083e275199f..bdf98f6f27bb1065ba9733021b5c68045324988a 100644 (file)
@@ -72,7 +72,7 @@ int strlist__load(struct strlist *slist, const char *filename)
        FILE *fp = fopen(filename, "r");
 
        if (fp == NULL)
-               return errno;
+               return -errno;
 
        while (fgets(entry, sizeof(entry), fp) != NULL) {
                const size_t len = strlen(entry);
@@ -108,43 +108,70 @@ struct str_node *strlist__find(struct strlist *slist, const char *entry)
        return snode;
 }
 
-static int strlist__parse_list_entry(struct strlist *slist, const char *s)
+static int strlist__parse_list_entry(struct strlist *slist, const char *s,
+                                    const char *subst_dir)
 {
+       int err;
+       char *subst = NULL;
+
        if (strncmp(s, "file://", 7) == 0)
                return strlist__load(slist, s + 7);
 
-       return strlist__add(slist, s);
+       if (subst_dir) {
+               err = -ENOMEM;
+               if (asprintf(&subst, "%s/%s", subst_dir, s) < 0)
+                       goto out;
+
+               if (access(subst, F_OK) == 0) {
+                       err = strlist__load(slist, subst);
+                       goto out;
+               }
+       }
+
+       err = strlist__add(slist, s);
+out:
+       free(subst);
+       return err;
 }
 
-int strlist__parse_list(struct strlist *slist, const char *s)
+static int strlist__parse_list(struct strlist *slist, const char *s, const char *subst_dir)
 {
        char *sep;
        int err;
 
        while ((sep = strchr(s, ',')) != NULL) {
                *sep = '\0';
-               err = strlist__parse_list_entry(slist, s);
+               err = strlist__parse_list_entry(slist, s, subst_dir);
                *sep = ',';
                if (err != 0)
                        return err;
                s = sep + 1;
        }
 
-       return *s ? strlist__parse_list_entry(slist, s) : 0;
+       return *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0;
 }
 
-struct strlist *strlist__new(bool dupstr, const char *list)
+struct strlist *strlist__new(const char *list, const struct strlist_config *config)
 {
        struct strlist *slist = malloc(sizeof(*slist));
 
        if (slist != NULL) {
+               bool dupstr = true;
+               const char *dirname = NULL;
+
+               if (config) {
+                       dupstr = !config->dont_dupstr;
+                       dirname = config->dirname;
+               }
+
                rblist__init(&slist->rblist);
                slist->rblist.node_cmp    = strlist__node_cmp;
                slist->rblist.node_new    = strlist__node_new;
                slist->rblist.node_delete = strlist__node_delete;
 
                slist->dupstr    = dupstr;
-               if (list && strlist__parse_list(slist, list) != 0)
+
+               if (list && strlist__parse_list(slist, list, dirname) != 0)
                        goto out_error;
        }
 
index 5c7f87069d9cd7bfa5412c186068840e76822b2e..297565aa753505d3f6f22d56f8b6545ee61bfa6b 100644 (file)
@@ -16,7 +16,12 @@ struct strlist {
        bool           dupstr;
 };
 
-struct strlist *strlist__new(bool dupstr, const char *slist);
+struct strlist_config {
+       bool dont_dupstr;
+       const char *dirname;
+};
+
+struct strlist *strlist__new(const char *slist, const struct strlist_config *config);
 void strlist__delete(struct strlist *slist);
 
 void strlist__remove(struct strlist *slist, struct str_node *sn);
@@ -74,6 +79,4 @@ static inline struct str_node *strlist__next(struct str_node *sn)
 #define strlist__for_each_safe(pos, n, slist)  \
        for (pos = strlist__first(slist), n = strlist__next(pos); pos;\
             pos = n, n = strlist__next(n))
-
-int strlist__parse_list(struct strlist *slist, const char *s);
 #endif /* __PERF_STRLIST_H */
index 60f11414bb5c8c6b463980f7b767662654e0331d..725640fd7cd80b88c6f2b17ae46fc77585cc1d33 100644 (file)
@@ -444,7 +444,12 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols,
 struct symbol *dso__find_symbol(struct dso *dso,
                                enum map_type type, u64 addr)
 {
-       return symbols__find(&dso->symbols[type], addr);
+       if (dso->last_find_result[type].addr != addr) {
+               dso->last_find_result[type].addr   = addr;
+               dso->last_find_result[type].symbol = symbols__find(&dso->symbols[type], addr);
+       }
+
+       return dso->last_find_result[type].symbol;
 }
 
 struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)
@@ -1906,7 +1911,7 @@ int setup_list(struct strlist **list, const char *list_str,
        if (list_str == NULL)
                return 0;
 
-       *list = strlist__new(true, list_str);
+       *list = strlist__new(list_str, NULL);
        if (!*list) {
                pr_err("problems parsing %s list\n", list_name);
                return -1;
index 292ae2c90e063d958da5f5859787c4c1c70ccab5..6ec3c5ca438f25c827dcfc1b7bde3491f8a5fe8c 100644 (file)
@@ -195,7 +195,8 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
        pid_t pid, prev_pid = INT_MAX;
        char *end_ptr;
        struct str_node *pos;
-       struct strlist *slist = strlist__new(false, pid_str);
+       struct strlist_config slist_config = { .dont_dupstr = true, };
+       struct strlist *slist = strlist__new(pid_str, &slist_config);
 
        if (!slist)
                return NULL;
@@ -265,13 +266,14 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
        pid_t tid, prev_tid = INT_MAX;
        char *end_ptr;
        struct str_node *pos;
+       struct strlist_config slist_config = { .dont_dupstr = true, };
        struct strlist *slist;
 
        /* perf-stat expects threads to be generated even if tid not given */
        if (!tid_str)
                return thread_map__new_dummy();
 
-       slist = strlist__new(false, tid_str);
+       slist = strlist__new(tid_str, &slist_config);
        if (!slist)
                return NULL;
 
index c307dd4382863dd7f68314885115138f489c385b..cab8cc24831bf479d3776f818c07996dad7ac57c 100644 (file)
@@ -46,6 +46,7 @@ struct perf_tool {
                        lost_samples,
                        aux,
                        itrace_start,
+                       context_switch,
                        throttle,
                        unthrottle;
        event_attr_op   attr;
index eb72716017ac265bf1572c912769bad3bf21c643..22245986e59e45632d4998285dbc3b6557ed370d 100644 (file)
@@ -341,20 +341,14 @@ out:
 
 static int record_proc_kallsyms(void)
 {
-       unsigned int size;
-       const char *path = "/proc/kallsyms";
-       struct stat st;
-       int ret, err = 0;
-
-       ret = stat(path, &st);
-       if (ret < 0) {
-               /* not found */
-               size = 0;
-               if (write(output_fd, &size, 4) != 4)
-                       err = -EIO;
-               return err;
-       }
-       return record_file(path, 4);
+       unsigned long long size = 0;
+       /*
+        * Just to keep older perf.data file parsers happy, record a zero
+        * sized kallsyms file, i.e. do the same thing that was done when
+        * /proc/kallsyms (or something specified via --kallsyms, in a
+        * different path) couldn't be read.
+        */
+       return write(output_fd, &size, 4) != 4 ? -EIO : 0;
 }
 
 static int record_ftrace_printk(void)
index d4957418657ec3ab88cce7f77b70095ff21d62c8..8ff7d620d9427490f529cf7ec63471ae1bcb3b17 100644 (file)
@@ -135,36 +135,6 @@ void event_format__print(struct event_format *event,
        return event_format__fprintf(event, cpu, data, size, stdout);
 }
 
-void parse_proc_kallsyms(struct pevent *pevent,
-                        char *file, unsigned int size __maybe_unused)
-{
-       unsigned long long addr;
-       char *func;
-       char *line;
-       char *next = NULL;
-       char *addr_str;
-       char *mod;
-       char *fmt = NULL;
-
-       line = strtok_r(file, "\n", &next);
-       while (line) {
-               mod = NULL;
-               addr_str = strtok_r(line, " ", &fmt);
-               addr = strtoull(addr_str, NULL, 16);
-               /* skip character */
-               strtok_r(NULL, " ", &fmt);
-               func = strtok_r(NULL, "\t", &fmt);
-               mod = strtok_r(NULL, "]", &fmt);
-               /* truncate the extra '[' */
-               if (mod)
-                       mod = mod + 1;
-
-               pevent_register_function(pevent, func, addr, mod);
-
-               line = strtok_r(NULL, "\n", &next);
-       }
-}
-
 void parse_ftrace_printk(struct pevent *pevent,
                         char *file, unsigned int size __maybe_unused)
 {
index 54d9e9b548a881fa7f8d068f46b232af35b7ffe2..b67a0ccf5ab94991653f6666dd4766864d282b3b 100644 (file)
@@ -162,25 +162,23 @@ out:
 static int read_proc_kallsyms(struct pevent *pevent)
 {
        unsigned int size;
-       char *buf;
 
        size = read4(pevent);
        if (!size)
                return 0;
-
-       buf = malloc(size + 1);
-       if (buf == NULL)
-               return -1;
-
-       if (do_read(buf, size) < 0) {
-               free(buf);
-               return -1;
-       }
-       buf[size] = '\0';
-
-       parse_proc_kallsyms(pevent, buf, size);
-
-       free(buf);
+       /*
+        * Just skip it, now that we configure libtraceevent to use the
+        * tools/perf/ symbol resolver.
+        *
+        * We need to skip it so that we can continue parsing old perf.data
+        * files, that contains this /proc/kallsyms payload.
+        *
+        * Newer perf.data files will have just the 4-bytes zeros "kallsyms
+        * payload", so that older tools can continue reading it and interpret
+        * it as "no kallsyms payload is present".
+        */
+       lseek(input_fd, size, SEEK_CUR);
+       trace_data_size += size;
        return 0;
 }
 
index 6322d37164c553de017b5241a8df3243136f1ae2..667bd109d16fcae8d2d7d00d3a769cd442d32b47 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <traceevent/event-parse.h>
 #include "trace-event.h"
+#include "machine.h"
 #include "util.h"
 
 /*
@@ -19,6 +20,7 @@
  * there.
  */
 static struct trace_event tevent;
+static bool tevent_initialized;
 
 int trace_event__init(struct trace_event *t)
 {
@@ -32,6 +34,32 @@ int trace_event__init(struct trace_event *t)
        return pevent ? 0 : -1;
 }
 
+static int trace_event__init2(void)
+{
+       int be = traceevent_host_bigendian();
+       struct pevent *pevent;
+
+       if (trace_event__init(&tevent))
+               return -1;
+
+       pevent = tevent.pevent;
+       pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT);
+       pevent_set_file_bigendian(pevent, be);
+       pevent_set_host_bigendian(pevent, be);
+       tevent_initialized = true;
+       return 0;
+}
+
+int trace_event__register_resolver(struct machine *machine)
+{
+       if (!tevent_initialized && trace_event__init2())
+               return -1;
+
+       return pevent_set_function_resolver(tevent.pevent,
+                                           machine__resolve_kernel_addr,
+                                           machine);
+}
+
 void trace_event__cleanup(struct trace_event *t)
 {
        traceevent_unload_plugins(t->plugin_list, t->pevent);
@@ -62,21 +90,8 @@ tp_format(const char *sys, const char *name)
 struct event_format*
 trace_event__tp_format(const char *sys, const char *name)
 {
-       static bool initialized;
-
-       if (!initialized) {
-               int be = traceevent_host_bigendian();
-               struct pevent *pevent;
-
-               if (trace_event__init(&tevent))
-                       return NULL;
-
-               pevent = tevent.pevent;
-               pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT);
-               pevent_set_file_bigendian(pevent, be);
-               pevent_set_host_bigendian(pevent, be);
-               initialized = true;
-       }
+       if (!tevent_initialized && trace_event__init2())
+               return NULL;
 
        return tp_format(sys, name);
 }
index d5168f0be4ec5b3284a825f620886689391e0f66..568128c3284a38039460e46afb905722d8f10d0b 100644 (file)
@@ -18,6 +18,7 @@ struct trace_event {
 
 int trace_event__init(struct trace_event *t);
 void trace_event__cleanup(struct trace_event *t);
+int trace_event__register_resolver(struct machine *machine);
 struct event_format*
 trace_event__tp_format(const char *sys, const char *name);
 
index 8bce58b47a826918db8f395e3e6c2711e9bbba8e..20d625a4cacf665f186d0cd44428cb3c593d62a3 100644 (file)
@@ -339,4 +339,16 @@ int gzip_decompress_to_file(const char *input, int output_fd);
 int lzma_decompress_to_file(const char *input, int output_fd);
 #endif
 
+char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints);
+
+static inline char *asprintf_expr_in_ints(const char *var, size_t nints, int *ints)
+{
+       return asprintf_expr_inout_ints(var, true, nints, ints);
+}
+
+static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int *ints)
+{
+       return asprintf_expr_inout_ints(var, false, nints, ints);
+}
+
 #endif /* GIT_COMPAT_UTIL_H */