diff options
Diffstat (limited to 'tools/perf/builtin-test.c')
-rw-r--r-- | tools/perf/builtin-test.c | 545 |
1 files changed, 522 insertions, 23 deletions
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 831d1baeac3..2b9a7f497a2 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); |
@@ -603,7 +603,7 @@ out_free_threads: | |||
603 | 603 | ||
604 | #define TEST_ASSERT_VAL(text, cond) \ | 604 | #define TEST_ASSERT_VAL(text, cond) \ |
605 | do { \ | 605 | do { \ |
606 | if (!cond) { \ | 606 | if (!(cond)) { \ |
607 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ | 607 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ |
608 | return -1; \ | 608 | return -1; \ |
609 | } \ | 609 | } \ |
@@ -759,6 +759,103 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) | |||
759 | return 0; | 759 | return 0; |
760 | } | 760 | } |
761 | 761 | ||
762 | static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist) | ||
763 | { | ||
764 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
765 | struct perf_evsel, node); | ||
766 | |||
767 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
768 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
769 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
770 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
771 | |||
772 | return test__checkevent_tracepoint(evlist); | ||
773 | } | ||
774 | |||
775 | static int | ||
776 | test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist) | ||
777 | { | ||
778 | struct perf_evsel *evsel; | ||
779 | |||
780 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); | ||
781 | |||
782 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
783 | TEST_ASSERT_VAL("wrong exclude_user", | ||
784 | !evsel->attr.exclude_user); | ||
785 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
786 | evsel->attr.exclude_kernel); | ||
787 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
788 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
789 | } | ||
790 | |||
791 | return test__checkevent_tracepoint_multi(evlist); | ||
792 | } | ||
793 | |||
794 | static int test__checkevent_raw_modifier(struct perf_evlist *evlist) | ||
795 | { | ||
796 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
797 | struct perf_evsel, node); | ||
798 | |||
799 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
800 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
801 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
802 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
803 | |||
804 | return test__checkevent_raw(evlist); | ||
805 | } | ||
806 | |||
807 | static int test__checkevent_numeric_modifier(struct perf_evlist *evlist) | ||
808 | { | ||
809 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
810 | struct perf_evsel, node); | ||
811 | |||
812 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
813 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
814 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
815 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
816 | |||
817 | return test__checkevent_numeric(evlist); | ||
818 | } | ||
819 | |||
820 | static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) | ||
821 | { | ||
822 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
823 | struct perf_evsel, node); | ||
824 | |||
825 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
826 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
827 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
828 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
829 | |||
830 | return test__checkevent_symbolic_name(evlist); | ||
831 | } | ||
832 | |||
833 | static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) | ||
834 | { | ||
835 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
836 | struct perf_evsel, node); | ||
837 | |||
838 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
839 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
840 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
841 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
842 | |||
843 | return test__checkevent_symbolic_alias(evlist); | ||
844 | } | ||
845 | |||
846 | static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) | ||
847 | { | ||
848 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
849 | struct perf_evsel, node); | ||
850 | |||
851 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
852 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
853 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
854 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
855 | |||
856 | return test__checkevent_genhw(evlist); | ||
857 | } | ||
858 | |||
762 | static struct test__event_st { | 859 | static struct test__event_st { |
763 | const char *name; | 860 | const char *name; |
764 | __u32 type; | 861 | __u32 type; |
@@ -808,6 +905,34 @@ static struct test__event_st { | |||
808 | .name = "mem:0:w", | 905 | .name = "mem:0:w", |
809 | .check = test__checkevent_breakpoint_w, | 906 | .check = test__checkevent_breakpoint_w, |
810 | }, | 907 | }, |
908 | { | ||
909 | .name = "syscalls:sys_enter_open:k", | ||
910 | .check = test__checkevent_tracepoint_modifier, | ||
911 | }, | ||
912 | { | ||
913 | .name = "syscalls:*:u", | ||
914 | .check = test__checkevent_tracepoint_multi_modifier, | ||
915 | }, | ||
916 | { | ||
917 | .name = "r1:kp", | ||
918 | .check = test__checkevent_raw_modifier, | ||
919 | }, | ||
920 | { | ||
921 | .name = "1:1:hp", | ||
922 | .check = test__checkevent_numeric_modifier, | ||
923 | }, | ||
924 | { | ||
925 | .name = "instructions:h", | ||
926 | .check = test__checkevent_symbolic_name_modifier, | ||
927 | }, | ||
928 | { | ||
929 | .name = "faults:u", | ||
930 | .check = test__checkevent_symbolic_alias_modifier, | ||
931 | }, | ||
932 | { | ||
933 | .name = "L1-dcache-load-miss:kp", | ||
934 | .check = test__checkevent_genhw_modifier, | ||
935 | }, | ||
811 | }; | 936 | }; |
812 | 937 | ||
813 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) | 938 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) |
@@ -841,6 +966,336 @@ static int test__parse_events(void) | |||
841 | 966 | ||
842 | return ret; | 967 | return ret; |
843 | } | 968 | } |
969 | |||
970 | static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp, | ||
971 | size_t *sizep) | ||
972 | { | ||
973 | cpu_set_t *mask; | ||
974 | size_t size; | ||
975 | int i, cpu = -1, nrcpus = 1024; | ||
976 | realloc: | ||
977 | mask = CPU_ALLOC(nrcpus); | ||
978 | size = CPU_ALLOC_SIZE(nrcpus); | ||
979 | CPU_ZERO_S(size, mask); | ||
980 | |||
981 | if (sched_getaffinity(pid, size, mask) == -1) { | ||
982 | CPU_FREE(mask); | ||
983 | if (errno == EINVAL && nrcpus < (1024 << 8)) { | ||
984 | nrcpus = nrcpus << 2; | ||
985 | goto realloc; | ||
986 | } | ||
987 | perror("sched_getaffinity"); | ||
988 | return -1; | ||
989 | } | ||
990 | |||
991 | for (i = 0; i < nrcpus; i++) { | ||
992 | if (CPU_ISSET_S(i, size, mask)) { | ||
993 | if (cpu == -1) { | ||
994 | cpu = i; | ||
995 | *maskp = mask; | ||
996 | *sizep = size; | ||
997 | } else | ||
998 | CPU_CLR_S(i, size, mask); | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | if (cpu == -1) | ||
1003 | CPU_FREE(mask); | ||
1004 | |||
1005 | return cpu; | ||
1006 | } | ||
1007 | |||
1008 | static int test__PERF_RECORD(void) | ||
1009 | { | ||
1010 | struct perf_record_opts opts = { | ||
1011 | .target_pid = -1, | ||
1012 | .target_tid = -1, | ||
1013 | .no_delay = true, | ||
1014 | .freq = 10, | ||
1015 | .mmap_pages = 256, | ||
1016 | .sample_id_all_avail = true, | ||
1017 | }; | ||
1018 | cpu_set_t *cpu_mask = NULL; | ||
1019 | size_t cpu_mask_size = 0; | ||
1020 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1021 | struct perf_evsel *evsel; | ||
1022 | struct perf_sample sample; | ||
1023 | const char *cmd = "sleep"; | ||
1024 | const char *argv[] = { cmd, "1", NULL, }; | ||
1025 | char *bname; | ||
1026 | u64 sample_type, prev_time = 0; | ||
1027 | bool found_cmd_mmap = false, | ||
1028 | found_libc_mmap = false, | ||
1029 | found_vdso_mmap = false, | ||
1030 | found_ld_mmap = false; | ||
1031 | int err = -1, errs = 0, i, wakeups = 0, sample_size; | ||
1032 | u32 cpu; | ||
1033 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | ||
1034 | |||
1035 | if (evlist == NULL || argv == NULL) { | ||
1036 | pr_debug("Not enough memory to create evlist\n"); | ||
1037 | goto out; | ||
1038 | } | ||
1039 | |||
1040 | /* | ||
1041 | * We need at least one evsel in the evlist, use the default | ||
1042 | * one: "cycles". | ||
1043 | */ | ||
1044 | err = perf_evlist__add_default(evlist); | ||
1045 | if (err < 0) { | ||
1046 | pr_debug("Not enough memory to create evsel\n"); | ||
1047 | goto out_delete_evlist; | ||
1048 | } | ||
1049 | |||
1050 | /* | ||
1051 | * Create maps of threads and cpus to monitor. In this case | ||
1052 | * we start with all threads and cpus (-1, -1) but then in | ||
1053 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
1054 | * we're monitoring, the one forked there. | ||
1055 | */ | ||
1056 | err = perf_evlist__create_maps(evlist, opts.target_pid, | ||
1057 | opts.target_tid, opts.cpu_list); | ||
1058 | if (err < 0) { | ||
1059 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
1060 | goto out_delete_evlist; | ||
1061 | } | ||
1062 | |||
1063 | /* | ||
1064 | * Prepare the workload in argv[] to run, it'll fork it, and then wait | ||
1065 | * for perf_evlist__start_workload() to exec it. This is done this way | ||
1066 | * so that we have time to open the evlist (calling sys_perf_event_open | ||
1067 | * on all the fds) and then mmap them. | ||
1068 | */ | ||
1069 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | ||
1070 | if (err < 0) { | ||
1071 | pr_debug("Couldn't run the workload!\n"); | ||
1072 | goto out_delete_evlist; | ||
1073 | } | ||
1074 | |||
1075 | /* | ||
1076 | * Config the evsels, setting attr->comm on the first one, etc. | ||
1077 | */ | ||
1078 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1079 | evsel->attr.sample_type |= PERF_SAMPLE_CPU; | ||
1080 | evsel->attr.sample_type |= PERF_SAMPLE_TID; | ||
1081 | evsel->attr.sample_type |= PERF_SAMPLE_TIME; | ||
1082 | perf_evlist__config_attrs(evlist, &opts); | ||
1083 | |||
1084 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask, | ||
1085 | &cpu_mask_size); | ||
1086 | if (err < 0) { | ||
1087 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | ||
1088 | goto out_delete_evlist; | ||
1089 | } | ||
1090 | |||
1091 | cpu = err; | ||
1092 | |||
1093 | /* | ||
1094 | * So that we can check perf_sample.cpu on all the samples. | ||
1095 | */ | ||
1096 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) { | ||
1097 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | ||
1098 | goto out_free_cpu_mask; | ||
1099 | } | ||
1100 | |||
1101 | /* | ||
1102 | * Call sys_perf_event_open on all the fds on all the evsels, | ||
1103 | * grouping them if asked to. | ||
1104 | */ | ||
1105 | err = perf_evlist__open(evlist, opts.group); | ||
1106 | if (err < 0) { | ||
1107 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
1108 | goto out_delete_evlist; | ||
1109 | } | ||
1110 | |||
1111 | /* | ||
1112 | * mmap the first fd on a given CPU and ask for events for the other | ||
1113 | * fds in the same CPU to be injected in the same mmap ring buffer | ||
1114 | * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). | ||
1115 | */ | ||
1116 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
1117 | if (err < 0) { | ||
1118 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
1119 | goto out_delete_evlist; | ||
1120 | } | ||
1121 | |||
1122 | /* | ||
1123 | * We'll need these two to parse the PERF_SAMPLE_* fields in each | ||
1124 | * event. | ||
1125 | */ | ||
1126 | sample_type = perf_evlist__sample_type(evlist); | ||
1127 | sample_size = __perf_evsel__sample_size(sample_type); | ||
1128 | |||
1129 | /* | ||
1130 | * Now that all is properly set up, enable the events, they will | ||
1131 | * count just on workload.pid, which will start... | ||
1132 | */ | ||
1133 | perf_evlist__enable(evlist); | ||
1134 | |||
1135 | /* | ||
1136 | * Now! | ||
1137 | */ | ||
1138 | perf_evlist__start_workload(evlist); | ||
1139 | |||
1140 | while (1) { | ||
1141 | int before = total_events; | ||
1142 | |||
1143 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
1144 | union perf_event *event; | ||
1145 | |||
1146 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
1147 | const u32 type = event->header.type; | ||
1148 | const char *name = perf_event__name(type); | ||
1149 | |||
1150 | ++total_events; | ||
1151 | if (type < PERF_RECORD_MAX) | ||
1152 | nr_events[type]++; | ||
1153 | |||
1154 | err = perf_event__parse_sample(event, sample_type, | ||
1155 | sample_size, true, | ||
1156 | &sample, false); | ||
1157 | if (err < 0) { | ||
1158 | if (verbose) | ||
1159 | perf_event__fprintf(event, stderr); | ||
1160 | pr_debug("Couldn't parse sample\n"); | ||
1161 | goto out_err; | ||
1162 | } | ||
1163 | |||
1164 | if (verbose) { | ||
1165 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
1166 | perf_event__fprintf(event, stderr); | ||
1167 | } | ||
1168 | |||
1169 | if (prev_time > sample.time) { | ||
1170 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
1171 | name, prev_time, sample.time); | ||
1172 | ++errs; | ||
1173 | } | ||
1174 | |||
1175 | prev_time = sample.time; | ||
1176 | |||
1177 | if (sample.cpu != cpu) { | ||
1178 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
1179 | name, cpu, sample.cpu); | ||
1180 | ++errs; | ||
1181 | } | ||
1182 | |||
1183 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
1184 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
1185 | name, evlist->workload.pid, sample.pid); | ||
1186 | ++errs; | ||
1187 | } | ||
1188 | |||
1189 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
1190 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
1191 | name, evlist->workload.pid, sample.tid); | ||
1192 | ++errs; | ||
1193 | } | ||
1194 | |||
1195 | if ((type == PERF_RECORD_COMM || | ||
1196 | type == PERF_RECORD_MMAP || | ||
1197 | type == PERF_RECORD_FORK || | ||
1198 | type == PERF_RECORD_EXIT) && | ||
1199 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
1200 | pr_debug("%s with unexpected pid/tid\n", name); | ||
1201 | ++errs; | ||
1202 | } | ||
1203 | |||
1204 | if ((type == PERF_RECORD_COMM || | ||
1205 | type == PERF_RECORD_MMAP) && | ||
1206 | event->comm.pid != event->comm.tid) { | ||
1207 | pr_debug("%s with different pid/tid!\n", name); | ||
1208 | ++errs; | ||
1209 | } | ||
1210 | |||
1211 | switch (type) { | ||
1212 | case PERF_RECORD_COMM: | ||
1213 | if (strcmp(event->comm.comm, cmd)) { | ||
1214 | pr_debug("%s with unexpected comm!\n", name); | ||
1215 | ++errs; | ||
1216 | } | ||
1217 | break; | ||
1218 | case PERF_RECORD_EXIT: | ||
1219 | goto found_exit; | ||
1220 | case PERF_RECORD_MMAP: | ||
1221 | bname = strrchr(event->mmap.filename, '/'); | ||
1222 | if (bname != NULL) { | ||
1223 | if (!found_cmd_mmap) | ||
1224 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
1225 | if (!found_libc_mmap) | ||
1226 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
1227 | if (!found_ld_mmap) | ||
1228 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
1229 | } else if (!found_vdso_mmap) | ||
1230 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
1231 | break; | ||
1232 | |||
1233 | case PERF_RECORD_SAMPLE: | ||
1234 | /* Just ignore samples for now */ | ||
1235 | break; | ||
1236 | default: | ||
1237 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
1238 | type); | ||
1239 | ++errs; | ||
1240 | } | ||
1241 | } | ||
1242 | } | ||
1243 | |||
1244 | /* | ||
1245 | * We don't use poll here because at least at 3.1 times the | ||
1246 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
1247 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
1248 | */ | ||
1249 | if (total_events == before && false) | ||
1250 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
1251 | |||
1252 | sleep(1); | ||
1253 | if (++wakeups > 5) { | ||
1254 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
1255 | break; | ||
1256 | } | ||
1257 | } | ||
1258 | |||
1259 | found_exit: | ||
1260 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
1261 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
1262 | ++errs; | ||
1263 | } | ||
1264 | |||
1265 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
1266 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
1267 | ++errs; | ||
1268 | } | ||
1269 | |||
1270 | if (!found_cmd_mmap) { | ||
1271 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
1272 | ++errs; | ||
1273 | } | ||
1274 | |||
1275 | if (!found_libc_mmap) { | ||
1276 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
1277 | ++errs; | ||
1278 | } | ||
1279 | |||
1280 | if (!found_ld_mmap) { | ||
1281 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
1282 | ++errs; | ||
1283 | } | ||
1284 | |||
1285 | if (!found_vdso_mmap) { | ||
1286 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
1287 | ++errs; | ||
1288 | } | ||
1289 | out_err: | ||
1290 | perf_evlist__munmap(evlist); | ||
1291 | out_free_cpu_mask: | ||
1292 | CPU_FREE(cpu_mask); | ||
1293 | out_delete_evlist: | ||
1294 | perf_evlist__delete(evlist); | ||
1295 | out: | ||
1296 | return (err < 0 || errs > 0) ? -1 : 0; | ||
1297 | } | ||
1298 | |||
844 | static struct test { | 1299 | static struct test { |
845 | const char *desc; | 1300 | const char *desc; |
846 | int (*func)(void); | 1301 | int (*func)(void); |
@@ -866,45 +1321,89 @@ static struct test { | |||
866 | .func = test__parse_events, | 1321 | .func = test__parse_events, |
867 | }, | 1322 | }, |
868 | { | 1323 | { |
1324 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
1325 | .func = test__PERF_RECORD, | ||
1326 | }, | ||
1327 | { | ||
869 | .func = NULL, | 1328 | .func = NULL, |
870 | }, | 1329 | }, |
871 | }; | 1330 | }; |
872 | 1331 | ||
873 | static int __cmd_test(void) | 1332 | static bool perf_test__matches(int curr, int argc, const char *argv[]) |
874 | { | 1333 | { |
875 | int i = 0; | 1334 | int i; |
1335 | |||
1336 | if (argc == 0) | ||
1337 | return true; | ||
876 | 1338 | ||
877 | page_size = sysconf(_SC_PAGE_SIZE); | 1339 | for (i = 0; i < argc; ++i) { |
1340 | char *end; | ||
1341 | long nr = strtoul(argv[i], &end, 10); | ||
1342 | |||
1343 | if (*end == '\0') { | ||
1344 | if (nr == curr + 1) | ||
1345 | return true; | ||
1346 | continue; | ||
1347 | } | ||
1348 | |||
1349 | if (strstr(tests[curr].desc, argv[i])) | ||
1350 | return true; | ||
1351 | } | ||
1352 | |||
1353 | return false; | ||
1354 | } | ||
1355 | |||
1356 | static int __cmd_test(int argc, const char *argv[]) | ||
1357 | { | ||
1358 | int i = 0; | ||
878 | 1359 | ||
879 | while (tests[i].func) { | 1360 | while (tests[i].func) { |
880 | int err; | 1361 | int curr = i++, err; |
881 | pr_info("%2d: %s:", i + 1, tests[i].desc); | 1362 | |
1363 | if (!perf_test__matches(curr, argc, argv)) | ||
1364 | continue; | ||
1365 | |||
1366 | pr_info("%2d: %s:", i, tests[curr].desc); | ||
882 | pr_debug("\n--- start ---\n"); | 1367 | pr_debug("\n--- start ---\n"); |
883 | err = tests[i].func(); | 1368 | err = tests[curr].func(); |
884 | pr_debug("---- end ----\n%s:", tests[i].desc); | 1369 | pr_debug("---- end ----\n%s:", tests[curr].desc); |
885 | pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); | 1370 | pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); |
886 | ++i; | ||
887 | } | 1371 | } |
888 | 1372 | ||
889 | return 0; | 1373 | return 0; |
890 | } | 1374 | } |
891 | 1375 | ||
892 | static const char * const test_usage[] = { | 1376 | static int perf_test__list(int argc, const char **argv) |
893 | "perf test [<options>]", | 1377 | { |
894 | NULL, | 1378 | int i = 0; |
895 | }; | 1379 | |
1380 | while (tests[i].func) { | ||
1381 | int curr = i++; | ||
896 | 1382 | ||
897 | static const struct option test_options[] = { | 1383 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) |
1384 | continue; | ||
1385 | |||
1386 | pr_info("%2d: %s\n", i, tests[curr].desc); | ||
1387 | } | ||
1388 | |||
1389 | return 0; | ||
1390 | } | ||
1391 | |||
1392 | int cmd_test(int argc, const char **argv, const char *prefix __used) | ||
1393 | { | ||
1394 | const char * const test_usage[] = { | ||
1395 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | ||
1396 | NULL, | ||
1397 | }; | ||
1398 | const struct option test_options[] = { | ||
898 | OPT_INTEGER('v', "verbose", &verbose, | 1399 | OPT_INTEGER('v', "verbose", &verbose, |
899 | "be more verbose (show symbol address, etc)"), | 1400 | "be more verbose (show symbol address, etc)"), |
900 | OPT_END() | 1401 | OPT_END() |
901 | }; | 1402 | }; |
902 | 1403 | ||
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); | 1404 | argc = parse_options(argc, argv, test_options, test_usage, 0); |
906 | if (argc) | 1405 | if (argc >= 1 && !strcmp(argv[0], "list")) |
907 | usage_with_options(test_usage, test_options); | 1406 | return perf_test__list(argc, argv); |
908 | 1407 | ||
909 | symbol_conf.priv_size = sizeof(int); | 1408 | symbol_conf.priv_size = sizeof(int); |
910 | symbol_conf.sort_by_name = true; | 1409 | symbol_conf.sort_by_name = true; |
@@ -915,5 +1414,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used) | |||
915 | 1414 | ||
916 | setup_pager(); | 1415 | setup_pager(); |
917 | 1416 | ||
918 | return __cmd_test(); | 1417 | return __cmd_test(argc, argv); |
919 | } | 1418 | } |