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_PRIORITY_PROPERTIES, child, 0, &prop, sizeof(prop));
50 T_QUIET; T_ASSERT_POSIX_ZERO(error, "set child properties");
51 error = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child, 0, NULL, 0);
52 T_QUIET; T_ASSERT_POSIX_ZERO(error, "jetsam child");
53 }
54
55 #define N_TEST_BANDS 5
56 int32_t test_bands[N_TEST_BANDS] = {0, 30, 35, 40, 45};
57 int32_t proc_counts[N_TEST_BANDS] = {2, 3, 1, 2, 4};
58
59 #define BUFFER_SIZE (sizeof(uint32_t) * (JETSAM_REASON_MEMORYSTATUS_MAX + 1))
60
61 T_DECL(memorystatus_kill_counts, "jetsam kill counts",
62 T_META_ASROOT(true))
63 {
64 int i, j;
65 uint32_t *buffers[N_TEST_BANDS];
66
67 /* Spawn a handful of children and kill them */
68 for (i = 0; i < N_TEST_BANDS; i++) {
69 buffers[i] = malloc(BUFFER_SIZE);
70 T_QUIET; T_ASSERT_POSIX_NOTNULL(buffers[i], "malloc()");
71 for (j = 0; j < proc_counts[i]; j++) {
72 spawn_and_jetsam(test_bands[i]);
73 }
74 }
75
76 void (^get_all_kill_counts)(uint32_t**, int) = ^(uint32_t **buffers, int flags){
77 int i, error;
78 for (i = 0; i < N_TEST_BANDS; i++) {
79 error = get_kill_counts(buffers[i], BUFFER_SIZE, test_bands[i], flags);
80 T_ASSERT_POSIX_ZERO(error, "get kill counts (band %d)", test_bands[i]);
81 }
82 };
83
84 /* Query for size */
85 void (^check_buffer)(uint32_t**, bool) = ^(uint32_t **buffers, bool expect_missing) {
86 int i;
87 bool missing_proc;
88
89 missing_proc = false;
90 for (i = 0; i < N_TEST_BANDS; i++) {
91 uint32_t count = buffers[i][kMemorystatusKilled];
92 missing_proc = missing_proc || (count < proc_counts[i]);
93 if (!expect_missing) {
94 T_QUIET; T_EXPECT_LE(proc_counts[i], count, "Children in band %d found in kill list.", test_bands[i]);
95 }
96 }
97
98 if (!expect_missing) {
99 T_EXPECT_FALSE(missing_proc, "Found all children in kill list");
100 } else {
101 T_EXPECT_TRUE(missing_proc, "Previously cleared entries not in list");
102 }
103 };
104
105 /* Get the list once, and don't clear it. */
106 T_LOG("--- Getting kill counts (without clear) ---");
107 get_all_kill_counts(buffers, 0);
108 check_buffer(buffers, false);
109
110 /* Check again (w/ clear) - The list should still have the same entries. */
111 T_LOG("--- Getting kill counts (with clear) ---");
112 get_all_kill_counts(buffers, MEMORYSTATUS_GET_KILL_COUNTS_CLEAR);
113 check_buffer(buffers, false);
114
115 /*
116 * Check one last time - The list should have been cleared.
117 * Things could have been jetsammed since we cleared the list, but we only
118 * care about the presence of our test children who have a generic
119 * jetsam reason - that shouldn't happen elsewhere.
120 */
121 T_LOG("--- Getting kill counts (after clear) ---");
122 get_all_kill_counts(buffers, 0);
123 check_buffer(buffers, true);
124 }
125