perf tools: Add AUX area tracing Snapshot Mode
authorAdrian Hunter <adrian.hunter@intel.com>
Thu, 30 Apr 2015 14:37:31 +0000 (17:37 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 5 May 2015 21:13:00 +0000 (18:13 -0300)
Add support for making snapshots of AUX area tracing data.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1430404667-10593-9-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/perf.h
tools/perf/util/auxtrace.c
tools/perf/util/auxtrace.h

index 5042093d5213d4443f812bc4c27c5724e1a7188a..aa79fb8a16d403ad7b0d2968fe2649c04ef44f6c 100644 (file)
@@ -55,6 +55,7 @@ struct record_opts {
        bool         sample_intr_regs;
        bool         running_time;
        bool         full_auxtrace;
+       bool         auxtrace_snapshot_mode;
        unsigned int freq;
        unsigned int mmap_pages;
        unsigned int auxtrace_mmap_pages;
@@ -62,6 +63,8 @@ struct record_opts {
        u64          branch_stack;
        u64          default_interval;
        u64          user_interval;
+       size_t       auxtrace_snapshot_size;
+       const char   *auxtrace_snapshot_opts;
        bool         sample_transaction;
        unsigned     initial_delay;
        bool         use_clockid;
index 129371048fc1c8aaed6b9726a10e35f17f3447ad..df66966cfde7ab1e78f9f5da6610ad8d707dea9c 100644 (file)
@@ -504,6 +504,29 @@ void auxtrace_record__free(struct auxtrace_record *itr)
                itr->free(itr);
 }
 
+int auxtrace_record__snapshot_start(struct auxtrace_record *itr)
+{
+       if (itr && itr->snapshot_start)
+               return itr->snapshot_start(itr);
+       return 0;
+}
+
+int auxtrace_record__snapshot_finish(struct auxtrace_record *itr)
+{
+       if (itr && itr->snapshot_finish)
+               return itr->snapshot_finish(itr);
+       return 0;
+}
+
+int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
+                                  struct auxtrace_mmap *mm,
+                                  unsigned char *data, u64 *head, u64 *old)
+{
+       if (itr && itr->find_snapshot)
+               return itr->find_snapshot(itr, idx, mm, data, head, old);
+       return 0;
+}
+
 int auxtrace_record__options(struct auxtrace_record *itr,
                             struct perf_evlist *evlist,
                             struct record_opts *opts)
@@ -520,6 +543,19 @@ u64 auxtrace_record__reference(struct auxtrace_record *itr)
        return 0;
 }
 
+int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
+                                   struct record_opts *opts, const char *str)
+{
+       if (!str)
+               return 0;
+
+       if (itr)
+               return itr->parse_snapshot_options(itr, opts, str);
+
+       pr_err("No AUX area tracing to snapshot\n");
+       return -EINVAL;
+}
+
 struct auxtrace_record *__weak
 auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err)
 {
@@ -1077,16 +1113,26 @@ int perf_event__process_auxtrace_error(struct perf_tool *tool __maybe_unused,
        return 0;
 }
 
-int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
-                       struct perf_tool *tool, process_auxtrace_t fn)
+static int __auxtrace_mmap__read(struct auxtrace_mmap *mm,
+                                struct auxtrace_record *itr,
+                                struct perf_tool *tool, process_auxtrace_t fn,
+                                bool snapshot, size_t snapshot_size)
 {
-       u64 head = auxtrace_mmap__read_head(mm);
-       u64 old = mm->prev, offset, ref;
+       u64 head, old = mm->prev, offset, ref;
        unsigned char *data = mm->base;
        size_t size, head_off, old_off, len1, len2, padding;
        union perf_event ev;
        void *data1, *data2;
 
+       if (snapshot) {
+               head = auxtrace_mmap__read_snapshot_head(mm);
+               if (auxtrace_record__find_snapshot(itr, mm->idx, mm, data,
+                                                  &head, &old))
+                       return -1;
+       } else {
+               head = auxtrace_mmap__read_head(mm);
+       }
+
        if (old == head)
                return 0;
 
@@ -1106,6 +1152,9 @@ int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
        else
                size = mm->len - (old_off - head_off);
 
+       if (snapshot && size > snapshot_size)
+               size = snapshot_size;
+
        ref = auxtrace_record__reference(itr);
 
        if (head > old || size <= head || mm->mask) {
@@ -1153,18 +1202,34 @@ int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
 
        mm->prev = head;
 
-       auxtrace_mmap__write_tail(mm, head);
-       if (itr->read_finish) {
-               int err;
+       if (!snapshot) {
+               auxtrace_mmap__write_tail(mm, head);
+               if (itr->read_finish) {
+                       int err;
 
-               err = itr->read_finish(itr, mm->idx);
-               if (err < 0)
-                       return err;
+                       err = itr->read_finish(itr, mm->idx);
+                       if (err < 0)
+                               return err;
+               }
        }
 
        return 1;
 }
 
+int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
+                       struct perf_tool *tool, process_auxtrace_t fn)
+{
+       return __auxtrace_mmap__read(mm, itr, tool, fn, false, 0);
+}
+
+int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm,
+                                struct auxtrace_record *itr,
+                                struct perf_tool *tool, process_auxtrace_t fn,
+                                size_t snapshot_size)
+{
+       return __auxtrace_mmap__read(mm, itr, tool, fn, true, snapshot_size);
+}
+
 /**
  * struct auxtrace_cache - hash table to implement a cache
  * @hashtable: the hashtable
index 8c6cbb123fe579334be690aae601318d548a25d9..c2c677e6273336f8cf3c98e18c87a63bc79b6b51 100644 (file)
@@ -276,6 +276,10 @@ struct auxtrace_mmap_params {
  * @info_priv_size: return the size of the private data in auxtrace_info_event
  * @info_fill: fill-in the private data in auxtrace_info_event
  * @free: free this auxtrace record structure
+ * @snapshot_start: starting a snapshot
+ * @snapshot_finish: finishing a snapshot
+ * @find_snapshot: find data to snapshot within auxtrace mmap
+ * @parse_snapshot_options: parse snapshot options
  * @reference: provide a 64-bit reference number for auxtrace_event
  * @read_finish: called after reading from an auxtrace mmap
  */
