1 #include <AvailabilityMacros.h>
2 #include <mach/thread_policy.h>
4 #include <mach/mach_traps.h>
5 #include <mach/mach_error.h>
6 #include <mach/mach_time.h>
12 #include <sys/errno.h>
13 #include <sys/kern_memorystatus.h>
15 #define MAXTESTPIDS 15
16 #define MAXPRIORITY JETSAM_PRIORITY_MAX - 1
19 * <rdar://problem/15976217> memorystatus_control support for
20 * reprioritizing multiple processes
22 * This test/tool operates in one of two modes.
23 * List mode or Generate mode.
25 * In generate mode (the default)
27 * Spin off some number of child processes. (Enforce a max)
28 * Generate a random jetsam priority band for each process.
29 * Kill at least one of the processes (this tests the kernel's
30 * ability to ignore non-existant pid.)
31 * Sprinkle the processes into their randomly assigned band.
33 * Query the kernel for a snapshot of the jetsam priority list,
34 * (saving the priority and the index into the overall
35 * priority list for each pid)
37 * Exercise the MEMORYSTATUS_CMD_GRP_SET_PROPERTIES control call.
39 * Properties supported in this exercise?
42 * Query the kernel again for a second snapshot.
45 * If everything works as expected, all the pids have moved
46 * to the new priority band and relative order before the
47 * move is the same order after the move.
49 * In list mode, the user passes in a list of pids from the command line.
50 * We skip the Setup phase, but follow through with the Test and Verify
53 * When using generate mode, you can add a delay that takes place just
54 * before the control call and then again just after the control call.
55 * eg: This allows time to manaully introspect the state of
56 * the device before and after the new property assignments.
60 int g_exit_status
= 0;
61 boolean_t generate_flag
= FALSE
;
62 boolean_t list_flag
= FALSE
;
63 boolean_t verbose_flag
= FALSE
;
64 boolean_t do_error_flag
= FALSE
;
65 uint64_t delay_seconds
= 0;
66 uint32_t kill_pid_indx
= 0;
67 uint32_t g_new_priority
= JETSAM_PRIORITY_IDLE
;
69 typedef struct pidinfo
{
71 int32_t pri_random
; /* random priority for generate path */
72 int32_t pri_before
; /* priority before idle move */
73 int32_t indx_before
; /* jetsam bucket index before idle move */
74 int32_t pri_after
; /* priority found after idle move test */
75 int32_t exp_after
; /* Expect priority. Zero if moved to idle band */
76 int32_t indx_after
; /* order it landed in the idle band */
79 static boolean_t
do_get_priority_list (boolean_t before
, memorystatus_priority_entry_t
*mypids
, size_t pid_count
, pidinfo_t
*pidinfo
);
80 static void do_generate_test();
81 static void do_child_labor();
82 static int priority_cmp(const void *x
, const void *y
);
83 static void do_pidlist_test(memorystatus_priority_entry_t
*list
, uint32_t pid_count
);
84 static void do_control_list_test(memorystatus_priority_entry_t
*list
, uint32_t pid_count
);
85 static void dump_info_table(pidinfo_t
*info
, uint32_t count
);
86 static void print_usage();
88 static char *g_testname
= "GrpSetProperties";
91 printTestHeader(pid_t testPid
, const char *testName
, ...)
94 printf("=============================================\n");
95 printf("[TEST] GrpSetProperty ");
96 va_start(va
, testName
);
97 vprintf(testName
, va
);
100 printf("[PID] %d\n", testPid
);
101 printf("=============================================\n");
106 printTestResult(const char *testName
, boolean_t didPass
, const char *msg
, ...)
117 printf("[PASS] GrpSetProperty\t%s\n\n", testName
);
119 printf("[FAIL] GrpSetProperty\t%s\n\n", testName
);
121 /* Any single failure, fails full test run */
129 boolean_t passflag
= TRUE
;
132 memorystatus_priority_entry_t list
[MAXTESTPIDS
];
134 listsize
= (sizeof(memorystatus_priority_entry_t
) * MAXTESTPIDS
);
135 memset (list
, 0, listsize
);
137 list
[0].pid
= getpid();
138 list
[0].priority
= JETSAM_PRIORITY_MAX
+10; /* out of range priority */
140 printTestHeader (getpid(), "NULL pointer test");
142 error
= memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES
, 0, 0, NULL
, listsize
);
143 printf("\t Expect: error (-1), errno (%d)\n", EINVAL
);
144 printf("\t Actual: error (%d), errno (%d)\n", error
, errno
);
145 if (error
== -1 && errno
== EINVAL
)
149 printTestResult("NULL pointer test", passflag
, NULL
);
152 printTestHeader (getpid(), "zero size test");
154 error
= memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES
, 0, 0, &list
, 0);
155 printf("\t Expect: error (-1), errno (%d)\n", EINVAL
);
156 printf("\t Actual: error (%d), errno (%d)\n", error
, errno
);
157 if (error
== -1 && errno
== EINVAL
)
161 printTestResult("zero size test", passflag
, NULL
);
164 printTestHeader (getpid(), "bad size test");
166 error
= memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES
, 0, 0, &list
, (listsize
-1));
167 printf("\t Expect: error (-1), errno (%d)\n", EINVAL
);
168 printf("\t Actual: error (%d), errno (%d)\n", error
, errno
);
169 if (error
== -1 && errno
== EINVAL
)
173 printTestResult("bad size test", passflag
, NULL
);
175 printTestHeader (getpid(), "bad priority test");
177 error
= memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES
, 0, 0, &list
, (listsize
));
178 printf("\t Expect: error (-1), errno (%d)\n", EINVAL
);
179 printf("\t Actual: error (%d), errno (%d)\n", error
, errno
);
180 if (error
== -1 && errno
== EINVAL
)
184 printTestResult("bad priority test", passflag
, NULL
);
188 main(int argc
, char *argv
[])
192 memorystatus_priority_entry_t list
[MAXTESTPIDS
];
193 uint32_t pid_count
= MAXTESTPIDS
; /* default */
198 if (geteuid() != 0) {
199 printf("\tMust be run as root\n");
203 listsize
= sizeof(memorystatus_priority_entry_t
) * MAXTESTPIDS
;
204 memset (list
, 0, listsize
);
206 while ((c
= getopt (argc
, argv
, "p:ed:hvg:l")) != -1) {
209 g_new_priority
= strtol(optarg
, NULL
, 10);
212 do_error_flag
= TRUE
;
218 delay_seconds
= strtol(optarg
, NULL
, 10);
221 /* means a list of pids follow */
225 /* dynamicall generate 'n' processes */
226 generate_flag
= TRUE
;
227 pid_count
= strtol(optarg
, NULL
, 10);
244 * This core part of this test has two modes only.
245 * Default is to dynamically generate a list of pids to work on.
246 * Else use the -l flag and pass in a list of pids.
248 if (generate_flag
&& list_flag
) {
249 printTestResult(g_testname
, FALSE
, "Can't use both -g and -l options\n");
254 if (pid_count
<= 0 || pid_count
> MAXTESTPIDS
) {
255 printTestResult(g_testname
, FALSE
,
256 "Pid count out of range (actual: %d), (max: %d)\n", pid_count
, MAXTESTPIDS
);
259 } else if (list_flag
) {
261 for (; *argv
; ++argv
) {
262 if (pid_count
< MAXTESTPIDS
){
263 list
[pid_count
].pid
= strtol(*argv
, NULL
, 10);
264 list
[pid_count
].priority
= g_new_priority
;
269 printTestResult(g_testname
, FALSE
,
270 "Too many pids (actual: %d), (max: %d)\n", pid_count
, MAXTESTPIDS
);
275 if (pid_count
<= 0 ) {
276 printTestResult(g_testname
, FALSE
,
277 "Provide at least one pid (actual: %d),(max: %d)\n", pid_count
, MAXTESTPIDS
);
282 do_error_flag
= TRUE
;
283 generate_flag
= TRUE
;
284 pid_count
= MAXTESTPIDS
;
292 do_generate_test(list
, pid_count
);
296 do_pidlist_test (list
, pid_count
);
299 return(g_exit_status
);
305 do_pidlist_test(memorystatus_priority_entry_t
*list
, uint32_t pid_count
)
308 do_control_list_test(list
, pid_count
);
312 do_control_list_test(memorystatus_priority_entry_t
*list
, uint32_t pid_count
)
317 pidinfo_t info
[MAXTESTPIDS
];
319 printTestHeader (getpid(), "new priority test");
320 memset (info
, 0, MAXTESTPIDS
* sizeof(pidinfo_t
));
321 printf ("\tInput: pid_count = %d\n", pid_count
);
322 printf ("\tInput: new_priority = %d\n", g_new_priority
);
325 printf("\tIntentionally killed pid [%d]\n", list
[kill_pid_indx
].pid
);
327 /* random value initialization */
328 srandom((u_long
)time(NULL
));
330 /* In generate path, we sprinkle pids into random priority buckets */
332 /* initialize info structures and properties */
333 for (i
= 0; i
< pid_count
; i
++) {
334 info
[i
].pid
= list
[i
].pid
;
335 info
[i
].pri_random
= random() % MAXPRIORITY
; /* generate path only */
336 info
[i
].pri_before
= -1;
337 info
[i
].indx_before
= -1;
338 info
[i
].pri_after
= -1;
339 info
[i
].exp_after
= g_new_priority
;
340 info
[i
].indx_after
= -1;
343 /* Initialize properties for generated pids */
344 memorystatus_priority_properties_t mp
;
345 mp
.priority
= info
[i
].pri_random
;
347 if(memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES
, list
[i
].pid
, 0, &mp
, sizeof(mp
)) == -1) {
349 * If we cannot set the properties on a given
350 * pid (for whatever reason), we'll ignore it.
351 * But set expectations for verification phase.
353 printf("\tWarning: set properties failed on pid [%d] (%s)\n", list
[i
].pid
, strerror(errno
));
354 info
[i
].exp_after
= -1;
360 /* Get the system's current jetsam priority list, init pass */
361 if (do_get_priority_list(TRUE
, list
, pid_count
, info
) == FALSE
) {
366 if (delay_seconds
> 0) {
367 printf("\tDelay [%llu] seconds... (before move to new band)\n", delay_seconds
);
368 sleep(delay_seconds
);
372 error
= memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES
, 0, 0,
373 list
, (pid_count
* sizeof(memorystatus_priority_entry_t
)));
375 printf("\tMEMORYSTATUS_CMD_GRP_SET_PROPERTIES failed (%s)\n", strerror(errno
));
379 /* Get the system's jetsam priority list, after move to new band */
380 if (do_get_priority_list(FALSE
, list
, pid_count
, info
) == FALSE
) {
385 if (delay_seconds
> 0) {
386 printf("\tDelay [%llu] seconds... (after move to new band)\n", delay_seconds
);
387 sleep(delay_seconds
);
391 qsort ((void *)info
, pid_count
, sizeof(pidinfo_t
),priority_cmp
);
394 * Verify that the list of pids have been placed in new priority band
395 * and that they are in the same relative priority order.
396 * The relative bucket placement before moving to the new priority
397 * band should be the same as that after moving to the new
401 for (i
=0; i
< pid_count
; i
++) {
402 if (info
[i
].pri_before
== -1){
403 /* skip... this pid did not exist */
407 /* The new priority band must meet expectations */
408 if (info
[i
].pri_after
!= info
[i
].exp_after
) {
412 if (i
+1 == pid_count
)
413 break; /* Done traversing list */
415 if (info
[i
].pid
== info
[i
+1].pid
) {
416 /* skip duplicate pids */
420 if (info
[i
].indx_before
< info
[i
+1].indx_before
&&
421 info
[i
].indx_after
< info
[i
+1].indx_after
&&
422 info
[i
].pri_before
<= info
[i
+1].pri_before
&&
423 info
[i
].pri_after
<= info
[i
+1].pri_after
) {
431 printf("\tFound [%d] verification errors.\n", error
);
433 if (error
|| errno
|| verbose_flag
==TRUE
) {
434 dump_info_table(info
, pid_count
);
438 printf("\n\tExpect: error (0), errno (0)\n");
439 printf("\tActual: error (%d), errno (%d)\n", error
, errno
);
440 if (error
!= 0 || errno
!= 0)
444 printTestResult(g_testname
, passflag
, NULL
);
448 * The concept of jetsam priority order can actually be viewed as
449 * the relative index of an item in a bucket from from lowest
450 * priority bucket to highest priority bucket and then from
451 * head bucket entry to tail bucket entry.
452 * In reality, we have a linear, ordered list at any point
458 priority_cmp(const void *x
, const void *y
)
460 pidinfo_t entry_x
= *((pidinfo_t
*)x
);
461 pidinfo_t entry_y
= *((pidinfo_t
*)y
);
463 if (entry_x
.pri_before
< entry_y
.pri_before
)
465 if (entry_x
.pri_before
== entry_y
.pri_before
) {
467 * Second level ordering.
469 if (entry_x
.indx_before
< entry_y
.indx_before
)
471 if (entry_x
.indx_before
== entry_y
.indx_before
)
472 return 0; /* never */
480 do_get_priority_list (boolean_t before
, memorystatus_priority_entry_t
*mypids
, size_t pid_count
, pidinfo_t
*pidinfo
)
482 #pragma unused (mypids)
485 memorystatus_priority_entry_t
*list
;
486 size_t list_count
= 0;
490 size
= memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST
, 0, 0, NULL
, 0);
492 printf("\tCan't get jetsam priority list size: %s\n", strerror(errno
));
496 list
= (memorystatus_priority_entry_t
*)malloc(size
);
498 size
= memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST
, 0, 0, list
, size
);
500 printf("\tCould not get jetsam priority list: %s\n", strerror(errno
));
505 /* recompute number of entries in the list and find the pid's priority*/
506 list_count
= size
/ sizeof(memorystatus_priority_entry_t
);
508 printf("\tFound [%d] jetsam bucket entries (%s move to new band).\n",
509 (int)list_count
, before
? "before" : " after");
511 for (i
=0; i
< pid_count
; i
++) {
512 for (j
=0; j
< list_count
; j
++) {
513 if (list
[j
].pid
== pidinfo
[i
].pid
) {
516 * Save process's priority and relative index
517 * before moving to new priority
519 pidinfo
[i
].pri_before
= list
[j
].priority
;
520 pidinfo
[i
].indx_before
= j
;
523 * Save process's priority and relative index
524 * after moving to new priority
526 pidinfo
[i
].pri_after
= list
[j
].priority
;
527 pidinfo
[i
].indx_after
= j
;
543 void do_generate_test (memorystatus_priority_entry_t
*list
, uint32_t pid_count
)
545 int launch_errors
= 0;
547 memorystatus_priority_properties_t mp
;
549 /* Generate mode Setup phase */
554 for (i
=0; i
< pid_count
; i
++) {
555 list
[i
].pid
= fork();
556 list
[i
].priority
= g_new_priority
; /*XXX introduce multiple
558 switch (list
[i
].pid
) {
572 * Parent will set the priority of the
576 if (verbose_flag
&& launch_errors
> 0)
577 printf("\tParent launch errors = %d\n", launch_errors
);
579 /* Introduce a case where pid is not found */
580 kill_pid_indx
= pid_count
/2 ;
581 kill(list
[kill_pid_indx
].pid
, SIGKILL
);
584 do_control_list_test (list
, pid_count
);
586 for (i
=0; i
< pid_count
; i
++) {
587 if (i
!= kill_pid_indx
) {
588 kill(list
[i
].pid
, SIGKILL
);
598 * Ideally, the process should be suspended,
599 * but letting it spin doing random
600 * stuff should be harmless for this test.
603 printf("\tLaunched child pid [%d]\n", getpid());
612 dump_info_table(pidinfo_t
*info
, uint32_t count
)
617 * The random priority value is only of interest in the
618 * generate_flag path, and even then, it's not really
619 * that interesting! So, not dumped here.
620 * But it is evident in the Jetsam Priority 'before' column.
623 printf("\n%10s \t%s \t\t%20s\n", "Pid", "Jetsam Priority", "Relative Bucket Index");
624 printf("%10s \t%s %20s\n", "", "(before | after | expected)", "(before | after)");
626 for (i
=0; i
< count
; i
++) {
627 printf("%10d", info
[i
].pid
);
628 printf("\t(%4d |", info
[i
].pri_before
);
629 printf("%4d |", info
[i
].pri_after
);
630 printf("%4d)", info
[i
].exp_after
);
631 printf("\t\t(%5d |", info
[i
].indx_before
);
632 printf("%5d)\n", info
[i
].indx_after
);
639 printf("\nUsage:\n");
640 printf("[-e] [-p] [-v] [-d <seconds>][ -g <count> | -l <list of pids>]\n\n");
641 printf("Exercise the MEMORYSTATUS_CMD_GRP_SET_PROPERTIES command.\n");
642 printf("Operates on at most %d pids.\n", MAXTESTPIDS
);
643 printf("Pass in a list of pids or allow the test to generate the pids dynamically.\n\n");
645 printf("\t -e : exercise error tests\n");
646 printf("\t -p <priority> : Override default priority band.\n");
647 printf("\t -v : extra verbosity\n");
648 printf("\t -d <seconds> : delay before and after idle move (default = 0)\n");
649 printf("\t -g <count> : dynamically generate <count> processes.\n");
650 printf("\t -l <list of pids> : operate on the given list of pids\n\n");
651 printf("\t default : generate %d pids, no delay, priority %d eg: -g %d -p %d\n\n",
652 MAXTESTPIDS
, g_new_priority
, MAXTESTPIDS
, g_new_priority
);