diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-12-02 08:13:50 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-12-02 08:13:50 -0500 |
commit | 3e7c439a7ce537ed662e347b9e6414d7881fb3dc (patch) | |
tree | 2b1ccb94b15798727cd8eb0b71424b10c72f63ca /tools/perf/builtin-test.c | |
parent | 482ad89745f7121020f6cee38aa4e894a4e7d642 (diff) |
perf test: Validate PERF_RECORD_ events and perf_sample fields
This new test will validate these new routines extracted from 'perf
record':
- perf_evlist__config_attrs
- perf_evlist__prepare_workload
- perf_evlist__start_workload
In addition to several other perf_evlist methods.
It consists of starting a simple workload, setting up just one event to
monitor ("cycles") requesting that several PERF_SAMPLE_ fields be
present in all events.
It then will check that the expected PERF_RECORD_ events are produced
and will sanity check all its fields.
Some checks performed:
. PERF_SAMPLE_TIME monotonically increases.
. PERF_SAMPLE_CPU is the one requested with sched_setaffinity
. PERF_SAMPLE_TID and PERF_SAMPLE_PID matches the one we forked
in perf_evlist__prepare_workload and that is stored in
evlist->workload.pid
. For the events where these fields are also present in its
pre-sample_id_all fields (e.g. event->mmap.pid), that they are what
is expected too.
. That we get a bunch of mmaps:
PATH/libcSUFFIX
PATH/ldSUFFIX
[vdso]
PATH/sleep
Example:
[root@emilia ~]# taskset -c 3,4 perf test -v1 perf_sample
6: Validate PERF_RECORD_* events & perf_sample fields:
--- start ---
7159480799825 3 PERF_RECORD_SAMPLE
7159480805584 3 PERF_RECORD_SAMPLE
7159480807814 3 PERF_RECORD_SAMPLE
7159480810430 3 PERF_RECORD_SAMPLE
7159480861511 3 PERF_RECORD_MMAP 8086/8086: [0x7fffffffd000(0x2000) @ 0x7fffffffd000]: //anon
7159481052516 3 PERF_RECORD_COMM: sleep:8086
7159481070188 3 PERF_RECORD_MMAP 8086/8086: [0x400000(0x6000) @ 0]: /bin/sleep
7159481077104 3 PERF_RECORD_MMAP 8086/8086: [0x3d06400000(0x221000) @ 0]: /lib64/ld-2.12.so
7159481092912 3 PERF_RECORD_MMAP 8086/8086: [0x7fff1adff000(0x1000) @ 0x7fff1adff000]: [vdso]
7159481196779 3 PERF_RECORD_MMAP 8086/8086: [0x3d06800000(0x37f000) @ 0]: /lib64/libc-2.12.so
7160481558435 3 PERF_RECORD_EXIT(8086:8086):(8086:8086)
---- end ----
Validate PERF_RECORD_* events & perf_sample fields: Ok
[root@emilia ~]#
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-svag18v2z4idas0dyz3umjpq@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-test.c')
-rw-r--r-- | tools/perf/builtin-test.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 3ab27223fc67..f1e36110b101 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -841,6 +841,339 @@ static int test__parse_events(void) | |||
841 | 841 | ||
842 | return ret; | 842 | return ret; |
843 | } | 843 | } |
844 | |||
845 | static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp, | ||
846 | size_t *sizep) | ||
847 | { | ||
848 | cpu_set_t *mask; | ||
849 | size_t size; | ||
850 | int i, cpu = -1, nrcpus = 1024; | ||
851 | realloc: | ||
852 | mask = CPU_ALLOC(nrcpus); | ||
853 | size = CPU_ALLOC_SIZE(nrcpus); | ||
854 | CPU_ZERO_S(size, mask); | ||
855 | |||
856 | if (sched_getaffinity(pid, size, mask) == -1) { | ||
857 | CPU_FREE(mask); | ||
858 | if (errno == EINVAL && nrcpus < (1024 << 8)) { | ||
859 | nrcpus = nrcpus << 2; | ||
860 | goto realloc; | ||
861 | } | ||
862 | perror("sched_getaffinity"); | ||
863 | return -1; | ||
864 | } | ||
865 | |||
866 | for (i = 0; i < nrcpus; i++) { | ||
867 | if (CPU_ISSET_S(i, size, mask)) { | ||
868 | if (cpu == -1) { | ||
869 | cpu = i; | ||
870 | *maskp = mask; | ||
871 | *sizep = size; | ||
872 | } else | ||
873 | CPU_CLR_S(i, size, mask); | ||
874 | } | ||
875 | } | ||
876 | |||
877 | if (cpu == -1) | ||
878 | CPU_FREE(mask); | ||
879 | |||
880 | return cpu; | ||
881 | } | ||
882 | |||
883 | static int test__PERF_RECORD(void) | ||
884 | { | ||
885 | struct perf_record_opts opts = { | ||
886 | .target_pid = -1, | ||
887 | .target_tid = -1, | ||
888 | .no_delay = true, | ||
889 | .freq = 10, | ||
890 | .mmap_pages = 256, | ||
891 | .sample_id_all_avail = true, | ||
892 | }; | ||
893 | cpu_set_t *cpu_mask = NULL; | ||
894 | size_t cpu_mask_size = 0; | ||
895 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
896 | struct perf_evsel *evsel; | ||
897 | struct perf_sample sample; | ||
898 | const char *cmd = "sleep"; | ||
899 | const char *argv[] = { cmd, "1", NULL, }; | ||
900 | char *bname; | ||
901 | u64 sample_type, prev_time = 0; | ||
902 | bool found_cmd_mmap = false, | ||
903 | found_libc_mmap = false, | ||
904 | found_vdso_mmap = false, | ||
905 | found_ld_mmap = false; | ||
906 | int err = -1, i, wakeups = 0, sample_size; | ||
907 | u32 cpu; | ||
908 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | ||
909 | |||
910 | if (evlist == NULL || argv == NULL) { | ||
911 | pr_debug("Not enough memory to create evlist\n"); | ||
912 | goto out; | ||
913 | } | ||
914 | |||
915 | /* | ||
916 | * We need at least one evsel in the evlist, use the default | ||
917 | * one: "cycles". | ||
918 | */ | ||
919 | err = perf_evlist__add_default(evlist); | ||
920 | if (err < 0) { | ||
921 | pr_debug("Not enough memory to create evsel\n"); | ||
922 | goto out_delete_evlist; | ||
923 | } | ||
924 | |||
925 | /* | ||
926 | * Create maps of threads and cpus to monitor. In this case | ||
927 | * we start with all threads and cpus (-1, -1) but then in | ||
928 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
929 | * we're monitoring, the one forked there. | ||
930 | */ | ||
931 | err = perf_evlist__create_maps(evlist, opts.target_pid, | ||
932 | opts.target_tid, opts.cpu_list); | ||
933 | if (err < 0) { | ||
934 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
935 | goto out_delete_evlist; | ||
936 | } | ||
937 | |||
938 | /* | ||
939 | * Prepare the workload in argv[] to run, it'll fork it, and then wait | ||
940 | * for perf_evlist__start_workload() to exec it. This is done this way | ||
941 | * so that we have time to open the evlist (calling sys_perf_event_open | ||
942 | * on all the fds) and then mmap them. | ||
943 | */ | ||
944 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | ||
945 | if (err < 0) { | ||
946 | pr_debug("Couldn't run the workload!\n"); | ||
947 | goto out_delete_evlist; | ||
948 | } | ||
949 | |||
950 | /* | ||
951 | * Config the evsels, setting attr->comm on the first one, etc. | ||
952 | */ | ||
953 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
954 | evsel->attr.sample_type |= PERF_SAMPLE_CPU; | ||
955 | evsel->attr.sample_type |= PERF_SAMPLE_TID; | ||
956 | evsel->attr.sample_type |= PERF_SAMPLE_TIME; | ||
957 | perf_evlist__config_attrs(evlist, &opts); | ||
958 | |||
959 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask, | ||
960 | &cpu_mask_size); | ||
961 | if (err < 0) { | ||
962 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | ||
963 | goto out_delete_evlist; | ||
964 | } | ||
965 | |||
966 | cpu = err; | ||
967 | |||
968 | /* | ||
969 | * So that we can check perf_sample.cpu on all the samples. | ||
970 | */ | ||
971 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) { | ||
972 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | ||
973 | goto out_free_cpu_mask; | ||
974 | } | ||
975 | |||
976 | /* | ||
977 | * Call sys_perf_event_open on all the fds on all the evsels, | ||
978 | * grouping them if asked to. | ||
979 | */ | ||
980 | err = perf_evlist__open(evlist, opts.group); | ||
981 | if (err < 0) { | ||
982 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
983 | goto out_delete_evlist; | ||
984 | } | ||
985 | |||
986 | /* | ||
987 | * mmap the first fd on a given CPU and ask for events for the other | ||
988 | * fds in the same CPU to be injected in the same mmap ring buffer | ||
989 | * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). | ||
990 | */ | ||
991 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
992 | if (err < 0) { | ||
993 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
994 | goto out_delete_evlist; | ||
995 | } | ||
996 | |||
997 | /* | ||
998 | * We'll need these two to parse the PERF_SAMPLE_* fields in each | ||
999 | * event. | ||
1000 | */ | ||
1001 | sample_type = perf_evlist__sample_type(evlist); | ||
1002 | sample_size = __perf_evsel__sample_size(sample_type); | ||
1003 | |||
1004 | /* | ||
1005 | * Now that all is properly set up, enable the events, they will | ||
1006 | * count just on workload.pid, which will start... | ||
1007 | */ | ||
1008 | perf_evlist__enable(evlist); | ||
1009 | |||
1010 | /* | ||
1011 | * Now! | ||
1012 | */ | ||
1013 | perf_evlist__start_workload(evlist); | ||
1014 | |||
1015 | err = -1; | ||
1016 | |||
1017 | while (1) { | ||
1018 | int before = total_events; | ||
1019 | |||
1020 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
1021 | union perf_event *event; | ||
1022 | |||
1023 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
1024 | const u32 type = event->header.type; | ||
1025 | const char *name = perf_event__name(type); | ||
1026 | |||
1027 | ++total_events; | ||
1028 | if (type < PERF_RECORD_MAX) | ||
1029 | nr_events[type]++; | ||
1030 | |||
1031 | if (perf_event__parse_sample(event, sample_type, | ||
1032 | sample_size, true, | ||
1033 | &sample, false) < 0) { | ||
1034 | if (verbose) | ||
1035 | perf_event__fprintf(event, stderr); | ||
1036 | pr_debug("Couldn't parse sample\n"); | ||
1037 | goto out_err; | ||
1038 | } | ||
1039 | |||
1040 | if (verbose) { | ||
1041 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
1042 | perf_event__fprintf(event, stderr); | ||
1043 | } | ||
1044 | |||
1045 | if (prev_time > sample.time) { | ||
1046 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
1047 | name, prev_time, sample.time); | ||
1048 | goto out_err; | ||
1049 | } | ||
1050 | |||
1051 | prev_time = sample.time; | ||
1052 | |||
1053 | if (sample.cpu != cpu) { | ||
1054 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
1055 | name, cpu, sample.cpu); | ||
1056 | goto out_err; | ||
1057 | } | ||
1058 | |||
1059 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
1060 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
1061 | name, evlist->workload.pid, sample.pid); | ||
1062 | goto out_err; | ||
1063 | } | ||
1064 | |||
1065 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
1066 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
1067 | name, evlist->workload.pid, sample.tid); | ||
1068 | goto out_err; | ||
1069 | } | ||
1070 | |||
1071 | if ((type == PERF_RECORD_COMM || | ||
1072 | type == PERF_RECORD_MMAP || | ||
1073 | type == PERF_RECORD_FORK || | ||
1074 | type == PERF_RECORD_EXIT) && | ||
1075 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
1076 | pr_debug("%s with unexpected pid/tid\n", name); | ||
1077 | goto out_err; | ||
1078 | } | ||
1079 | |||
1080 | if ((type == PERF_RECORD_COMM || | ||
1081 | type == PERF_RECORD_MMAP) && | ||
1082 | event->comm.pid != event->comm.tid) { | ||
1083 | pr_debug("%s with different pid/tid!\n", name); | ||
1084 | goto out_err; | ||
1085 | } | ||
1086 | |||
1087 | switch (type) { | ||
1088 | case PERF_RECORD_COMM: | ||
1089 | if (strcmp(event->comm.comm, cmd)) { | ||
1090 | pr_debug("%s with unexpected comm!\n", name); | ||
1091 | goto out_err; | ||
1092 | } | ||
1093 | break; | ||
1094 | case PERF_RECORD_EXIT: | ||
1095 | goto found_exit; | ||
1096 | case PERF_RECORD_MMAP: | ||
1097 | bname = strrchr(event->mmap.filename, '/'); | ||
1098 | if (bname != NULL) { | ||
1099 | if (!found_cmd_mmap) | ||
1100 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
1101 | if (!found_libc_mmap) | ||
1102 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
1103 | if (!found_ld_mmap) | ||
1104 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
1105 | } else if (!found_vdso_mmap) | ||
1106 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
1107 | break; | ||
1108 | |||
1109 | case PERF_RECORD_SAMPLE: | ||
1110 | /* Just ignore samples for now */ | ||
1111 | break; | ||
1112 | default: | ||
1113 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
1114 | type); | ||
1115 | goto out_err; | ||
1116 | } | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | /* | ||
1121 | * We don't use poll here because at least at 3.1 times the | ||
1122 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
1123 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
1124 | */ | ||
1125 | if (total_events == before && false) | ||
1126 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
1127 | |||
1128 | sleep(1); | ||
1129 | if (++wakeups > 5) { | ||
1130 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
1131 | goto out_err; | ||
1132 | } | ||
1133 | } | ||
1134 | |||
1135 | found_exit: | ||
1136 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
1137 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
1138 | goto out_err; | ||
1139 | } | ||
1140 | |||
1141 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
1142 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
1143 | goto out_err; | ||
1144 | } | ||
1145 | |||
1146 | if (!found_cmd_mmap) { | ||
1147 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
1148 | goto out_err; | ||
1149 | } | ||
1150 | |||
1151 | if (!found_libc_mmap) { | ||
1152 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
1153 | goto out_err; | ||
1154 | } | ||
1155 | |||
1156 | if (!found_ld_mmap) { | ||
1157 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
1158 | goto out_err; | ||
1159 | } | ||
1160 | |||
1161 | if (!found_vdso_mmap) { | ||
1162 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
1163 | goto out_err; | ||
1164 | } | ||
1165 | |||
1166 | err = 0; | ||
1167 | out_err: | ||
1168 | perf_evlist__munmap(evlist); | ||
1169 | out_free_cpu_mask: | ||
1170 | CPU_FREE(cpu_mask); | ||
1171 | out_delete_evlist: | ||
1172 | perf_evlist__delete(evlist); | ||
1173 | out: | ||
1174 | return err; | ||
1175 | } | ||
1176 | |||
844 | static struct test { | 1177 | static struct test { |
845 | const char *desc; | 1178 | const char *desc; |
846 | int (*func)(void); | 1179 | int (*func)(void); |
@@ -866,6 +1199,10 @@ static struct test { | |||
866 | .func = test__parse_events, | 1199 | .func = test__parse_events, |
867 | }, | 1200 | }, |
868 | { | 1201 | { |
1202 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
1203 | .func = test__PERF_RECORD, | ||
1204 | }, | ||
1205 | { | ||
869 | .func = NULL, | 1206 | .func = NULL, |
870 | }, | 1207 | }, |
871 | }; | 1208 | }; |