diff options
Diffstat (limited to 'tools/perf')
| -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 3ab27223fc6..f1e36110b10 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 | }; |
