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(¶m->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(¶m->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