perf tests: Add thread maps lookup automated tests
authorJiri Olsa <jolsa@kernel.org>
Wed, 5 Mar 2014 16:20:31 +0000 (17:20 +0100)
committerJiri Olsa <jolsa@kernel.org>
Mon, 28 Apr 2014 11:42:52 +0000 (13:42 +0200)
Adding automated test for memory maps lookup within multiple machines
threads.

The test creates 4 threads and separated memory maps. It checks that we
could use thread__find_addr_map function with thread object based on TID
to find memory maps.

Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1397490723-1992-2-git-send-email-jolsa@redhat.com
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
tools/perf/Makefile.perf
tools/perf/perf.h
tools/perf/tests/builtin-test.c
tools/perf/tests/mmap-thread-lookup.c [new file with mode: 0644]
tools/perf/tests/tests.h

index 0807e4c38505916c2b1e8a6e9eec5a0012348950..16d4f4ee8a696c1155f1b8e445846939dbfe8313 100644 (file)
@@ -416,6 +416,7 @@ ifeq ($(ARCH),x86)
 LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o
 endif
 endif
+LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
 
 BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
 BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
index 5c11ecad02a9623c4324ca4aeefdb6fa1135c9f4..ebdad3376c6728933ba789d02593da5c5ed18a0a 100644 (file)
@@ -15,6 +15,9 @@
 #ifndef __NR_futex
 # define __NR_futex 240
 #endif
+#ifndef __NR_gettid
+# define __NR_gettid 224
+#endif
 #endif
 
 #if defined(__x86_64__)
@@ -29,6 +32,9 @@
 #ifndef __NR_futex
 # define __NR_futex 202
 #endif
+#ifndef __NR_gettid
+# define __NR_gettid 186
+#endif
 #endif
 
 #ifdef __powerpc__
index ceb9daebc3894d1dd1a237fe9d3ea220660ee4e6..bb6079233ef8bf1477683a99605fcf8c6e3a279c 100644 (file)
@@ -127,6 +127,10 @@ static struct test {
                .desc = "Test filtering hist entries",
                .func = test__hists_filter,
        },
