xref: /xnu-12377.61.12/tests/host_statistics_rate_limiting.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
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