3 #include <sys/sysctl.h>
4 #include <sys/kern_memorystatus.h>
6 #include <mach-o/dyld.h>
7 #include <mach/mach_vm.h>
8 #include <mach/vm_page_size.h> /* Needed for vm_region info */
9 #include <mach/shared_region.h>
10 #include <mach/mach.h>
15 #include <darwintest.h>
16 #include <darwintest_utils.h>
18 #include "memorystatus_assertion_helpers.h"
21 T_META_NAMESPACE("xnu.vm"),
22 T_META_CHECK_LEAKS(false)
25 #define MEM_SIZE_MB 10
26 #define NUM_ITERATIONS 5
27 #define FREEZE_PAGES_MAX 256
29 #define CREATE_LIST(X) \
31 X(TOO_FEW_ARGUMENTS) \
32 X(SYSCTL_VM_PAGESIZE_FAILED) \
33 X(VM_PAGESIZE_IS_ZERO) \
34 X(DISPATCH_SOURCE_CREATE_FAILED) \
35 X(INITIAL_SIGNAL_TO_PARENT_FAILED) \
36 X(SIGNAL_TO_PARENT_FAILED) \
37 X(MEMORYSTATUS_CONTROL_FAILED) \
38 X(IS_FREEZABLE_NOT_AS_EXPECTED) \
39 X(MEMSTAT_PRIORITY_CHANGE_FAILED) \
40 X(INVALID_ALLOCATE_PAGES_ARGUMENTS) \
43 #define EXIT_CODES_ENUM(VAR) VAR,
45 CREATE_LIST(EXIT_CODES_ENUM
)
48 #define EXIT_CODES_STRING(VAR) #VAR,
49 static const char *exit_codes_str
[] = {
50 CREATE_LIST(EXIT_CODES_STRING
)
57 size_t size
= sizeof(vmpage_size
);
58 int ret
= sysctlbyname("vm.pagesize", &vmpage_size
, &size
, NULL
, 0);
59 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "failed to query vm.pagesize");
60 T_QUIET
; T_ASSERT_GT(vmpage_size
, 0, "vm.pagesize is not > 0");
64 static pid_t child_pid
= -1;
65 static int freeze_count
= 0;
67 void move_to_idle_band(void);
68 void run_freezer_test(int);
69 void freeze_helper_process(void);
70 /* Gets and optionally sets the freeze pages max threshold */
71 int sysctl_freeze_pages_max(int* new_value
);
73 /* NB: in_shared_region and get_rprvt are pulled from the memorystatus unit test.
74 * We're moving away from those unit tests, so they're copied here.
77 /* Cribbed from 'top'... */
79 in_shared_region(mach_vm_address_t addr
, cpu_type_t type
)
81 mach_vm_address_t base
= 0, size
= 0;
85 base
= SHARED_REGION_BASE_ARM
;
86 size
= SHARED_REGION_SIZE_ARM
;
90 base
= SHARED_REGION_BASE_ARM64
;
91 size
= SHARED_REGION_SIZE_ARM64
;
96 base
= SHARED_REGION_BASE_X86_64
;
97 size
= SHARED_REGION_SIZE_X86_64
;
101 base
= SHARED_REGION_BASE_I386
;
102 size
= SHARED_REGION_SIZE_I386
;
105 case CPU_TYPE_POWERPC
:
106 base
= SHARED_REGION_BASE_PPC
;
107 size
= SHARED_REGION_SIZE_PPC
;
110 case CPU_TYPE_POWERPC64
:
111 base
= SHARED_REGION_BASE_PPC64
;
112 size
= SHARED_REGION_SIZE_PPC64
;
118 fprintf(stderr
, "unknown CPU type: 0x%x\n", t
);
123 return addr
>= base
&& addr
< (base
+ size
);
126 /* Get the resident private memory of the given pid */
127 static unsigned long long
130 mach_port_name_t task
;
133 mach_vm_size_t rprvt
= 0;
134 mach_vm_size_t empty
= 0;
135 mach_vm_size_t fw_private
= 0;
136 mach_vm_size_t pagesize
= vm_kernel_page_size
; // The vm_region page info is reported
137 // in terms of vm_kernel_page_size.
138 mach_vm_size_t regs
= 0;
140 mach_vm_address_t addr
;
145 kr
= task_for_pid(mach_task_self(), pid
, &task
);
146 T_QUIET
; T_ASSERT_TRUE(kr
== KERN_SUCCESS
, "Unable to get task_for_pid of child");
148 for (addr
= 0;; addr
+= size
) {
149 vm_region_top_info_data_t info
;
150 mach_msg_type_number_t count
= VM_REGION_TOP_INFO_COUNT
;
151 mach_port_t object_name
;
153 kr
= mach_vm_region(task
, &addr
, &size
, VM_REGION_TOP_INFO
, (vm_region_info_t
)&info
, &count
, &object_name
);
154 if (kr
!= KERN_SUCCESS
) {
158 #if defined (__arm64__)
159 if (in_shared_region(addr
, CPU_TYPE_ARM64
)) {
161 if (in_shared_region(addr
, CPU_TYPE_ARM
)) {
164 fw_private
+= info
.private_pages_resident
* pagesize
;
167 * Check if this process has the globally shared
168 * text and data regions mapped in. If so, set
169 * split to TRUE and avoid checking
172 if (split
== FALSE
&& info
.share_mode
== SM_EMPTY
) {
173 vm_region_basic_info_data_64_t b_info
;
174 mach_vm_address_t b_addr
= addr
;
175 mach_vm_size_t b_size
= size
;
176 count
= VM_REGION_BASIC_INFO_COUNT_64
;
178 kr
= mach_vm_region(task
, &b_addr
, &b_size
, VM_REGION_BASIC_INFO_64
, (vm_region_info_t
)&b_info
, &count
, &object_name
);
179 if (kr
!= KERN_SUCCESS
) {
183 if (b_info
.reserved
) {
189 * Short circuit the loop if this isn't a shared
190 * private region, since that's the only region
191 * type we care about within the current address
194 if (info
.share_mode
!= SM_PRIVATE
) {
202 * Update counters according to the region type.
205 if (info
.share_mode
== SM_COW
&& info
.ref_count
== 1) {
206 // Treat single reference SM_COW as SM_PRIVATE
207 info
.share_mode
= SM_PRIVATE
;
210 switch (info
.share_mode
) {
212 // Treat SM_LARGE_PAGE the same as SM_PRIVATE
213 // since they are not shareable and are wired.
215 rprvt
+= info
.private_pages_resident
* pagesize
;
216 rprvt
+= info
.shared_pages_resident
* pagesize
;
226 // Treat kernel_task specially
227 if (info
.share_mode
== SM_COW
) {
228 rprvt
+= info
.private_pages_resident
* pagesize
;
233 if (info
.share_mode
== SM_COW
) {
234 rprvt
+= info
.private_pages_resident
* pagesize
;
248 move_to_idle_band(void)
250 memorystatus_priority_properties_t props
;
252 * Freezing a process also moves it to an elevated jetsam band in order to protect it from idle exits.
253 * So we move the child process to the idle band to mirror the typical 'idle app being frozen' scenario.
255 props
.priority
= JETSAM_PRIORITY_IDLE
;
259 * This requires us to run as root (in the absence of entitlement).
260 * Hence the T_META_ASROOT(true) in the T_HELPER_DECL.
262 if (memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES
, getpid(), 0, &props
, sizeof(props
))) {
263 exit(MEMSTAT_PRIORITY_CHANGE_FAILED
);
268 freeze_helper_process(void)
271 int ret
, freeze_enabled
, errno_freeze_sysctl
;
272 uint64_t resident_memory_before
, resident_memory_after
, vmpage_size
;
273 vmpage_size
= (uint64_t) get_vmpage_size();
274 resident_memory_before
= get_rprvt(child_pid
) / vmpage_size
;
276 T_LOG("Freezing child pid %d", child_pid
);
277 ret
= sysctlbyname("kern.memorystatus_freeze", NULL
, NULL
, &child_pid
, sizeof(child_pid
));
278 errno_freeze_sysctl
= errno
;
282 * The child process toggles its freezable state on each iteration.
283 * So a failure for every alternate freeze is expected.
285 if (freeze_count
% 2) {
286 length
= sizeof(freeze_enabled
);
287 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled
, &length
, NULL
, 0),
288 "failed to query vm.freeze_enabled");
289 if (freeze_enabled
) {
290 errno
= errno_freeze_sysctl
;
291 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_freeze failed");
293 /* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
294 T_LOG("Freeze has been disabled. Terminating early.");
297 resident_memory_after
= get_rprvt(child_pid
) / vmpage_size
;
298 uint64_t freeze_pages_max
= (uint64_t) sysctl_freeze_pages_max(NULL
);
299 T_QUIET
; T_ASSERT_LT(resident_memory_after
, resident_memory_before
, "Freeze didn't reduce resident memory set");
300 if (resident_memory_before
> freeze_pages_max
) {
301 T_QUIET
; T_ASSERT_LE(resident_memory_before
- resident_memory_after
, freeze_pages_max
, "Freeze pages froze more than the threshold.");
303 ret
= sysctlbyname("kern.memorystatus_thaw", NULL
, NULL
, &child_pid
, sizeof(child_pid
));
304 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_thaw failed");
306 T_QUIET
; T_ASSERT_TRUE(ret
!= KERN_SUCCESS
, "Freeze should have failed");
307 T_LOG("Freeze failed as expected");
312 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kill(child_pid
, SIGUSR1
), "failed to send SIGUSR1 to child process");
316 run_freezer_test(int num_pages
)
318 int ret
, freeze_enabled
;
320 char **launch_tool_args
;
321 char testpath
[PATH_MAX
];
322 uint32_t testpath_buf_size
;
323 dispatch_source_t ds_freeze
, ds_proc
;
326 length
= sizeof(freeze_enabled
);
327 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled
, &length
, NULL
, 0),
328 "failed to query vm.freeze_enabled");
329 if (!freeze_enabled
) {
330 /* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
331 T_SKIP("Freeze has been disabled. Skipping test.");
334 signal(SIGUSR1
, SIG_IGN
);
335 ds_freeze
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, SIGUSR1
, 0, dispatch_get_main_queue());
336 T_QUIET
; T_ASSERT_NOTNULL(ds_freeze
, "dispatch_source_create (ds_freeze)");
338 dispatch_source_set_event_handler(ds_freeze
, ^{
339 if (freeze_count
< NUM_ITERATIONS
) {
340 freeze_helper_process();
342 kill(child_pid
, SIGKILL
);
343 dispatch_source_cancel(ds_freeze
);
346 dispatch_activate(ds_freeze
);
348 testpath_buf_size
= sizeof(testpath
);
349 ret
= _NSGetExecutablePath(testpath
, &testpath_buf_size
);
350 T_QUIET
; T_ASSERT_POSIX_ZERO(ret
, "_NSGetExecutablePath");
351 T_LOG("Executable path: %s", testpath
);
353 sprintf(sz_str
, "%d", num_pages
);
354 launch_tool_args
= (char *[]){
363 /* Spawn the child process. Suspend after launch until the exit proc handler has been set up. */
364 ret
= dt_launch_tool(&child_pid
, launch_tool_args
, true, NULL
, NULL
);
366 T_LOG("dt_launch tool returned %d with error code %d", ret
, errno
);
368 T_QUIET
; T_ASSERT_POSIX_SUCCESS(child_pid
, "dt_launch_tool");
370 ds_proc
= dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t)child_pid
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue());
371 T_QUIET
; T_ASSERT_NOTNULL(ds_proc
, "dispatch_source_create (ds_proc)");
373 dispatch_source_set_event_handler(ds_proc
, ^{
374 int status
= 0, code
= 0;
375 pid_t rc
= waitpid(child_pid
, &status
, 0);
376 T_QUIET
; T_ASSERT_EQ(rc
, child_pid
, "waitpid");
377 code
= WEXITSTATUS(status
);
381 } else if (code
> 0 && code
< EXIT_CODE_MAX
) {
382 T_ASSERT_FAIL("Child exited with %s", exit_codes_str
[code
]);
384 T_ASSERT_FAIL("Child exited with unknown exit code %d", code
);
387 dispatch_activate(ds_proc
);
389 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kill(child_pid
, SIGCONT
), "failed to send SIGCONT to child process");
394 allocate_pages(int num_pages
)
398 __block
int num_iter
= 0;
400 dispatch_source_t ds_signal
;
401 vmpgsize
= get_vmpage_size();
403 printf("Invalid number of pages to allocate: %d\n", num_pages
);
404 exit(INVALID_ALLOCATE_PAGES_ARGUMENTS
);
407 buf
= (char**)malloc(sizeof(char*) * (size_t)num_pages
);
409 /* Gives us the compression ratio we see in the typical case (~2.7) */
410 for (j
= 0; j
< num_pages
; j
++) {
411 buf
[j
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
413 for (i
= 0; i
< vmpgsize
; i
+= 16) {
414 memset(&buf
[j
][i
], val
, 16);
415 if (i
< 3400 * (vmpgsize
/ 4096)) {
421 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
), dispatch_get_main_queue(), ^{
422 /* Signal to the parent that we're done allocating and it's ok to freeze us */
423 printf("[%d] Sending initial signal to parent to begin freezing\n", getpid());
424 if (kill(getppid(), SIGUSR1
) != 0) {
425 exit(INITIAL_SIGNAL_TO_PARENT_FAILED
);
429 signal(SIGUSR1
, SIG_IGN
);
430 ds_signal
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, SIGUSR1
, 0, dispatch_get_main_queue());
431 if (ds_signal
== NULL
) {
432 exit(DISPATCH_SOURCE_CREATE_FAILED
);
435 dispatch_source_set_event_handler(ds_signal
, ^{
436 int current_state
, new_state
;
439 /* Make sure all the pages are accessed before trying to freeze again */
440 for (int x
= 0; x
< num_pages
; x
++) {
444 current_state
= memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE
, getpid(), 0, NULL
, 0);
445 /* Sysprocs start off as unfreezable. Verify that first. */
446 if (num_iter
== 0 && current_state
!= 0) {
447 exit(IS_FREEZABLE_NOT_AS_EXPECTED
);
450 /* Toggle freezable state */
451 new_state
= (current_state
) ? 0: 1;
452 printf("[%d] Changing state from %s to %s\n", getpid(),
453 (current_state
) ? "freezable": "unfreezable", (new_state
) ? "freezable": "unfreezable");
454 if (memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE
, getpid(), (uint32_t)new_state
, NULL
, 0) != KERN_SUCCESS
) {
455 exit(MEMORYSTATUS_CONTROL_FAILED
);
458 /* Verify that the state has been set correctly */
459 current_state
= memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE
, getpid(), 0, NULL
, 0);
460 if (new_state
!= current_state
) {
461 exit(IS_FREEZABLE_NOT_AS_EXPECTED
);
465 if (kill(getppid(), SIGUSR1
) != 0) {
466 exit(SIGNAL_TO_PARENT_FAILED
);
469 dispatch_activate(ds_signal
);
475 T_HELPER_DECL(allocate_pages
,
476 "allocates pages to freeze",
477 T_META_ASROOT(true)) {
479 exit(TOO_FEW_ARGUMENTS
);
482 int num_pages
= atoi(argv
[0]);
483 allocate_pages(num_pages
);
486 T_DECL(freeze
, "VM freezer test", T_META_ASROOT(true)) {
488 (MEM_SIZE_MB
<< 20) / get_vmpage_size());
491 static int old_freeze_pages_max
= 0;
493 reset_freeze_pages_max()
495 if (old_freeze_pages_max
!= 0) {
496 sysctl_freeze_pages_max(&old_freeze_pages_max
);
501 sysctl_freeze_pages_max(int* new_value
)
503 static int set_end_handler
= false;
504 int freeze_pages_max
, ret
;
505 size_t size
= sizeof(freeze_pages_max
);
506 ret
= sysctlbyname("kern.memorystatus_freeze_pages_max", &freeze_pages_max
, &size
, new_value
, size
);
507 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "Unable to query kern.memorystatus_freeze_pages_max");
508 if (!set_end_handler
) {
509 // Save the original value and instruct darwintest to restore it after the test completes
510 old_freeze_pages_max
= freeze_pages_max
;
511 T_ATEND(reset_freeze_pages_max
);
512 set_end_handler
= true;
514 return old_freeze_pages_max
;
517 T_DECL(freeze_over_max_threshold
, "Max Freeze Threshold is Enforced", T_META_ASROOT(true)) {
518 int freeze_pages_max
= FREEZE_PAGES_MAX
;
519 sysctl_freeze_pages_max(&freeze_pages_max
);
520 run_freezer_test(FREEZE_PAGES_MAX
* 2);
523 T_HELPER_DECL(frozen_background
, "Frozen background process", T_META_ASROOT(true)) {
524 kern_return_t kern_ret
;
525 /* Set the process to freezable */
526 kern_ret
= memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE
, getpid(), 1, NULL
, 0);
527 T_QUIET
; T_ASSERT_EQ(kern_ret
, KERN_SUCCESS
, "set process is freezable");
528 /* Signal to our parent that we can be frozen */
529 if (kill(getppid(), SIGUSR1
) != 0) {
530 T_LOG("Unable to signal to parent process!");
538 /* Launches the frozen_background helper as a managed process. */
540 launch_frozen_background_process()
543 char **launch_tool_args
;
544 char testpath
[PATH_MAX
];
545 uint32_t testpath_buf_size
;
548 testpath_buf_size
= sizeof(testpath
);
549 ret
= _NSGetExecutablePath(testpath
, &testpath_buf_size
);
550 printf("Launching %s\n", testpath
);
551 launch_tool_args
= (char *[]){
557 ret
= dt_launch_tool(&pid
, launch_tool_args
, false, NULL
, NULL
);
559 T_LOG("dt_launch tool returned %d with error code %d", ret
, errno
);
561 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "dt_launch_tool");
562 /* Set the process's managed bit, so that the kernel treats this process like an app instead of a sysproc. */
563 ret
= memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED
, pid
, 1, NULL
, 0);
564 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "memorystatus_control");
569 freeze_process(pid_t pid
)
571 int ret
, freeze_enabled
, errno_freeze_sysctl
;
573 T_LOG("Freezing pid %d", pid
);
575 ret
= sysctlbyname("kern.memorystatus_freeze", NULL
, NULL
, &pid
, sizeof(pid
));
576 errno_freeze_sysctl
= errno
;
577 length
= sizeof(freeze_enabled
);
578 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled
, &length
, NULL
, 0),
579 "failed to query vm.freeze_enabled");
580 if (freeze_enabled
) {
581 errno
= errno_freeze_sysctl
;
582 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_freeze failed");
584 /* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
585 T_LOG("Freeze has been disabled. Terminating early.");
591 memorystatus_assertion_test_demote_frozen()
594 T_SKIP("Freezing processes is only supported on embedded");
597 * Test that if we assert a priority on a process, freeze it, and then demote all frozen processes, it does not get demoted below the asserted priority.
598 * Then remove thee assertion, and ensure it gets demoted properly.
600 /* these values will remain fixed during testing */
601 int active_limit_mb
= 15; /* arbitrary */
602 int inactive_limit_mb
= 7; /* arbitrary */
603 int demote_value
= 1;
604 /* Launch the child process, and elevate its priority */
605 int requestedpriority
;
606 dispatch_source_t ds_signal
, ds_exit
;
607 requestedpriority
= JETSAM_PRIORITY_UI_SUPPORT
;
609 /* Wait for the child process to tell us that it's ready, and then freeze it */
610 signal(SIGUSR1
, SIG_IGN
);
611 ds_signal
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, SIGUSR1
, 0, dispatch_get_main_queue());
612 T_QUIET
; T_ASSERT_NOTNULL(ds_signal
, "dispatch_source_create");
613 dispatch_source_set_event_handler(ds_signal
, ^{
615 /* Freeze the process, trigger agressive demotion, and check that it hasn't been demoted. */
616 freeze_process(child_pid
);
617 /* Agressive demotion */
618 sysctl_ret
= sysctlbyname("kern.memorystatus_demote_frozen_processes", NULL
, NULL
, &demote_value
, sizeof(demote_value
));
619 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctl_ret
, "sysctl kern.memorystatus_demote_frozen_processes succeeded");
621 (void)check_properties(child_pid
, requestedpriority
, inactive_limit_mb
, 0x0, ASSERTION_STATE_IS_SET
, "Priority was set");
622 T_LOG("Relinquishing our assertion.");
623 /* Relinquish our assertion, and check that it gets demoted. */
624 relinquish_assertion_priority(child_pid
, 0x0);
625 (void)check_properties(child_pid
, JETSAM_PRIORITY_AGING_BAND2
, inactive_limit_mb
, 0x0, ASSERTION_STATE_IS_RELINQUISHED
, "Assertion was reqlinquished.");
627 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kill(child_pid
, SIGKILL
), "Killed child process");
631 /* Launch the child process and set the initial properties on it. */
632 child_pid
= launch_frozen_background_process();
633 set_memlimits(child_pid
, active_limit_mb
, inactive_limit_mb
, false, false);
634 set_assertion_priority(child_pid
, requestedpriority
, 0x0);
635 (void)check_properties(child_pid
, requestedpriority
, inactive_limit_mb
, 0x0, ASSERTION_STATE_IS_SET
, "Priority was set");
636 /* Listen for exit. */
637 ds_exit
= dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t)child_pid
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue());
638 dispatch_source_set_event_handler(ds_exit
, ^{
639 int status
= 0, code
= 0;
640 pid_t rc
= waitpid(child_pid
, &status
, 0);
641 T_QUIET
; T_ASSERT_EQ(rc
, child_pid
, "waitpid");
642 code
= WEXITSTATUS(status
);
643 T_QUIET
; T_ASSERT_EQ(code
, 0, "Child exited cleanly");
647 dispatch_activate(ds_exit
);
648 dispatch_activate(ds_signal
);
652 T_DECL(assertion_test_demote_frozen
, "demoted frozen process goes to asserted priority.", T_META_ASROOT(true)) {
653 memorystatus_assertion_test_demote_frozen();
656 T_DECL(budget_replenishment
, "budget replenishes properly") {
659 static unsigned int kTestIntervalSecs
= 60 * 60 * 32; // 32 Hours
660 unsigned int memorystatus_freeze_daily_mb_max
, memorystatus_freeze_daily_pages_max
;
661 static unsigned int kFixedPointFactor
= 100;
662 static unsigned int kNumSecondsInDay
= 60 * 60 * 24;
663 unsigned int new_budget
, expected_new_budget_pages
;
664 size_t new_budget_ln
;
665 unsigned int page_size
= (unsigned int) get_vmpage_size();
668 * Calculate a new budget as if the previous interval expired kTestIntervalSecs
669 * ago and we used up its entire budget.
671 length
= sizeof(kTestIntervalSecs
);
672 new_budget_ln
= sizeof(new_budget
);
673 ret
= sysctlbyname("vm.memorystatus_freeze_calculate_new_budget", &new_budget
, &new_budget_ln
, &kTestIntervalSecs
, length
);
674 T_ASSERT_POSIX_SUCCESS(ret
, "vm.memorystatus_freeze_calculate_new_budget");
676 // Grab the daily budget.
677 length
= sizeof(memorystatus_freeze_daily_mb_max
);
678 ret
= sysctlbyname("kern.memorystatus_freeze_daily_mb_max", &memorystatus_freeze_daily_mb_max
, &length
, NULL
, 0);
679 T_ASSERT_POSIX_SUCCESS(ret
, "kern.memorystatus_freeze_daily_mb_max");
681 memorystatus_freeze_daily_pages_max
= memorystatus_freeze_daily_mb_max
* 1024UL * 1024UL / page_size
;
682 T_LOG("memorystatus_freeze_daily_mb_max %u", memorystatus_freeze_daily_mb_max
);
683 T_LOG("memorystatus_freeze_daily_pages_max %u", memorystatus_freeze_daily_pages_max
);
684 T_LOG("page_size %u", page_size
);
687 * We're kTestIntervalSecs past a new interval. Which means we are owed kNumSecondsInDay
690 expected_new_budget_pages
= memorystatus_freeze_daily_pages_max
;
691 T_LOG("expected_new_budget_pages before %u", expected_new_budget_pages
);
692 T_ASSERT_EQ(kTestIntervalSecs
, 60 * 60 * 32, "kTestIntervalSecs did not change");
693 expected_new_budget_pages
+= ((kTestIntervalSecs
* kFixedPointFactor
) / (kNumSecondsInDay
)
694 * memorystatus_freeze_daily_pages_max
) / kFixedPointFactor
;
695 T_LOG("expected_new_budget_pages after %u", expected_new_budget_pages
);
696 T_LOG("memorystatus_freeze_daily_pages_max after %u", memorystatus_freeze_daily_pages_max
);
698 T_QUIET
; T_ASSERT_EQ(new_budget
, expected_new_budget_pages
, "Calculate new budget behaves correctly.");