diff options
Diffstat (limited to 'tools/perf/builtin-test.c')
-rw-r--r-- | tools/perf/builtin-test.c | 418 |
1 files changed, 396 insertions, 22 deletions
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 831d1baeac3..6173f780dce 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #include "util/cache.h" | 8 | #include "util/cache.h" |
9 | #include "util/debug.h" | 9 | #include "util/debug.h" |
10 | #include "util/debugfs.h" | ||
10 | #include "util/evlist.h" | 11 | #include "util/evlist.h" |
11 | #include "util/parse-options.h" | 12 | #include "util/parse-options.h" |
12 | #include "util/parse-events.h" | 13 | #include "util/parse-events.h" |
@@ -14,8 +15,6 @@ | |||
14 | #include "util/thread_map.h" | 15 | #include "util/thread_map.h" |
15 | #include "../../include/linux/hw_breakpoint.h" | 16 | #include "../../include/linux/hw_breakpoint.h" |
16 | 17 | ||
17 | static long page_size; | ||
18 | |||
19 | static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) | 18 | static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) |
20 | { | 19 | { |
21 | bool *visited = symbol__priv(sym); | 20 | bool *visited = symbol__priv(sym); |
@@ -31,6 +30,7 @@ static int test__vmlinux_matches_kallsyms(void) | |||
31 | struct map *kallsyms_map, *vmlinux_map; | 30 | struct map *kallsyms_map, *vmlinux_map; |
32 | struct machine kallsyms, vmlinux; | 31 | struct machine kallsyms, vmlinux; |
33 | enum map_type type = MAP__FUNCTION; | 32 | enum map_type type = MAP__FUNCTION; |
33 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
34 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | 34 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; |
35 | 35 | ||
36 | /* | 36 | /* |
@@ -247,7 +247,7 @@ static int trace_event__id(const char *evname) | |||
247 | 247 | ||
248 | if (asprintf(&filename, | 248 | if (asprintf(&filename, |
249 | "%s/syscalls/%s/id", | 249 | "%s/syscalls/%s/id", |
250 | debugfs_path, evname) < 0) | 250 | tracing_events_path, evname) < 0) |
251 | return -1; | 251 | return -1; |
252 | 252 | ||
253 | fd = open(filename, O_RDONLY); | 253 | fd = open(filename, O_RDONLY); |
@@ -841,6 +841,336 @@ 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, errs = 0, 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 | while (1) { | ||
1016 | int before = total_events; | ||
1017 | |||
1018 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
1019 | union perf_event *event; | ||
1020 | |||
1021 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
1022 | const u32 type = event->header.type; | ||
1023 | const char *name = perf_event__name(type); | ||
1024 | |||
1025 | ++total_events; | ||
1026 | if (type < PERF_RECORD_MAX) | ||
1027 | nr_events[type]++; | ||
1028 | |||
1029 | err = perf_event__parse_sample(event, sample_type, | ||
1030 | sample_size, true, | ||
1031 | &sample, false); | ||
1032 | if (err < 0) { | ||
1033 | if (verbose) | ||
1034 | perf_event__fprintf(event, stderr); | ||
1035 | pr_debug("Couldn't parse sample\n"); | ||
1036 | goto out_err; | ||
1037 | } | ||
1038 | |||
1039 | if (verbose) { | ||
1040 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
1041 | perf_event__fprintf(event, stderr); | ||
1042 | } | ||
1043 | |||
1044 | if (prev_time > sample.time) { | ||
1045 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
1046 | name, prev_time, sample.time); | ||
1047 | ++errs; | ||
1048 | } | ||
1049 | |||
1050 | prev_time = sample.time; | ||
1051 | |||
1052 | if (sample.cpu != cpu) { | ||
1053 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
1054 | name, cpu, sample.cpu); | ||
1055 | ++errs; | ||
1056 | } | ||
1057 | |||
1058 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
1059 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
1060 | name, evlist->workload.pid, sample.pid); | ||
1061 | ++errs; | ||
1062 | } | ||
1063 | |||
1064 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
1065 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
1066 | name, evlist->workload.pid, sample.tid); | ||
1067 | ++errs; | ||
1068 | } | ||
1069 | |||
1070 | if ((type == PERF_RECORD_COMM || | ||
1071 | type == PERF_RECORD_MMAP || | ||
1072 | type == PERF_RECORD_FORK || | ||
1073 | type == PERF_RECORD_EXIT) && | ||
1074 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
1075 | pr_debug("%s with unexpected pid/tid\n", name); | ||
1076 | ++errs; | ||
1077 | } | ||
1078 | |||
1079 | if ((type == PERF_RECORD_COMM || | ||
1080 | type == PERF_RECORD_MMAP) && | ||
1081 | event->comm.pid != event->comm.tid) { | ||
1082 | pr_debug("%s with different pid/tid!\n", name); | ||
1083 | ++errs; | ||
1084 | } | ||
1085 | |||
1086 | switch (type) { | ||
1087 | case PERF_RECORD_COMM: | ||
1088 | if (strcmp(event->comm.comm, cmd)) { | ||
1089 | pr_debug("%s with unexpected comm!\n", name); | ||
1090 | ++errs; | ||
1091 | } | ||
1092 | break; | ||
1093 | case PERF_RECORD_EXIT: | ||
1094 | goto found_exit; | ||
1095 | case PERF_RECORD_MMAP: | ||
1096 | bname = strrchr(event->mmap.filename, '/'); | ||
1097 | if (bname != NULL) { | ||
1098 | if (!found_cmd_mmap) | ||
1099 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
1100 | if (!found_libc_mmap) | ||
1101 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
1102 | if (!found_ld_mmap) | ||
1103 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
1104 | } else if (!found_vdso_mmap) | ||
1105 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
1106 | break; | ||
1107 | |||
1108 | case PERF_RECORD_SAMPLE: | ||
1109 | /* Just ignore samples for now */ | ||
1110 | break; | ||
1111 | default: | ||
1112 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
1113 | type); | ||
1114 | ++errs; | ||
1115 | } | ||
1116 | } | ||
1117 | } | ||
1118 | |||
1119 | /* | ||
1120 | * We don't use poll here because at least at 3.1 times the | ||
1121 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
1122 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
1123 | */ | ||
1124 | if (total_events == before && false) | ||
1125 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
1126 | |||
1127 | sleep(1); | ||
1128 | if (++wakeups > 5) { | ||
1129 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
1130 | break; | ||
1131 | } | ||
1132 | } | ||
1133 | |||
1134 | found_exit: | ||
1135 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
1136 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
1137 | ++errs; | ||
1138 | } | ||
1139 | |||
1140 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
1141 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
1142 | ++errs; | ||
1143 | } | ||
1144 | |||
1145 | if (!found_cmd_mmap) { | ||
1146 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
1147 | ++errs; | ||
1148 | } | ||
1149 | |||
1150 | if (!found_libc_mmap) { | ||
1151 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
1152 | ++errs; | ||
1153 | } | ||
1154 | |||
1155 | if (!found_ld_mmap) { | ||
1156 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
1157 | ++errs; | ||
1158 | } | ||
1159 | |||
1160 | if (!found_vdso_mmap) { | ||
1161 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
1162 | ++errs; | ||
1163 | } | ||
1164 | out_err: | ||
1165 | perf_evlist__munmap(evlist); | ||
1166 | out_free_cpu_mask: | ||
1167 | CPU_FREE(cpu_mask); | ||
1168 | out_delete_evlist: | ||
1169 | perf_evlist__delete(evlist); | ||
1170 | out: | ||
1171 | return (err < 0 || errs > 0) ? -1 : 0; | ||
1172 | } | ||
1173 | |||
844 | static struct test { | 1174 | static struct test { |
845 | const char *desc; | 1175 | const char *desc; |
846 | int (*func)(void); | 1176 | int (*func)(void); |
@@ -866,45 +1196,89 @@ static struct test { | |||
866 | .func = test__parse_events, | 1196 | .func = test__parse_events, |
867 | }, | 1197 | }, |
868 | { | 1198 | { |
1199 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
1200 | .func = test__PERF_RECORD, | ||
1201 | }, | ||
1202 | { | ||
869 | .func = NULL, | 1203 | .func = NULL, |
870 | }, | 1204 | }, |
871 | }; | 1205 | }; |
872 | 1206 | ||
873 | static int __cmd_test(void) | 1207 | static bool perf_test__matches(int curr, int argc, const char *argv[]) |
874 | { | 1208 | { |
875 | int i = 0; | 1209 | int i; |
1210 | |||
1211 | if (argc == 0) | ||
1212 | return true; | ||
876 | 1213 | ||
877 | page_size = sysconf(_SC_PAGE_SIZE); | 1214 | for (i = 0; i < argc; ++i) { |
1215 | char *end; | ||
1216 | long nr = strtoul(argv[i], &end, 10); | ||
1217 | |||
1218 | if (*end == '\0') { | ||
1219 | if (nr == curr + 1) | ||
1220 | return true; | ||
1221 | continue; | ||
1222 | } | ||
1223 | |||
1224 | if (strstr(tests[curr].desc, argv[i])) | ||
1225 | return true; | ||
1226 | } | ||
1227 | |||
1228 | return false; | ||
1229 | } | ||
1230 | |||
1231 | static int __cmd_test(int argc, const char *argv[]) | ||
1232 | { | ||
1233 | int i = 0; | ||
878 | 1234 | ||
879 | while (tests[i].func) { | 1235 | while (tests[i].func) { |
880 | int err; | 1236 | int curr = i++, err; |
881 | pr_info("%2d: %s:", i + 1, tests[i].desc); | 1237 | |
1238 | if (!perf_test__matches(curr, argc, argv)) | ||
1239 | continue; | ||
1240 | |||
1241 | pr_info("%2d: %s:", i, tests[curr].desc); | ||
882 | pr_debug("\n--- start ---\n"); | 1242 | pr_debug("\n--- start ---\n"); |
883 | err = tests[i].func(); | 1243 | err = tests[curr].func(); |
884 | pr_debug("---- end ----\n%s:", tests[i].desc); | 1244 | pr_debug("---- end ----\n%s:", tests[curr].desc); |
885 | pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); | 1245 | pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); |
886 | ++i; | ||
887 | } | 1246 | } |
888 | 1247 | ||
889 | return 0; | 1248 | return 0; |
890 | } | 1249 | } |
891 | 1250 | ||
892 | static const char * const test_usage[] = { | 1251 | static int perf_test__list(int argc, const char **argv) |
893 | "perf test [<options>]", | 1252 | { |
894 | NULL, | 1253 | int i = 0; |
895 | }; | ||
896 | 1254 | ||
897 | static const struct option test_options[] = { | 1255 | while (tests[i].func) { |
1256 | int curr = i++; | ||
1257 | |||
1258 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) | ||
1259 | continue; | ||
1260 | |||
1261 | pr_info("%2d: %s\n", i, tests[curr].desc); | ||
1262 | } | ||
1263 | |||
1264 | return 0; | ||
1265 | } | ||
1266 | |||
1267 | int cmd_test(int argc, const char **argv, const char *prefix __used) | ||
1268 | { | ||
1269 | const char * const test_usage[] = { | ||
1270 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | ||
1271 | NULL, | ||
1272 | }; | ||
1273 | const struct option test_options[] = { | ||
898 | OPT_INTEGER('v', "verbose", &verbose, | 1274 | OPT_INTEGER('v', "verbose", &verbose, |
899 | "be more verbose (show symbol address, etc)"), | 1275 | "be more verbose (show symbol address, etc)"), |
900 | OPT_END() | 1276 | OPT_END() |
901 | }; | 1277 | }; |
902 | 1278 | ||
903 | int cmd_test(int argc, const char **argv, const char *prefix __used) | ||
904 | { | ||
905 | argc = parse_options(argc, argv, test_options, test_usage, 0); | 1279 | argc = parse_options(argc, argv, test_options, test_usage, 0); |
906 | if (argc) | 1280 | if (argc >= 1 && !strcmp(argv[0], "list")) |
907 | usage_with_options(test_usage, test_options); | 1281 | return perf_test__list(argc, argv); |
908 | 1282 | ||
909 | symbol_conf.priv_size = sizeof(int); | 1283 | symbol_conf.priv_size = sizeof(int); |
910 | symbol_conf.sort_by_name = true; | 1284 | symbol_conf.sort_by_name = true; |
@@ -915,5 +1289,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used) | |||
915 | 1289 | ||
916 | setup_pager(); | 1290 | setup_pager(); |
917 | 1291 | ||
918 | return __cmd_test(); | 1292 | return __cmd_test(argc, argv); |
919 | } | 1293 | } |