@@ -289,12 +293,36 @@ struct auxtrace_record {
                         struct auxtrace_info_event *auxtrace_info,
                         size_t priv_size);
        void (*free)(struct auxtrace_record *itr);
+       int (*snapshot_start)(struct auxtrace_record *itr);
+       int (*snapshot_finish)(struct auxtrace_record *itr);
+       int (*find_snapshot)(struct auxtrace_record *itr, int idx,
+                            struct auxtrace_mmap *mm, unsigned char *data,
+                            u64 *head, u64 *old);
+       int (*parse_snapshot_options)(struct auxtrace_record *itr,
+                                     struct record_opts *opts,
+                                     const char *str);
        u64 (*reference)(struct auxtrace_record *itr);
        int (*read_finish)(struct auxtrace_record *itr, int idx);
 };
 
 #ifdef HAVE_AUXTRACE_SUPPORT
 
+/*
+ * In snapshot mode the mmapped page is read-only which makes using
+ * __sync_val_compare_and_swap() problematic.  However, snapshot mode expects
+ * the buffer is not updated while the snapshot is made (e.g. Intel PT disables
+ * the event) so there is not a race anyway.
+ */
+static inline u64 auxtrace_mmap__read_snapshot_head(struct auxtrace_mmap *mm)
+{
+       struct perf_event_mmap_page *pc = mm->userpg;
+       u64 head = ACCESS_ONCE(pc->aux_head);
+
+       /* Ensure all reads are done after we read the head */
+       rmb();
+       return head;
+}
+
 static inline u64 auxtrace_mmap__read_head(struct auxtrace_mmap *mm)
 {
        struct perf_event_mmap_page *pc = mm->userpg;
@@ -346,6 +374,11 @@ typedef int (*process_auxtrace_t)(struct perf_tool *tool,
 int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
                        struct perf_tool *tool, process_auxtrace_t fn);
 
+int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm,
+                                struct auxtrace_record *itr,
+                                struct perf_tool *tool, process_auxtrace_t fn,
+                                size_t snapshot_size);
+
 int auxtrace_queues__init(struct auxtrace_queues *queues);
 int auxtrace_queues__add_event(struct auxtrace_queues *queues,
                               struct perf_session *session,
@@ -383,6 +416,9 @@ void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key);
 struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
                                              int *err);
 
+int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
+                                   struct record_opts *opts,
+                                   const char *str);
 int auxtrace_record__options(struct auxtrace_record *itr,
                             struct perf_evlist *evlist,
                             struct record_opts *opts);
@@ -392,6 +428,11 @@ int auxtrace_record__info_fill(struct auxtrace_record *itr,
                               struct auxtrace_info_event *auxtrace_info,
                               size_t priv_size);
 void auxtrace_record__free(struct auxtrace_record *itr);
+int auxtrace_record__snapshot_start(struct auxtrace_record *itr);
+int auxtrace_record__snapshot_finish(struct auxtrace_record *itr);
+int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
+                                  struct auxtrace_mmap *mm,
+                                  unsigned char *data, u64 *head, u64 *old);
 u64 auxtrace_record__reference(struct auxtrace_record *itr);
 
 int auxtrace_index__auxtrace_event(struct list_head *head, union perf_event *event,