1 #include <darwintest.h>
2 #include <darwintest_utils.h>
4 #include <mach/task_info.h>
6 #include <sys/kern_memorystatus.h>
9 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
12 void test_os_proc_available_memory(void);
13 extern int getpid(void);
15 T_DECL(test_os_proc_available_memory
, "Basic available memory")
18 task_vm_info_data_t vm_info
= {};
19 mach_msg_type_number_t count
= TASK_VM_INFO_REV4_COUNT
;
20 uint64_t remainingBytes
;
22 err
= task_info(mach_task_self(), TASK_VM_INFO
, (task_info_t
)&vm_info
, &count
);
23 remainingBytes
= os_proc_available_memory();
25 T_ASSERT_MACH_SUCCESS(err
, "verify task_info call succeeded");
26 T_EXPECT_EQ(count
, TASK_VM_INFO_REV4_COUNT
, "task_info count(%d) is equal to TASK_VM_INFO_REV4_COUNT (%d)\n", count
, TASK_VM_INFO_REV4_COUNT
);
27 T_EXPECT_NE(remainingBytes
, 0ULL, "os_proc_available_memory() should not return 0");
28 T_EXPECT_NE(vm_info
.limit_bytes_remaining
, 0ULL, "vm_info.limit_bytes_remaining should not return 0");
29 T_EXPECT_EQ(vm_info
.limit_bytes_remaining
, remainingBytes
,
30 "task_info --rev4 call returned value 0x%llx for vm_info.limit_bytes_remaining. Expected 0x%llx",
31 vm_info
.limit_bytes_remaining
, remainingBytes
);
33 /* this should now make the available memory return 0 */
34 proc_track_dirty(getpid(), PROC_DIRTY_TRACK
);
36 count
= TASK_VM_INFO_REV4_COUNT
;
37 err
= task_info(mach_task_self(), TASK_VM_INFO
, (task_info_t
)&vm_info
, &count
);
38 remainingBytes
= os_proc_available_memory();
40 T_ASSERT_MACH_SUCCESS(err
, "verify task_info call succeeded");
41 T_EXPECT_EQ(count
, TASK_VM_INFO_REV4_COUNT
, "task_info count(%d) is equal to TASK_VM_INFO_REV4_COUNT\n", count
);
42 T_EXPECT_EQ(remainingBytes
, 0ULL, "os_proc_available_memory() should return 0");
43 T_EXPECT_EQ(vm_info
.limit_bytes_remaining
, 0ULL, "vm_info.limit_bytes_remaining should return 0");
44 T_EXPECT_EQ(vm_info
.limit_bytes_remaining
, remainingBytes
,
45 "task_info --rev4 call returned value 0x%llx for vm_info.limit_bytes_remaining. Expected 0x%llx",
46 vm_info
.limit_bytes_remaining
, remainingBytes
);
51 * os_proc_available_memory is only available on embedded.
52 * But the underlying syscall works on macOS to support catalyst
53 * extensions. So we test the syscall directly here.
55 extern uint64_t __memorystatus_available_memory(void);
58 set_memlimit(pid_t pid
, int32_t limit_mb
)
60 memorystatus_memlimit_properties_t mmprops
;
62 memset(&mmprops
, 0, sizeof(memorystatus_memlimit_properties_t
));
64 mmprops
.memlimit_active
= limit_mb
;
65 mmprops
.memlimit_inactive
= limit_mb
;
67 /* implies we want to set fatal limits */
68 mmprops
.memlimit_active_attr
|= MEMORYSTATUS_MEMLIMIT_ATTR_FATAL
;
69 mmprops
.memlimit_inactive_attr
|= MEMORYSTATUS_MEMLIMIT_ATTR_FATAL
;
70 return memorystatus_control(MEMORYSTATUS_CMD_SET_MEMLIMIT_PROPERTIES
, pid
, 0, &mmprops
, sizeof(mmprops
));
72 T_DECL(test_os_proc_available_memory
, "Basic available memory")
74 uint64_t available_memory
;
77 static const size_t kLimitMb
= 1024;
80 * Should return 0 unless an proccess is both memory managed and has a
83 ret
= memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED
, pid
, 0, NULL
, 0);
84 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "memorystatus_control");
86 available_memory
= __memorystatus_available_memory();
87 T_ASSERT_EQ(available_memory
, 0ULL, "__memorystatus_available_memory == 0");
89 ret
= memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED
, pid
, 1, NULL
, 0);
90 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "memorystatus_control");
91 available_memory
= __memorystatus_available_memory();
92 T_ASSERT_EQ(available_memory
, 0ULL, "__memorystatus_available_memory == 0");
95 * Should not return 0 for managed procs with a hard memory limit.
97 ret
= set_memlimit(pid
, kLimitMb
);
98 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "memorystatus_control");
99 available_memory
= __memorystatus_available_memory();
100 T_ASSERT_NE(available_memory
, 0ULL, "__memorystatus_available_memory != 0");