1 #include <unistd.h>
2 #include <stdint.h>
3 #include <sys/time.h>
4 #include <System/sys/codesign.h>
5 #include <mach/mach_time.h>
6 #include <mach/mach.h>
7 #include <darwintest.h>
8 #include <stdlib.h>
9 #include <sys/sysctl.h>
10 #include "cs_helpers.h"
11
12 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
13
14 #if !defined(CS_OPS_CLEARPLATFORM)
15 #define CS_OPS_CLEARPLATFORM 13
16 #endif
17
18 #define WINDOW 1 /* seconds */
19 #define MAX_ATTEMP_PER_SEC 10
20 #define ITER 30
21 #define RETRY 5
22
23 struct all_host_info {
24 vm_statistics64_data_t host_vm_info64_rev0;
25 vm_statistics64_data_t host_vm_info64_rev1;
26 vm_statistics64_data_t host_vm_info64_rev2;
27 vm_extmod_statistics_data_t host_extmod_info64;
28 host_load_info_data_t host_load_info;
29 vm_statistics_data_t host_vm_info_rev0;
30 vm_statistics_data_t host_vm_info_rev1;
31 vm_statistics_data_t host_vm_info_rev2;
32 host_cpu_load_info_data_t host_cpu_load_info;
33 task_power_info_v2_data_t host_expired_task_info;
34 task_power_info_v2_data_t host_expired_task_info2;
35 };
36
37 static bool
on_rosetta(void)38 on_rosetta(void)
39 {
40 #if defined(__x86_64__)
41 int out_value = 0;
42 size_t io_size = sizeof(out_value);
43 if (sysctlbyname("sysctl.proc_translated", &out_value, &io_size, NULL, 0) == 0) {
44 assert(io_size >= sizeof(out_value));
45 return out_value;
46 }
47 return false;
48 #else /* defined(__x86_64__) */
49 return false;
50 #endif /* !defined(__x86_64__) */
51 }
52
53 static void
check_host_info(struct all_host_info * data,unsigned long iter,char lett)54 check_host_info(struct all_host_info* data, unsigned long iter, char lett)
55 {
56 char* datap;
57 unsigned long i, j;
58
59 /* check that for the shorter revisions no data is copied on the bytes of diff with the longer */
60 for (j = 0; j < iter; j++) {
61 datap = (char*) &data[j].host_vm_info64_rev0;
62 for (i = (HOST_VM_INFO64_REV0_COUNT * sizeof(int)); i < (HOST_VM_INFO64_REV1_COUNT * sizeof(int)); i++) {
63 T_QUIET; T_ASSERT_EQ(datap[i], lett, "HOST_VM_INFO64_REV0 byte %lu iter %lu", i, j);
64 }
65
66 datap = (char*) &data[j].host_vm_info_rev0;
67 for (i = (HOST_VM_INFO_REV0_COUNT * sizeof(int)); i < (HOST_VM_INFO_REV2_COUNT * sizeof(int)); i++) {
68 T_QUIET; T_ASSERT_EQ(datap[i], lett, "HOST_VM_INFO_REV0 byte %lu iter %lu", i, j);
69 }
70
71 datap = (char*) &data[j].host_vm_info_rev1;
72 for (i = (HOST_VM_INFO_REV1_COUNT * sizeof(int)); i < (HOST_VM_INFO_REV2_COUNT * sizeof(int)); i++) {
73 T_QUIET; T_ASSERT_EQ(datap[i], lett, "HOST_VM_INFO_REV1 byte %lu iter %lu", i, j);
74 }
75
76 datap = (char*) &data[j].host_expired_task_info;
77 for (i = (TASK_POWER_INFO_COUNT * sizeof(int)); i < (TASK_POWER_INFO_V2_COUNT * sizeof(int)); i++) {
78 T_QUIET; T_ASSERT_EQ(datap[i], lett, "TASK_POWER_INFO_COUNT byte %lu iter %lu", i, j);
79 }
80 }
81 T_LOG("No data overflow");
82
83 datap = (char*) data;
84
85 /* check that after MAX_ATTEMP_PER_SEC data are all the same */
86 for (i = 0; i < sizeof(struct all_host_info); i++) {
87 for (j = MAX_ATTEMP_PER_SEC - 1; j < iter - 1; j++) {
88 T_QUIET; T_ASSERT_EQ(datap[i + (j * sizeof(struct all_host_info))], datap[i + ((j + 1) * sizeof(struct all_host_info))], "all_host_info iter %lu does not match iter %lu", j, j + 1);
89 }
90 }
91
92 T_LOG("Data was cached");
93 }
94
95 static void
get_host_info(struct all_host_info * data,host_t self,int iter)96 get_host_info(struct all_host_info* data, host_t self, int iter)
97 {
98 int i;
99 unsigned int count;
100 for (i = 0; i < iter; i++) {
101 count = HOST_VM_INFO64_REV0_COUNT;
102 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics64(self, HOST_VM_INFO64, (host_info64_t)&data[i].host_vm_info64_rev0, &count), NULL);
103 T_QUIET; T_ASSERT_EQ(count, HOST_VM_INFO64_REV0_COUNT, NULL);
104
105 count = HOST_VM_INFO64_REV1_COUNT;
106 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics64(self, HOST_VM_INFO64, (host_info64_t)&data[i].host_vm_info64_rev1, &count), NULL);
107 T_QUIET; T_ASSERT_EQ(count, HOST_VM_INFO64_REV1_COUNT, NULL);
108
109 count = HOST_VM_INFO64_REV2_COUNT;
110 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics64(self, HOST_VM_INFO64, (host_info64_t)&data[i].host_vm_info64_rev2, &count), NULL);
111 T_QUIET; T_ASSERT_EQ(count, HOST_VM_INFO64_REV2_COUNT, NULL);
112
113 count = HOST_EXTMOD_INFO64_COUNT;
114 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics64(self, HOST_EXTMOD_INFO64, (host_info64_t)&data[i].host_extmod_info64, &count), NULL);
115 T_QUIET; T_ASSERT_EQ(count, HOST_EXTMOD_INFO64_COUNT, NULL);
116
117 count = HOST_LOAD_INFO_COUNT;
118 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_LOAD_INFO, (host_info_t)&data[i].host_load_info, &count), NULL);
119 T_QUIET; T_ASSERT_EQ(count, HOST_LOAD_INFO_COUNT, NULL);
120
121 count = HOST_VM_INFO_REV0_COUNT;
122 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_VM_INFO, (host_info_t)&data[i].host_vm_info_rev0, &count), NULL);
123 T_QUIET; T_ASSERT_EQ(count, HOST_VM_INFO_REV0_COUNT, NULL);
124
125 count = HOST_VM_INFO_REV1_COUNT;
126 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_VM_INFO, (host_info_t)&data[i].host_vm_info_rev1, &count), NULL);
127 T_QUIET; T_ASSERT_EQ(count, HOST_VM_INFO_REV1_COUNT, NULL);
128
129 count = HOST_VM_INFO_REV2_COUNT;
130 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_VM_INFO, (host_info_t)&data[i].host_vm_info_rev2, &count), NULL);
131 T_QUIET; T_ASSERT_EQ(count, HOST_VM_INFO_REV2_COUNT, NULL);
132
133 count = HOST_CPU_LOAD_INFO_COUNT;
134 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_CPU_LOAD_INFO, (host_info_t)&data[i].host_cpu_load_info, &count), NULL);
135 T_QUIET; T_ASSERT_EQ(count, HOST_CPU_LOAD_INFO_COUNT, NULL);
136
137 count = TASK_POWER_INFO_COUNT;
138 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_EXPIRED_TASK_INFO, (host_info_t)&data[i].host_expired_task_info, &count), NULL);
139 if (!on_rosetta()) {
140 /* rdar://61083333 */
141 T_QUIET; T_ASSERT_EQ(count, TASK_POWER_INFO_COUNT, NULL);
142 }
143
144 count = TASK_POWER_INFO_V2_COUNT;
145 T_QUIET; T_ASSERT_POSIX_ZERO(host_statistics(self, HOST_EXPIRED_TASK_INFO, (host_info_t)&data[i].host_expired_task_info2, &count), NULL);
146 if (!on_rosetta()) {
147 /* rdar://61083333 */
148 T_QUIET; T_ASSERT_EQ(count, TASK_POWER_INFO_V2_COUNT, NULL);
149 }
150 }
151 }
152
153 T_DECL(test_host_statistics, "testing rate limit for host_statistics",
154 T_META_CHECK_LEAKS(false),
155 T_META_ALL_VALID_ARCHS(true),
156 T_META_TAG_VM_NOT_PREFERRED,
157 T_META_ENABLED(false) /* rdar://134505671 */)
158 {
159 unsigned long long start, end, window;
160 int retry = 0;
161 host_t self;
162 char lett = 'a';
163 struct all_host_info* data;
164 mach_timebase_info_data_t timebaseInfo = { 0, 0 };
165
166 if (remove_platform_binary()) {
167 T_SKIP("Failed to remove platform binary");
168 }
169
170 data = malloc(ITER * sizeof(struct all_host_info));
171 T_QUIET; T_ASSERT_NE(data, NULL, "malloc");
172
173 /* check the size of the data structure against the bytes in COUNT*/
174 T_QUIET; T_ASSERT_EQ(sizeof(data[0].host_vm_info64_rev0), HOST_VM_INFO64_COUNT * sizeof(int), "HOST_VM_INFO64_COUNT");
175 T_QUIET; T_ASSERT_EQ(sizeof(data[0].host_extmod_info64), HOST_EXTMOD_INFO64_COUNT * sizeof(int), "HOST_EXTMOD_INFO64_COUNT");
176 T_QUIET; T_ASSERT_EQ(sizeof(data[0].host_load_info), HOST_LOAD_INFO_COUNT * sizeof(int), "HOST_LOAD_INFO_COUNT");
177 T_QUIET; T_ASSERT_EQ(sizeof(data[0].host_vm_info_rev0), HOST_VM_INFO_COUNT * sizeof(int), "HOST_VM_INFO_COUNT");
178 T_QUIET; T_ASSERT_EQ(sizeof(data[0].host_cpu_load_info), HOST_CPU_LOAD_INFO_COUNT * sizeof(int), "HOST_CPU_LOAD_INFO_COUNT");
179 T_QUIET; T_ASSERT_EQ(sizeof(data[0].host_expired_task_info2), TASK_POWER_INFO_V2_COUNT * sizeof(int), "TASK_POWER_INFO_V2_COUNT");
180
181 /* check that the latest revision is the COUNT */
182 T_QUIET; T_ASSERT_EQ(HOST_VM_INFO64_REV2_COUNT, HOST_VM_INFO64_COUNT, "HOST_VM_INFO64_REV2_COUNT");
183 T_QUIET; T_ASSERT_EQ(HOST_VM_INFO_REV2_COUNT, HOST_VM_INFO_COUNT, "HOST_VM_INFO_REV2_COUNT");
184
185 /* check that the previous revision are smaller than the latest */
186 T_QUIET; T_ASSERT_LE(HOST_VM_INFO64_REV0_COUNT, HOST_VM_INFO64_REV1_COUNT, "HOST_VM_INFO64_REV0");
187 T_QUIET; T_ASSERT_LE(HOST_VM_INFO64_REV1_COUNT, HOST_VM_INFO64_REV2_COUNT, "HOST_VM_INFO64_REV1");
188 T_QUIET; T_ASSERT_LE(HOST_VM_INFO_REV0_COUNT, HOST_VM_INFO_REV2_COUNT, "HOST_VM_INFO_REV0_COUNT");
189 T_QUIET; T_ASSERT_LE(HOST_VM_INFO_REV1_COUNT, HOST_VM_INFO_REV2_COUNT, "HOST_VM_INFO_REV1_COUNT");
190 T_QUIET; T_ASSERT_LE(TASK_POWER_INFO_COUNT, TASK_POWER_INFO_V2_COUNT, "TASK_POWER_INFO_COUNT");
191
192 memset(data, lett, ITER * sizeof(struct all_host_info));
193 self = mach_host_self();
194
195 T_QUIET; T_ASSERT_EQ(mach_timebase_info(&timebaseInfo), KERN_SUCCESS, NULL);
196 window = (WINDOW * NSEC_PER_SEC * timebaseInfo.denom) / timebaseInfo.numer;
197 retry = 0;
198
199 /* try to get ITER copies of host_info within window time, in such a way we should hit for sure a cached copy */
200 do {
201 start = mach_continuous_time();
202 get_host_info(data, self, ITER);
203 end = mach_continuous_time();
204 retry++;
205 } while ((end - start > window) && retry <= RETRY);
206
207 if (retry <= RETRY) {
208 check_host_info(data, ITER, lett);
209 } else {
210 T_SKIP("Failed to find window for test");
211 }
212 }
213