xref: /xnu-12377.81.4/tests/vm/memorystatus_kill_counts.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <mach/mach.h>
4 #include <unistd.h>
5 #include <mach-o/dyld.h>
6 #include <sys/sysctl.h>
7 #include <sys/kern_memorystatus.h>
8 
9 #include <darwintest.h>
10 #include <darwintest_utils.h>
11 
12 T_GLOBAL_META(
13 	T_META_RUN_CONCURRENTLY(false),
14 	T_META_RADAR_COMPONENT_NAME("xnu"),
15 	T_META_RADAR_COMPONENT_VERSION("VM"));
16 
17 #define MAX_TRIES 3
18 
19 static int
get_kill_counts(uint32_t * buffer,size_t buffer_size,int band,int flags)20 get_kill_counts(uint32_t *buffer, size_t buffer_size, int band, int flags)
21 {
22 	return memorystatus_control(MEMORYSTATUS_CMD_GET_KILL_COUNTS, band, flags, buffer, buffer_size);
23 }
24 
25 T_HELPER_DECL(thrown_overboard, "child to be jetsammed") {
26 	for (;;) {
27 		sleep(1);
28 	}
29 }
30 
31 static void
spawn_and_jetsam(int32_t band)32 spawn_and_jetsam(int32_t band)
33 {
34 	static char path[PATH_MAX] = {0};
35 	static uint32_t path_size = sizeof(path);
36 	int error;
37 	pid_t child;
38 	memorystatus_priority_properties_t prop;
39 
40 	if (!path[0]) {
41 		T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &path_size), "_NSGetExecutablePath");
42 	}
43 
44 	char *args[] = { path, "-n", "thrown_overboard", NULL};
45 	error = dt_launch_tool(&child, args, false, NULL, NULL);
46 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "spawn child");
47 	prop.priority = band;
48 	prop.user_data = 0;
49 	error = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED, child, 1, NULL, 0);
50 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "set child managed");
51 	error = memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, child, MEMORYSTATUS_SET_PRIORITY_ASSERTION, &prop, sizeof(prop));
52 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "set child priority");
53 	error = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child, 0, NULL, 0);
54 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "jetsam child");
55 }
56 
57 #define N_TEST_BANDS 5
58 // Insert at head to skip idle aging
59 int32_t test_bands[N_TEST_BANDS] = {JETSAM_PRIORITY_IDLE_HEAD, JETSAM_PRIORITY_BACKGROUND, 35, JETSAM_PRIORITY_MAIL, 45};
60 int32_t expected_bands[N_TEST_BANDS] = {JETSAM_PRIORITY_IDLE, JETSAM_PRIORITY_BACKGROUND, 35, JETSAM_PRIORITY_MAIL, 45};
61 int32_t proc_counts[N_TEST_BANDS] = {2, 3, 1, 2, 4};
62 
63 #define BUFFER_SIZE (sizeof(uint32_t) * (JETSAM_REASON_MEMORYSTATUS_MAX + 1))
64 
65 T_DECL(memorystatus_kill_counts, "jetsam kill counts",
66     T_META_ASROOT(true))
67 {
68 	int i, j;
69 	uint32_t *buffers[N_TEST_BANDS];
70 
71 	/* Spawn a handful of children and kill them */
72 	for (i = 0; i < N_TEST_BANDS; i++) {
73 		buffers[i] = malloc(BUFFER_SIZE);
74 		T_QUIET; T_ASSERT_POSIX_NOTNULL(buffers[i], "malloc()");
75 		for (j = 0; j < proc_counts[i]; j++) {
76 			spawn_and_jetsam(test_bands[i]);
77 		}
78 	}
79 
80 	void (^get_all_kill_counts)(uint32_t**, int) = ^(uint32_t **buffers, int flags){
81 		int i, error;
82 		for (i = 0; i < N_TEST_BANDS; i++) {
83 			error = get_kill_counts(buffers[i], BUFFER_SIZE, expected_bands[i], flags);
84 			T_ASSERT_POSIX_ZERO(error, "get kill counts (band %d)", expected_bands[i]);
85 		}
86 	};
87 
88 	/* Query for size */
89 	void (^check_buffer)(uint32_t**, bool) = ^(uint32_t **buffers, bool expect_missing) {
90 		int i;
91 		bool missing_proc;
92 
93 		missing_proc = false;
94 		for (i = 0; i < N_TEST_BANDS; i++) {
95 			uint32_t count = buffers[i][kMemorystatusKilled];
96 			missing_proc = missing_proc || (count < proc_counts[i]);
97 			if (!expect_missing) {
98 				T_QUIET; T_EXPECT_LE(proc_counts[i], count, "Children in band %d found in kill list.", test_bands[i]);
99 			}
100 		}
101 
102 		if (!expect_missing) {
103 			T_EXPECT_FALSE(missing_proc, "Found all children in kill list");
104 		} else {
105 			T_EXPECT_TRUE(missing_proc, "Previously cleared entries not in list");
106 		}
107 	};
108 
109 	/* Get the list once, and don't clear it. */
110 	T_LOG("--- Getting kill counts (without clear) ---");
111 	get_all_kill_counts(buffers, 0);
112 	check_buffer(buffers, false);
113 
114 	/* Check again (w/ clear) - The list should still have the same entries. */
115 	T_LOG("--- Getting kill counts (with clear) ---");
116 	get_all_kill_counts(buffers, MEMORYSTATUS_GET_KILL_COUNTS_CLEAR);
117 	check_buffer(buffers, false);
118 
119 	/*
120 	 * Check one last time - The list should have been cleared.
121 	 * Things could have been jetsammed since we cleared the list, but we only
122 	 * care about the presence of our test children who have a generic
123 	 * jetsam reason - that shouldn't happen elsewhere.
124 	 */
125 	T_LOG("--- Getting kill counts (after clear) ---");
126 	get_all_kill_counts(buffers, 0);
127 	check_buffer(buffers, true);
128 }
129