1 // Copyright (c) 2020 Apple Computer, Inc. All rights reserved. 2 3 #include <darwintest.h> 4 #include <errno.h> 5 #include <inttypes.h> 6 #include <stdint.h> 7 8 #include "rump.h" 9 10 #include <sys/perfmon_private.h> 11 12 #define CONFIG_PERFMON 1 13 #define CPMU_PMC_COUNT 10 14 #define HAS_UPMU 1 15 #define UPMU_PMC_COUNT 16 16 17 #include "../osfmk/kern/perfmon.h" 18 #include "../osfmk/arm64/perfmon_arm64_regs.h" 19 #include "../osfmk/machine/machine_perfmon.h" 20 #include "../osfmk/kern/kern_perfmon.c" 21 #include "../osfmk/arm64/perfmon_arm64.c" 22 23 T_GLOBAL_META(T_META_NAMESPACE("xnu.perfmon")); 24 25 struct perfmon_event test_events[2] = { 26 { 27 .pe_name = "test", 28 .pe_number = 1, 29 .pe_counter = 3, 30 }, { 31 .pe_name = "second", 32 .pe_number = 2, 33 .pe_counter = 4, 34 }, 35 }; 36 37 T_DECL(config_add_event_spec, "ensure events can be added to a configuration") 38 { 39 T_SETUPBEGIN; 40 perfmon_machine_startup(); 41 perfmon_config_t config = perfmon_config_create(&perfmon_sources[0]); 42 T_QUIET; T_ASSERT_NOTNULL(config, "created config"); 43 T_SETUPEND; 44 45 struct perfmon_event event = test_events[0]; 46 47 int error = perfmon_config_add_event(config, &event); 48 T_ASSERT_POSIX_ZERO(error, "should add event to config"); 49 const struct perfmon_spec *spec = perfmon_config_specify(config); 50 T_ASSERT_EQ(spec->ps_event_count, (unsigned short)1, 51 "one event added to config"); 52 53 error = perfmon_config_add_event(config, &event); 54 T_ASSERT_POSIX_ERROR(error, EALREADY, 55 "should not add event to already-used counter"); 56 T_QUIET; T_ASSERT_EQ(spec->ps_event_count, (unsigned short)1, 57 "still one event added to config"); 58 59 event = test_events[1]; 60 error = perfmon_config_add_event(config, &event); 61 T_ASSERT_POSIX_ZERO(error, "should add second event to config"); 62 T_ASSERT_EQ(spec->ps_event_count, (unsigned short)2, 63 "two events added to config"); 64 65 event.pe_counter = 20; 66 error = perfmon_config_add_event(config, &event); 67 T_ASSERT_POSIX_ERROR(error, ERANGE, 68 "should not add event to counter out of range"); 69 T_QUIET; T_ASSERT_EQ(spec->ps_event_count, (unsigned short)2, 70 "still two events added to config"); 71 72 event.pe_counter = 0; 73 error = perfmon_config_add_event(config, &event); 74 T_ASSERT_POSIX_ERROR(error, ENODEV, 75 "should not add event to fixed counter, error was %d", error); 76 T_QUIET; T_ASSERT_EQ(spec->ps_event_count, (unsigned short)2, 77 "still two events added to config"); 78 79 error = perfmon_configure(config); 80 T_ASSERT_POSIX_ZERO(error, "configured CPMU"); 81 82 event.pe_counter = 4; 83 error = perfmon_config_add_event(config, &event); 84 T_ASSERT_POSIX_ERROR(error, EBUSY, 85 "should not add event to already-configured config"); 86 T_QUIET; T_ASSERT_EQ(spec->ps_event_count, (unsigned short)2, 87 "still two events added to config"); 88 89 const struct perfmon_event *read_events = spec->ps_events; 90 for (unsigned short i = 0; i < spec->ps_event_count; i++) { 91 T_QUIET; 92 T_ASSERT_EQ_STR(read_events[i].pe_name, test_events[i].pe_name, 93 "event %hu name matches", i); 94 T_QUIET; 95 T_ASSERT_EQ(read_events[i].pe_number, test_events[i].pe_number, 96 "event %hu number matches", i); 97 T_QUIET; 98 T_ASSERT_EQ(read_events[i].pe_counter, test_events[i].pe_counter, 99 "event %hu counter matches", i); 100 T_PASS("event %hu in config matches what was set", i); 101 } 102 103 perfmon_config_destroy(config); 104 } 105 106 struct perfmon_attr test_attrs[2] = { 107 { 108 .pa_name = "PMCR2", 109 .pa_value = 0x123 110 }, { 111 .pa_name = "OPMAT0", 112 .pa_value = 0x123, 113 }, 114 }; 115 116 T_DECL(config_set_attr_spec, "ensure attributes can be set on a configuration") 117 { 118 T_SETUPBEGIN; 119 perfmon_machine_startup(); 120 perfmon_config_t config = perfmon_config_create(&perfmon_sources[0]); 121 T_QUIET; T_ASSERT_NOTNULL(config, "created config"); 122 T_SETUPEND; 123 124 struct perfmon_attr attr = test_attrs[0]; 125 126 int error = perfmon_config_set_attr(config, &attr); 127 T_ASSERT_POSIX_ZERO(error, "should set attr in config"); 128 struct perfmon_spec *spec = perfmon_config_specify(config); 129 T_QUIET; T_ASSERT_EQ(spec->ps_attr_count, (unsigned short)1, 130 "one attr set in config"); 131 132 error = perfmon_config_set_attr(config, &attr); 133 T_ASSERT_POSIX_ERROR(error, EALREADY, 134 "should not set same attribute to config"); 135 T_QUIET; T_ASSERT_EQ(spec->ps_attr_count, (unsigned short)1, 136 "still one attr set in config"); 137 138 attr = test_attrs[1]; 139 error = perfmon_config_set_attr(config, &attr); 140 T_ASSERT_POSIX_ZERO(error, "should set second attr in config"); 141 T_QUIET; T_ASSERT_EQ(spec->ps_attr_count, (unsigned short)2, 142 "two attrs set in config"); 143 144 strlcpy(attr.pa_name, "ENOATTR", sizeof(attr.pa_name)); 145 error = perfmon_config_set_attr(config, &attr); 146 T_ASSERT_POSIX_ERROR(error, ENOATTR, 147 "should not set non-existent attr in config"); 148 T_QUIET; T_ASSERT_EQ(spec->ps_attr_count, (unsigned short)2, 149 "still two attrs set in config"); 150 151 error = perfmon_configure(config); 152 T_ASSERT_POSIX_ZERO(error, "configured CPMU"); 153 154 strlcpy(attr.pa_name, "PMCR3", sizeof(attr.pa_name)); 155 error = perfmon_config_set_attr(config, &attr); 156 T_ASSERT_POSIX_ERROR(error, EBUSY, 157 "should not set attr on already-configured config"); 158 T_QUIET; T_ASSERT_EQ(spec->ps_attr_count, (unsigned short)2, 159 "still two attrs added to config"); 160 161 const struct perfmon_attr *read_attrs = spec->ps_attrs; 162 for (unsigned short i = 0; i < spec->ps_attr_count; i++) { 163 T_QUIET; 164 T_ASSERT_EQ_STR(read_attrs[i].pa_name, test_attrs[i].pa_name, 165 "attr %hu name matches", i); 166 T_QUIET; 167 T_ASSERT_EQ(read_attrs[i].pa_value, test_attrs[i].pa_value, 168 "attr %hu number matches", i); 169 T_PASS("attr %hu in config matches what was set", i); 170 } 171 172 perfmon_config_destroy(config); 173 } 174 175 T_DECL(config_arm64_cpmu, "ensure the ARM64 configuration is correct") 176 { 177 T_SETUPBEGIN; 178 perfmon_machine_startup(); 179 struct perfmon_source *cpmu_source = &perfmon_sources[0]; 180 perfmon_config_t config = perfmon_config_create(cpmu_source); 181 T_QUIET; T_ASSERT_NOTNULL(config, "created config"); 182 183 for (size_t i = 0; i < ARRAYLEN(test_events); i++) { 184 int error = perfmon_config_add_event(config, &test_events[i]); 185 T_QUIET; T_ASSERT_POSIX_ZERO(error, "add event %zu to config", i); 186 } 187 188 T_SETUPEND; 189 190 int error = perfmon_configure(config); 191 T_ASSERT_POSIX_ZERO(error, "configured CPMU"); 192 193 T_LOG("PMCR0 = 0x%016" PRIx64 ", PMESR0 = 0x%016" PRIx64 ", PMESR1 = 0x%016" 194 PRIx64, cpmu_reg_state.pcr_pmcr0, cpmu_reg_state.pcr_pmesr[0], 195 cpmu_reg_state.pcr_pmesr[1]); 196 unsigned short fixed_count = cpmu_source->ps_layout.pl_fixed_count; 197 for (size_t i = 0; i < ARRAYLEN(test_events); i++) { 198 T_EXPECT_BITS_SET(cpmu_reg_state.pcr_pmcr0, 199 1ULL << test_events[i].pe_counter, "PMCR0 enabled event %zu", i); 200 T_EXPECT_BITS_SET(cpmu_reg_state.pcr_pmcr0, 201 1ULL << (test_events[i].pe_counter + 12), 202 "PMCR0 enabled PMIs for event %zu", i); 203 204 uint64_t event_shift = (test_events[i].pe_counter - fixed_count) * 8; 205 T_EXPECT_EQ((cpmu_reg_state.pcr_pmesr[0] >> event_shift) & 0xff, 206 test_events[i].pe_number, "PMESR0 has event %zu set", i); 207 } 208 209 perfmon_config_destroy(config); 210 } 211 212 T_DECL(config_lock, "ensure only one config can be active at a time") 213 { 214 T_SETUPBEGIN; 215 perfmon_machine_startup(); 216 struct perfmon_source *cpmu_source = &perfmon_sources[0]; 217 perfmon_config_t config = perfmon_config_create(cpmu_source); 218 T_QUIET; T_ASSERT_NOTNULL(config, "created config"); 219 perfmon_config_t config_later = perfmon_config_create(cpmu_source); 220 T_QUIET; T_ASSERT_NOTNULL(config_later, "created later config"); 221 222 int error = perfmon_configure(config); 223 T_ASSERT_POSIX_ZERO(error, "configured CPMU"); 224 225 T_SETUPEND; 226 227 error = perfmon_configure(config_later); 228 T_ASSERT_POSIX_ERROR(error, EBUSY, 229 "later config should be unable to configure CPMU"); 230 231 perfmon_config_destroy(config); 232 perfmon_config_destroy(config_later); 233 } 234 235 T_DECL(config_release, "ensure the active config releases control") 236 { 237 T_SETUPBEGIN; 238 perfmon_machine_startup(); 239 struct perfmon_source *cpmu_source = &perfmon_sources[0]; 240 perfmon_config_t config = perfmon_config_create(cpmu_source); 241 T_QUIET; T_ASSERT_NOTNULL(config, "created config"); 242 perfmon_config_t config_later = perfmon_config_create(cpmu_source); 243 T_QUIET; T_ASSERT_NOTNULL(config_later, "created later config"); 244 245 int error = perfmon_configure(config); 246 T_ASSERT_POSIX_ZERO(error, "configured CPMU"); 247 248 perfmon_config_destroy(config); 249 T_LOG("destroyed first config"); 250 251 T_SETUPEND; 252 253 error = perfmon_configure(config_later); 254 T_ASSERT_POSIX_ZERO(error, "later config configured CPMU"); 255 256 perfmon_config_destroy(config_later); 257 } 258