+       {
+               .desc = "Test mmap thread lookup",
+               .func = test__mmap_thread_lookup,
+       },
        {
                .func = NULL,
        },
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c
new file mode 100644 (file)
index 0000000..4a456fe
--- /dev/null
@@ -0,0 +1,233 @@
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "debug.h"
+#include "tests.h"
+#include "machine.h"
+#include "thread_map.h"
+#include "symbol.h"
+#include "thread.h"
+
+#define THREADS 4
+
+static int go_away;
+
+struct thread_data {
+       pthread_t       pt;
+       pid_t           tid;
+       void            *map;
+       int             ready[2];
+};
+
+static struct thread_data threads[THREADS];
+
+static int thread_init(struct thread_data *td)
+{
+       void *map;
+
+       map = mmap(NULL, page_size,
+                  PROT_READ|PROT_WRITE|PROT_EXEC,
+                  MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+
+       if (map == MAP_FAILED) {
+               perror("mmap failed");
+               return -1;
+       }
+
+       td->map = map;
+       td->tid = syscall(SYS_gettid);
+
+       pr_debug("tid = %d, map = %p\n", td->tid, map);
+       return 0;
+}
+
+static void *thread_fn(void *arg)
+{
+       struct thread_data *td = arg;
+       ssize_t ret;
+       int go;
+
+       if (thread_init(td))
+               return NULL;
+
+       /* Signal thread_create thread is initialized. */
+       ret = write(td->ready[1], &go, sizeof(int));
+       if (ret != sizeof(int)) {
+               pr_err("failed to notify\n");
+               return NULL;
+       }
+
+       while (!go_away) {
+               /* Waiting for main thread to kill us. */
+               usleep(100);
+       }
+
+       munmap(td->map, page_size);
+       return NULL;
+}
+
+static int thread_create(int i)
+{
+       struct thread_data *td = &threads[i];
+       int err, go;
+
+       if (pipe(td->ready))
+               return -1;
+
+       err = pthread_create(&td->pt, NULL, thread_fn, td);
+       if (!err) {
+               /* Wait for thread initialization. */
+               ssize_t ret = read(td->ready[0], &go, sizeof(int));
+               err = ret != sizeof(int);
+       }
+
+       close(td->ready[0]);
+       close(td->ready[1]);
+       return err;
+}
+
+static int threads_create(void)
+{
+       struct thread_data *td0 = &threads[0];
+       int i, err = 0;
+
+       go_away = 0;
+
+       /* 0 is main thread */
+       if (thread_init(td0))
+               return -1;
+
+       for (i = 1; !err && i < THREADS; i++)
+               err = thread_create(i);
+
+       return err;
+}
+
+static int threads_destroy(void)
+{
+       struct thread_data *td0 = &threads[0];
+       int i, err = 0;
+
+       /* cleanup the main thread */
+       munmap(td0->map, page_size);
+
+       go_away = 1;
+
+       for (i = 1; !err && i < THREADS; i++)
+               err = pthread_join(threads[i].pt, NULL);
+
+       return err;
+}
+
+typedef int (*synth_cb)(struct machine *machine);
+
+static int synth_all(struct machine *machine)
+{
+       return perf_event__synthesize_threads(NULL,
+                                             perf_event__process,
+                                             machine, 0);
+}
+
+static int synth_process(struct machine *machine)
+{
+       struct thread_map *map;
+       int err;
+
+       map = thread_map__new_by_pid(getpid());
+
+       err = perf_event__synthesize_thread_map(NULL, map,
+                                               perf_event__process,
+                                               machine, 0);
+
+       thread_map__delete(map);
+       return err;
+}
+
+static int mmap_events(synth_cb synth)
+{
+       struct machines machines;
+       struct machine *machine;
+       int err, i;
+
+       /*
+        * The threads_create will not return before all threads
+        * are spawned and all created memory map.
+        *
+        * They will loop until threads_destroy is called, so we
+        * can safely run synthesizing function.
+        */
+       TEST_ASSERT_VAL("failed to create threads", !threads_create());
+
+       machines__init(&machines);
+       machine = &machines.host;
+
+       dump_trace = verbose > 1 ? 1 : 0;
+
+       err = synth(machine);
+
+       dump_trace = 0;
+
+       TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy());
+       TEST_ASSERT_VAL("failed to synthesize maps", !err);
+
+       /*
+        * All data is synthesized, try to find map for each
+        * thread object.
+        */
+       for (i = 0; i < THREADS; i++) {
+               struct thread_data *td = &threads[i];
+               struct addr_location al;
+               struct thread *thread;
+
+               thread = machine__findnew_thread(machine, getpid(), td->tid);
+
+               pr_debug("looking for map %p\n", td->map);
+
+               thread__find_addr_map(thread, machine,
+                                     PERF_RECORD_MISC_USER, MAP__FUNCTION,
+                                     (unsigned long) (td->map + 1), &al);
+
+               if (!al.map) {
+                       pr_debug("failed, couldn't find map\n");
+                       err = -1;
+                       break;
+               }
+
+               pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start);
+       }
+
+       machine__delete_threads(machine);
+       machines__exit(&machines);
+       return err;
+}
+
+/*
+ * This test creates 'THREADS' number of threads (including
+ * main thread) and each thread creates memory map.
+ *
+ * When threads are created, we synthesize them with both
+ * (separate tests):
+ *   perf_event__synthesize_thread_map (process based)
+ *   perf_event__synthesize_threads    (global)
+ *
+ * We test we can find all memory maps via:
+ *   thread__find_addr_map
+ *
+ * by using all thread objects.
+ */
+int test__mmap_thread_lookup(void)
+{
+       /* perf_event__synthesize_threads synthesize */
+       TEST_ASSERT_VAL("failed with sythesizing all",
+                       !mmap_events(synth_all));
+
+       /* perf_event__synthesize_thread_map synthesize */
+       TEST_ASSERT_VAL("failed with sythesizing process",
+                       !mmap_events(synth_process));
+
+       return 0;
+}
index fe39163e9ea7d7b3e3b8c2c756449f5b72f62bf8..82e8061df46e73004e17f7f72232a2dcb9bfded3 100644 (file)
@@ -42,6 +42,7 @@ int test__keep_tracking(void);
 int test__parse_no_sample_id_all(void);
 int test__dwarf_unwind(void);
 int test__hists_filter(void);
+int test__mmap_thread_lookup(void);
 
 #if defined(__x86_64__) || defined(__i386__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT