xref: /xnu-10002.1.13/tests/vm/diag_threshold_test.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
1 /*
2  *
3  *  DiagThresholdTest.c
4  *  DiagThresholdTest
5  *  Test suite for the memory diagnostics thresholds kernel.
6  *  Copyright (c) 2022 Apple Inc. All rights reserved.
7  *
8  */
9 
10 
11 #include "vm/diag_threshold_test.h"
12 #include <sys/kern_memorystatus.h>
13 #include <sys/types.h>
14 #include <sys/sysctl.h>
15 #include <signal.h>
16 #include <sys/resource.h>
17 #include <errno.h>
18 #define TIMEOUT_MAX_SECONDS 10
19 /**
20  * Preprocessor declarations
21  */
22 /**
23  * Global variables
24  */
25 static mach_port_t         exception_port;                                            /**   Port used to receive exception information*/
26 static semaphore_t         exception_semaphore;                                       /**   Termination semaphore when the exception is caught */
27 static exception_mask_t    mask                        =  EXC_MASK_CORPSE_NOTIFY;     /**   Kind of exception we are interested in*/
28 static char                main_thread_name[COMMAND_LINE_MAX];                        /**   Name of the main thread, for restauration */
29 static struct sigaction    original_action;                                           /**   Original signal handler for the process */
30 
31 /**
32  * Data types private
33  */
34 
35 static test_context_t *current_test_info;                                              /**   Data type used to handle working information on the current test*/
36 /**
37  * Function prototypes (private)
38  */
39 static void prepare_harness(void);                                                     /**   Preparation of the exception port and other miscs */
40 static void init_mach_exceptions_port(void);                                           /**   Creation of the exception port */
41 static void wait_for_exceptions_thread(test_context_t *tests_info);                    /**   Wait for for the startup of the exception handler thread */
42 static void *test_executor_thread(void *);                                             /**   Code of the exception handler thread */
43 static void wait_for_executor_thread(test_context_t *info);                            /**   Wait for the initialization of the worker thread */
44 static void create_exception_handler_thread(test_context_t *);                         /**   Creator of the exception handler thread */
45 static void create_executor_handler_thread(test_context_t *param);                     /**   Creation of the worker thread, the one that performs really the test code */
46 static void terminate_exception_handler_thread(test_context_t *);                      /**   Order to terminate the current exception handler thread (by sending a user posix signal)*/
47 static void terminate_executor_handler_thread(test_context_t *param);                  /**   Order to terminate the current worker thread (by sending a user posix signal)*/
48 static void set_exception_ports(test_context_t *param);                                /**   Function that sets the exception port for the current test */
49 static void save_exception_ports(test_context_t *param);                               /**   Save the current exception mach configuration to restore after the test */
50 static void restore_exception_ports(test_context_t *param);                            /**   Restauration of the exception mach configuration after each test */
51 static void wait_for_exceptions_ready(test_context_t *info);                           /**   Synch point between the worker and the test harness, to allow workers to capture exceptions */
52 static void set_sig_handler(void);                                                     /**   Initialization of the SIGUSR1 posix signal handler, used to terminate threads */
53 static void reset_sig_handler(void);                                                   /**   Reset the signal handler */
54 static void enable_coredumps(void);                                                    /**   Since debug of this app is not possible with lldb, that allows to create core files for debugging */
55 static void deinit_mach_exceptions_port(void);                                         /**   Destroys the mach port used to exceptions*/
56 static void execute_one_test(test_case_t *test);                                       /**   Perform the exeuction of a single test */
57 static void remove_harness(void);                                                      /**   Remove the test harness for this process*/
58 static uint64_t get_hw_memory_size(void);                                              /**   Get the ammount of RAM available on this device */
59 static const char *g_sysctl_memsize_name = "hw.memsize";
60 
61 void
diag_mem_threshold_set_setup(__unused test_case_t * test)62 diag_mem_threshold_set_setup(__unused test_case_t *test)
63 {
64 	T_QUIET; T_ASSERT_POSIX_ZERO( pthread_setname_np("Test controller"), "Verification thread set name");
65 	enable_coredumps();
66 
67 	set_sig_handler();
68 	prepare_harness();
69 	execute_one_test(test);
70 }
71 /**
72  * Shutdown the test environment
73  */
74 void
diag_mem_threshold_set_shutdown(void)75 diag_mem_threshold_set_shutdown(void)
76 {
77 	T_QUIET; T_ASSERT_POSIX_ZERO( pthread_getname_np(pthread_self(), main_thread_name, sizeof(main_thread_name)), "Verification thread get name");
78 	remove_harness();
79 	reset_sig_handler();
80 	deinit_mach_exceptions_port();
81 }
82 
83 /**
84  * Once the test worker is created, this function waits for a period of time (TIMEOUT_MAX_SECONDS) for an exception
85  * and returns TRUE if the exception is seen, or FALSE if not.
86  * - Parameter param: test execution information, used to mark in the test if the exception is seen.
87  * */
88 bool
diag_mem_threshold_wait_for_exception(test_context_t * param)89 diag_mem_threshold_wait_for_exception(test_context_t *param)
90 {
91 	int seconds_execution = 0;
92 	param->exception_seen = FALSE;
93 	mach_timespec_t wait_time = {.tv_sec = 1, .tv_nsec = 0};
94 	while (seconds_execution++ < TIMEOUT_MAX_SECONDS) {
95 		if (!semaphore_timedwait(exception_semaphore, wait_time)) {
96 			param->exception_seen = TRUE;
97 			diag_mem_threshold_log_test("Semaphore done, exiting\n");
98 			/* Cleanup any residual values in the semahore */
99 			wait_time.tv_sec = 0;
100 			while (!semaphore_timedwait(exception_semaphore, wait_time)) {
101 				NULL;
102 			}
103 			return TRUE;
104 		}
105 
106 		diag_mem_threshold_log_test("Waiting\n");
107 	}
108 
109 	return FALSE;
110 }
111 
112 /**
113  * Execute only one test
114  */
115 void
execute_one_test(test_case_t * test)116 execute_one_test(test_case_t *test)
117 {
118 	test_context_t info = {
119 		.test = test,
120 		.exception_semaphore = exception_semaphore,
121 		.exception_thread_start_sema = dispatch_semaphore_create(0),
122 		.executor_thread_start_sema = dispatch_semaphore_create(0),
123 		.executor_thread_end_sema = dispatch_semaphore_create(0),
124 		.executor_ready_for_exceptions = dispatch_semaphore_create(0),
125 	};
126 	uint64_t this_system_ram_size = get_hw_memory_size();
127 	current_test_info = &info;
128 	/** If this test required a certain ammount of memory in the device, and is
129 	 * not present, just pass the test.
130 	 */
131 	if (test->required_minimum_hw_ram_size != 0) {
132 		if (this_system_ram_size < test->required_minimum_hw_ram_size) {
133 			T_SKIP("This system have less memory as required to run this test (Required %llu MB found %llu MB). Skipping", test->required_minimum_hw_ram_size >> 20ULL, this_system_ram_size >> 20ULL);
134 			test->did_pass = TRUE;
135 			return;
136 		}
137 	}
138 	diag_mem_threshold_log_test("This system have %llu MB RAM\n", (this_system_ram_size >> 20ULL));
139 
140 	save_exception_ports(&info);
141 	create_exception_handler_thread(&info);
142 	create_executor_handler_thread(&info);
143 	set_exception_ports(&info);
144 
145 	wait_for_exceptions_thread(&info);
146 	wait_for_executor_thread(&info);
147 	wait_for_exceptions_ready(&info);
148 	if (test->exceptions_handled_in_test == false) {
149 		bool wait_result = diag_mem_threshold_wait_for_exception(&info);
150 
151 		/**   If the test expects to have an exception at end, and is seen.. */
152 		if ((test->result_already_present == FALSE) && (wait_result == TRUE)) {
153 			test->did_pass = TRUE;
154 		} else if (test->exception_not_expected == TRUE && wait_result == FALSE) {
155 			/**   If the test expects to NOT have an exception at end, and NOT seen.. */
156 			test->did_pass = TRUE;
157 		} else {
158 			test->did_pass = FALSE;
159 		}
160 	} else {
161 		pthread_join(info.executor_handler_thread_id, NULL);
162 	}
163 	T_EXPECT_TRUE(test->did_pass, "Test result");
164 	restore_exception_ports(&info);
165 
166 	if (test->exceptions_handled_in_test == false) {
167 		terminate_executor_handler_thread(&info);
168 	}
169 	terminate_exception_handler_thread(&info);
170 	if (info.test->did_pass) {
171 		diag_mem_threshold_log_test("Test success\n");
172 	} else {
173 		diag_mem_threshold_log_test("Test failed\n");
174 	}
175 }
176 /**
177  * Thread that really performs the test execution, sets its thread name,  signals
178  * the semaphore of executor ready and calls to the test code. Finally
179  * it marks another semaphore to signal that the test code is complete.
180  * - Parameter params: parameters of the test execution
181  */
182 static void *
test_executor_thread(void * params)183 test_executor_thread(void *params)
184 {
185 	char thread_name[MAX_MESSAGE];
186 	test_context_t info = *(test_context_t *)params;
187 	snprintf(thread_name, sizeof(thread_name), "Exec: %s", info.test->short_name);
188 	pthread_setname_np(thread_name);
189 	diag_mem_threshold_log_test("Startup of executor for test %s\n", info.test->short_name);
190 	dispatch_semaphore_signal(info.executor_thread_start_sema);
191 	info.test->test_code(info.test, &info);
192 	dispatch_semaphore_signal(info.executor_thread_end_sema);
193 	return NULL;
194 }
195 
196 /**
197  * Set the exception port for the current task, did not manage to make it work with execution thread..
198  * - Parameter param: paramers of the exeuction, not used at the moment
199  */
200 static void
set_exception_ports(__unused test_context_t * param)201 set_exception_ports(__unused test_context_t *param)
202 {
203 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, task_set_exception_ports(mach_task_self(), mask, exception_port, (exception_behavior_t) (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY), MACHINE_THREAD_STATE), "Cannot set exception port");
204 }
205 /**
206  * Allocates and sets rights for the exceptions port
207  */
208 static void
init_mach_exceptions_port(void)209 init_mach_exceptions_port(void)
210 {
211 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port), "Verification allocation port for exceptions");
212 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND), "Verification set port rights");
213 }
214 /**
215  * Destroys the used mach port for exceptions.
216  */
217 static void
deinit_mach_exceptions_port(void)218 deinit_mach_exceptions_port(void)
219 {
220 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, mach_port_deallocate(mach_task_self(), exception_port), "Verification exceptions port deallocation");
221 }
222 /**
223  * Saves the current task exception port on the current test context.
224  * - Parameter info: test context.
225  */
226 static void
save_exception_ports(test_context_t * info)227 save_exception_ports(test_context_t *info)
228 {
229 	info->size_mask = NUM_MASKS;
230 	kern_return_t ret;
231 	ret  = task_get_exception_ports(
232 		mach_task_self(),
233 		mask,
234 		info->old_mask,
235 		&info->size_mask,
236 		info->old_handlers,
237 		info->old_behaviors,
238 		info->old_flavors);
239 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, ret, "Verification get old exception ports");
240 }
241 /**
242  * Once the test is completed, this function restores all the mach task exception configuration to its original state
243  * - Parameter info: test context.
244  */
245 static void
restore_exception_ports(test_context_t * info)246 restore_exception_ports(test_context_t *info)
247 {
248 	kern_return_t ret;
249 	ret  = task_swap_exception_ports(
250 		mach_task_self(),
251 		mask,
252 		MACH_PORT_NULL,
253 		EXCEPTION_DEFAULT,
254 		THREAD_STATE_NONE,
255 		(exception_mask_array_t) &info->old_mask,
256 		&info->size_mask,
257 		info->old_handlers,
258 		info->old_behaviors,
259 		info->old_flavors);
260 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, ret, "Verification restore exception ports");
261 }
262 /**
263  * Create the pthread used to handle exceptions from this test.
264  * - Parameter param: Test context.
265  */
266 static void
create_exception_handler_thread(test_context_t * param)267 create_exception_handler_thread(test_context_t *param)
268 {
269 	T_QUIET; T_ASSERT_POSIX_ZERO( pthread_create(&param->exception_handler_thread_id, NULL, exception_thread, (void *)param), "Creation of exception handler thread");
270 }
271 /**
272  * Create the pthread used to really run the test code
273  * - Parameter param: Test context.
274  */
275 static void
create_executor_handler_thread(test_context_t * param)276 create_executor_handler_thread(test_context_t *param)
277 {
278 	T_QUIET; T_ASSERT_POSIX_ZERO(pthread_create(&param->executor_handler_thread_id, NULL, test_executor_thread, (void *)param), "Creation test worker thread");
279 }
280 /**
281  * Terminates the exception handler by sending a SIGUSR1 posix signal to the thread. On arrival it will terminate
282  * - Parameter param: Test context.
283  */
284 static void
terminate_exception_handler_thread(test_context_t * param)285 terminate_exception_handler_thread(test_context_t *param)
286 {
287 	void *ret_value;
288 	T_QUIET; T_ASSERT_POSIX_ZERO( pthread_kill(param->exception_handler_thread_id, SIGUSR1), "Verification send terminate signal to the exception handler thread");
289 	T_QUIET; T_ASSERT_POSIX_ZERO( pthread_join(param->exception_handler_thread_id, &ret_value), "Verification join to exception handler thread");
290 }
291 /**
292  * Terminates the test code executior  by sending a SIGUSR1 posix signal to the thread. On arrival it will terminate
293  * - Parameter param: Test context.
294  */
295 static void
terminate_executor_handler_thread(test_context_t * param)296 terminate_executor_handler_thread(test_context_t *param)
297 {
298 	void *ret_value;
299 	int pthread_kill_val =  pthread_kill(param->executor_handler_thread_id, SIGUSR1);
300 	T_QUIET; T_ASSERT_TRUE((pthread_kill_val == 0 || pthread_kill_val == ESRCH) ? TRUE : FALSE, "Verification send terminate to worker thread");
301 	T_QUIET; T_ASSERT_POSIX_ZERO( pthread_join(param->executor_handler_thread_id, &ret_value), "Verification join to the test worker thread");
302 }
303 
304 /**
305  * Exception handler thread. Receives the mach exception port messages, and dispatch them by using
306  * a standard mach_exc_server. It reads the messages one by one to allow some logging.
307  * Also sets its thread name to the EH test_name (EH from exception handler).
308  * Signals to the main thread that is ready to run the test by using the semaphore exception_thread_start_sema
309  *
310  * Please note that, in order to avoid problems it creates a working copy of the thread context, so, if the main loop
311  * discards the current test context, it can work with a copy.
312  * - Parameter param: Test context.
313  */
314 void *
exception_thread(void * params)315 exception_thread(void *params)
316 {
317 	char thread_name[MAX_MESSAGE];
318 	test_context_t info = *(test_context_t *)params;
319 	snprintf(thread_name, sizeof(thread_name), "EH %s", info.test->short_name);
320 	pthread_setname_np(thread_name);
321 	diag_mem_threshold_log_test("Startup of exception handler for test %s\n", info.test->short_name);
322 	dispatch_semaphore_signal(info.exception_thread_start_sema);
323 
324 	while (1) {
325 		/* Handle exceptions on exc_port */
326 		T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, mach_msg_server_once(mach_exc_server, 4096, exception_port, 0), "Verification of obtain a exception message");
327 		diag_mem_threshold_log_test("Exception arrived\n");
328 	}
329 	return NULL;
330 }
331 
332 /**
333  * Helper to test the diagnostics threshold limits for the tests. In order to ensure that everything works
334  * as expected, it also verifies the limit agains the ledger.
335  * - Parameter limit_param: new limit in bytes, -1 means "disable the threshold limit"
336  *
337  */
338 bool
set_memory_diagnostics_threshold_limits(uint64_t limit_param,bool assert_on_error)339 set_memory_diagnostics_threshold_limits(uint64_t limit_param, bool assert_on_error)
340 {
341 	memorystatus_diag_memlimit_properties_t limit;
342 	memorystatus_diag_memlimit_properties_t limit_verify;
343 	diag_mem_threshold_log_test("Set threshold limit to %d MB\n", limit_param >> 20);
344 	int pid = getpid();
345 	limit.memlimit = limit_param;
346 	int retValue = memorystatus_control(
347 		MEMORYSTATUS_CMD_SET_DIAG_LIMIT,
348 		pid,
349 		0,
350 		&limit, sizeof(limit)
351 		);
352 	T_ASSERT_MACH_SUCCESS( retValue, "Verification diagnostics threshold limit adjustment");
353 	if (assert_on_error) {
354 		retValue = memorystatus_control(
355 			MEMORYSTATUS_CMD_GET_DIAG_LIMIT,
356 			pid,
357 			0,
358 			&limit_verify, sizeof(limit_verify)
359 			);
360 		T_ASSERT_MACH_SUCCESS(retValue, "Verification memory diagnostics limit");
361 
362 		T_ASSERT_EQ(limit.memlimit, limit_verify.memlimit, "Verification of diag threshold mem limit");
363 	}
364 	return (retValue == KERN_SUCCESS) ? true : false;
365 }
366 
367 /**
368  * Helper that allows to waste memory and afterwards free it. It uses kernel memory to ensure is allocated really.
369  * - Parameter ammount: ammount of memory to waste in bytes.
370  */
371 void
diag_mem_threshold_waste_memory(uint64_t ammount)372 diag_mem_threshold_waste_memory(uint64_t ammount)
373 {
374 	mach_vm_address_t global_addr = 0;
375 	vm_size_t global_size = ammount;
376 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, mach_vm_allocate(mach_task_self(), &global_addr, global_size, VM_FLAGS_ANYWHERE ), "Allocate memory for test threshold");
377 
378 	diag_mem_threshold_log_test("Going to waste memory (%d MB)\n", ammount >> 20);
379 	memset((void *)global_addr, 0xaa, ammount);
380 	diag_mem_threshold_log_test("Memory wasted\n");
381 
382 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, mach_vm_deallocate(mach_task_self(), global_addr, global_size), "Deallocation of memory from test threshold");
383 }
384 /**
385  * Helper that allows to set a jetsam watermark
386  * - Parameter ammount: ammount of memory to waste in bytes.
387  */
388 void
diag_mem_set_jetsam_watermark(uint ammount)389 diag_mem_set_jetsam_watermark(uint ammount)
390 {
391 	diag_mem_threshold_log_test("Set jetsam watermark to %d MB\n", ammount >> 20);
392 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_HIGH_WATER_MARK, getpid(), ammount >> 20, NULL, 0), "Set jetsam watermark");
393 }
394 /**
395  * Helper that allows to set a jetsam watermark
396  * - Parameter ammount: ammount of memory to waste in bytes.
397  */
398 void
diag_mem_set_jetsam_limit(uint ammount)399 diag_mem_set_jetsam_limit(uint ammount)
400 {
401 	diag_mem_threshold_log_test("Set jetsam limit to %d MB\n", ammount >> 20);
402 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, getpid(), ammount >> 20, NULL, 0), "Set jetsam watermark");
403 }
404 
405 /**
406  * Standard mach exception handler invoked by mach_server
407  * - Parameters:
408  *   - exception_port: see mach documentation
409  *   - thread: see mach documentation
410  *   - task: see mach documentation
411  *   - exception: see mach documentation
412  *   - code: see mach documentation
413  *   - code_count: see mach documentation
414  */
415 kern_return_t
catch_mach_exception_raise(__unused mach_port_t port,__unused mach_port_t thread,__unused mach_port_t task,__unused exception_type_t exception,__unused exception_data_t code,__unused mach_msg_type_number_t code_count)416 catch_mach_exception_raise( __unused mach_port_t  port, __unused  mach_port_t  thread, __unused  mach_port_t  task, __unused exception_type_t  exception, __unused exception_data_t  code, __unused  mach_msg_type_number_t  code_count)
417 {
418 	#ifdef VERBOSE
419 	diag_mem_threshold_log_test("catch_mach_exception_raise:Have exception (expected) type 0x%x code len %d\n", exception, code_count);
420 	unsigned int j;
421 	for (j = 0; j < code_count << 1; j++) {
422 		diag_mem_threshold_log_test("catch_mach_exception_raise:Code[%d]=0x%x\n", j, code[j]);
423 	}
424 	#endif
425 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, semaphore_signal(exception_semaphore), "Verification of signal of exception semaphore");
426 	return KERN_SUCCESS;
427 }
428 /**
429  * Standard mach exception handler invoked by mach_server, not used in this application
430  * - Parameters:
431  *   - exception_port: see mach documentation
432  *   - exception: see mach documentation
433  *   - code: see mach documentation
434  *   - codeCnt: see mach documentation
435  *   - flavor: see mach documentation
436  *   - old_state: see mach documentation
437  *   - old_stateCnt: see mach documentation
438  *   - new_state: see mach documentation
439  *   - new_stateCnt: see mach documentation
440  */
441 kern_return_t
catch_mach_exception_raise_state(__unused mach_port_t port,__unused exception_type_t exception,__unused const exception_data_t code,__unused mach_msg_type_number_t codeCnt,__unused int * flavor,__unused const thread_state_t old_state,__unused mach_msg_type_number_t old_stateCnt,__unused thread_state_t new_state,__unused mach_msg_type_number_t * new_stateCnt)442 catch_mach_exception_raise_state(__unused mach_port_t port, __unused exception_type_t exception, __unused const exception_data_t code, __unused mach_msg_type_number_t codeCnt, __unused int *flavor, __unused const thread_state_t old_state, __unused mach_msg_type_number_t old_stateCnt, __unused thread_state_t new_state, __unused mach_msg_type_number_t *new_stateCnt)
443 {
444 	diag_mem_threshold_log_test("catch_mach_exception_raise_state:Have exception (expected) type 0x%x code len %d\n", exception, codeCnt);
445 	T_QUIET; T_ASSERT_TRUE(FALSE, "Unexpected exception handler called");
446 	return KERN_FAILURE;
447 }
448 
449 /**
450  * Standard mach exception handler invoked by mach_server, not used in this application
451  * - Parameters:
452  *   - exception_port: see mach documentation
453  *   - exception: see mach documentation
454  *   - code: see mach documentation
455  *   - codeCnt: see mach documentation
456  *   - flavor: see mach documentation
457  *   - old_state: see mach documentation
458  *   - old_stateCnt: see mach documentation
459  *   - new_state: see mach documentation
460  *   - new_stateCnt: see mach documentation
461  */
462 kern_return_t
catch_mach_exception_raise_state_identity(__unused mach_port_t port,__unused mach_port_t thread,__unused mach_port_t task,__unused exception_type_t exception,__unused mach_exception_data_t code,__unused mach_msg_type_number_t codeCnt,__unused int * flavor,__unused thread_state_t old_state,__unused mach_msg_type_number_t old_stateCnt,__unused thread_state_t new_state,__unused mach_msg_type_number_t * new_stateCnt)463 catch_mach_exception_raise_state_identity(__unused mach_port_t port, __unused mach_port_t thread, __unused mach_port_t task, __unused exception_type_t exception, __unused mach_exception_data_t code, __unused mach_msg_type_number_t codeCnt, __unused int *flavor, __unused thread_state_t old_state, __unused mach_msg_type_number_t old_stateCnt, __unused thread_state_t new_state, __unused mach_msg_type_number_t *new_stateCnt)
464 {
465 	#ifdef VERBOSE
466 	diag_mem_threshold_log_test("catch_mach_exception_raise_state_identity:Have exception (expected)  exception_port %x  thread %x task %x exception %x code %x codeCnt %x flavor %x old_statd %x old_stateCnt %x new_state %x new_stateCnt %x",
467 	    port,
468 	    thread,
469 	    task,
470 	    exception,
471 	    *code,
472 	    codeCnt,
473 	    *flavor,
474 	    old_state,
475 	    old_stateCnt,
476 	    new_state,
477 	    new_stateCnt);
478 
479 	for (unsigned int j = 0; j < codeCnt; j++) {
480 		diag_mem_threshold_log_test("catch_mach_exception_raise_state_identity:Code[%d]=0x%llx\n", j, code[j]);
481 	}
482 	#endif
483 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, semaphore_signal(exception_semaphore), "Verification of signal of exception semaphore");
484 	diag_mem_threshold_log_test("catch_mach_exception_raise_state_identity:Semaphore signalled");
485 	return KERN_SUCCESS;
486 }
487 /**
488  * getter of the current exception port as a helper.
489  */
490 mach_port_t
diag_mem_threshold_get_exception_port(void)491 diag_mem_threshold_get_exception_port(void)
492 {
493 	return exception_port;
494 }
495 /**
496  * Getter of the configured exceptions masks for this execution
497  */
498 exception_mask_t
diag_mem_threshold_get_exceptions_mask(void)499 diag_mem_threshold_get_exceptions_mask(void)
500 {
501 	return mask;
502 }
503 /**
504  *  Wait until the exceptions thread is ready.
505  * - Parameter info: test context.
506  */
507 static void
wait_for_exceptions_thread(test_context_t * info)508 wait_for_exceptions_thread(test_context_t *info)
509 {
510 	dispatch_semaphore_wait(info->exception_thread_start_sema, DISPATCH_TIME_FOREVER);
511 }
512 /**
513  *  Wait until the executoir thread is ready.
514  * - Parameter info: test context.
515  */
516 static void
wait_for_executor_thread(test_context_t * info)517 wait_for_executor_thread(test_context_t *info)
518 {
519 	dispatch_semaphore_wait(info->executor_thread_start_sema, DISPATCH_TIME_FOREVER);
520 }
521 /**
522  *  Wait until the executor thread is ready to accept exceptions from the test launcher. It is used to handle also internally
523  *  exceptions by the test worker thread. An example of that is the double limit test, where an app sets a limit, it passes it, and
524  *  wants to re-enable the threshold limt. In this case part of the exception verification is done inside the test, and the rest (second exception)
525  *  is delegated to the test launcher. Test launcher will not consider exceptions before this semaphore is signalled.
526  * - Parameter info: test context.
527  */
528 static void
wait_for_exceptions_ready(test_context_t * info)529 wait_for_exceptions_ready(test_context_t *info)
530 {
531 	dispatch_semaphore_wait(info->executor_ready_for_exceptions, DISPATCH_TIME_FOREVER);
532 }
533 /* Temporally block signals while logging */
534 static sigset_t
thread_block_signal(int blocking_signal_mask)535 thread_block_signal(int blocking_signal_mask)
536 {
537 	sigset_t   signal_mask;
538 	sigset_t   signal_mask_return;
539 	sigemptyset(&signal_mask);
540 	sigaddset(&signal_mask, blocking_signal_mask);
541 	int mask_set_ret_value = pthread_sigmask(SIG_BLOCK, &signal_mask, &signal_mask_return);
542 	assert( mask_set_ret_value == 0);
543 	return signal_mask_return;
544 }
545 /* Restore the signals configuration */
546 static void
thread_unblock_signal(sigset_t unblocking_signal_mask)547 thread_unblock_signal(sigset_t unblocking_signal_mask)
548 {
549 	assert(pthread_sigmask(SIG_SETMASK, &unblocking_signal_mask, NULL) == 0);
550 }
551 /**
552  *
553  * Simple log routine that adds information about the thread that is logging
554  */
555 void
diag_mem_threshold_log_test(const char * fmt,...)556 diag_mem_threshold_log_test(const char *fmt, ...)
557 {
558 	char log_string[MAX_MESSAGE];
559 	char thread_name[MAX_MESSAGE];
560 	va_list valist;
561 	/**
562 	 * To avoid problems with the xnu testing library log support, lets avoid signals while
563 	 * logging
564 	 */
565 	sigset_t sig_mask = thread_block_signal(SIGUSR1);
566 
567 	va_start(valist, fmt);
568 #pragma clang diagnostic push
569 #pragma clang diagnostic ignored "-Wformat-nonliteral"
570 	vsnprintf(log_string, sizeof(log_string), (const char *)fmt, valist);
571 #pragma clang diagnostic pop
572 
573 	assert(pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name)) == 0);
574 	T_LOG("[%-32.32s] %s", thread_name, log_string);
575 	/* And restore the signals */
576 	thread_unblock_signal(sig_mask);
577 }
578 
579 /**
580  * Prepare all the semaphores and other stuff required for all the tests
581  */
582 static void
prepare_harness(void)583 prepare_harness(void)
584 {
585 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, semaphore_create(mach_task_self(), &exception_semaphore, SYNC_POLICY_FIFO, 0), "Creation of semaphore for exceptions");
586 	init_mach_exceptions_port();
587 }
588 
589 /**
590  * Prepare all the semaphores and other stuff required for all the tests
591  */
592 static void
remove_harness(void)593 remove_harness(void)
594 {
595 	T_QUIET; T_ASSERT_EQ(KERN_SUCCESS, semaphore_destroy(mach_task_self(), exception_semaphore), "Destruction of exceptions semaphore");
596 	deinit_mach_exceptions_port();
597 }
598 
599 /**
600  * Handler of the SIGUSR1 signal, just terminates the current thread.
601  */
602 static void
termination_handler(__unused int signo,__unused siginfo_t * info,__unused void * extra)603 termination_handler(__unused int signo, __unused  siginfo_t *info, __unused void *extra)
604 {
605 	diag_mem_threshold_log_test("End of thread\n");
606 	pthread_exit(NULL);
607 	// We should never reach that point..
608 	T_QUIET; T_ASSERT_MACH_SUCCESS(0, "Cannot kill this thread");
609 }
610 /**
611  * Standard function to set a singal handler routine. Sets the handler for SIGUSR1 to the
612  * termination_handler routine, and indeed terminates current thread.
613  */
614 static void
set_sig_handler(void)615 set_sig_handler(void)
616 {
617 	struct sigaction action;
618 
619 	action.sa_flags = SA_SIGINFO;
620 	action.sa_sigaction = termination_handler;
621 
622 	T_QUIET; T_ASSERT_MACH_SUCCESS(sigaction(SIGUSR1, &action, &original_action), "Verification of adjustment of signal handler");
623 }
624 /**
625  * Standard function to set a singal handler routine. Sets the handler for SIGUSR1 to the
626  * termination_handler routine, and indeed terminates current thread.
627  */
628 static void
reset_sig_handler(void)629 reset_sig_handler(void)
630 {
631 	T_QUIET; T_ASSERT_MACH_SUCCESS(sigaction(SIGUSR1, &original_action, NULL), "Verification of reset signal handler");
632 }
633 /**
634  * Since lldb do not work with CORPSE exceptions, that allows to
635  * store a task coredump for further analysis.
636  */
637 static void
enable_coredumps(void)638 enable_coredumps(void)
639 {
640 	struct rlimit limits = {
641 		.rlim_cur = UINT64_MAX,
642 		.rlim_max = UINT64_MAX
643 	};
644 	T_QUIET; T_ASSERT_MACH_SUCCESS( getrlimit(RLIMIT_CORE, &limits), "obtain coredump limits");
645 	limits.rlim_cur = 0x7fffffffffffffff;
646 	T_QUIET; T_ASSERT_MACH_SUCCESS( setrlimit(RLIMIT_CORE, &limits), "set coredump limits");
647 }
648 
649 /**
650  * Return the ammount of memory available on this device
651  */
652 static uint64_t
get_hw_memory_size(void)653 get_hw_memory_size(void)
654 {
655 	int ret;
656 	uint64_t max_mem;
657 	size_t max_mem_size = sizeof(max_mem);
658 	ret = sysctlbyname(g_sysctl_memsize_name, &max_mem, &max_mem_size, NULL, 0);
659 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "memsize sysctl failed");
660 	return max_mem;
661 }
662