memorystatus_groups.c [plain text]
#include <AvailabilityMacros.h>
#include <mach/thread_policy.h>
#include <mach/mach.h>
#include <mach/mach_traps.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/kern_memorystatus.h>
#define MAXTESTPIDS 15
#define MAXPRIORITY JETSAM_PRIORITY_MAX - 1
int g_exit_status = 0;
boolean_t generate_flag = FALSE;
boolean_t list_flag = FALSE;
boolean_t verbose_flag = FALSE;
boolean_t do_error_flag = FALSE;
uint64_t delay_seconds = 0;
uint32_t kill_pid_indx = 0;
uint32_t g_new_priority = JETSAM_PRIORITY_IDLE;
typedef struct pidinfo {
pid_t pid;
int32_t pri_random;
int32_t pri_before;
int32_t indx_before;
int32_t pri_after;
int32_t exp_after;
int32_t indx_after;
} pidinfo_t;
static boolean_t do_get_priority_list (boolean_t before, memorystatus_priority_entry_t *mypids, size_t pid_count, pidinfo_t *pidinfo);
static void do_generate_test();
static void do_child_labor();
static int priority_cmp(const void *x, const void *y);
static void do_pidlist_test(memorystatus_priority_entry_t *list, uint32_t pid_count);
static void do_control_list_test(memorystatus_priority_entry_t *list, uint32_t pid_count);
static void dump_info_table(pidinfo_t *info, uint32_t count);
static void print_usage();
static char *g_testname = "GrpSetProperties";
static void
printTestHeader(pid_t testPid, const char *testName, ...)
{
va_list va;
printf("=============================================\n");
printf("[TEST] GrpSetProperty ");
va_start(va, testName);
vprintf(testName, va);
va_end(va);
printf("\n");
printf("[PID] %d\n", testPid);
printf("=============================================\n");
printf("[BEGIN]\n");
}
static void
printTestResult(const char *testName, boolean_t didPass, const char *msg, ...)
{
if (msg != NULL) {
va_list va;
printf("\t\t");
va_start(va, msg);
vprintf(msg, va);
va_end(va);
printf("\n");
}
if (didPass) {
printf("[PASS] GrpSetProperty\t%s\n\n", testName);
} else {
printf("[FAIL] GrpSetProperty\t%s\n\n", testName);
g_exit_status = -1;
}
}
static void
do_error_test ()
{
boolean_t passflag = TRUE;
int error;
size_t listsize = 0;
memorystatus_priority_entry_t list[MAXTESTPIDS];
listsize = (sizeof(memorystatus_priority_entry_t) * MAXTESTPIDS);
memset (list, 0, listsize);
list[0].pid = getpid();
list[0].priority = JETSAM_PRIORITY_MAX+10;
printTestHeader (getpid(), "NULL pointer test");
errno=0;
error = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, 0, NULL, listsize);
printf("\t Expect: error (-1), errno (%d)\n", EINVAL);
printf("\t Actual: error (%d), errno (%d)\n", error, errno);
if (error == -1 && errno == EINVAL)
passflag = TRUE;
else
passflag = FALSE;
printTestResult("NULL pointer test", passflag, NULL);
printTestHeader (getpid(), "zero size test");
errno=0;
error = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, 0, &list, 0);
printf("\t Expect: error (-1), errno (%d)\n", EINVAL);
printf("\t Actual: error (%d), errno (%d)\n", error, errno);
if (error == -1 && errno == EINVAL)
passflag = TRUE;
else
passflag = FALSE;
printTestResult("zero size test", passflag, NULL);
printTestHeader (getpid(), "bad size test");
errno=0;
error = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, 0, &list, (listsize-1));
printf("\t Expect: error (-1), errno (%d)\n", EINVAL);
printf("\t Actual: error (%d), errno (%d)\n", error, errno);
if (error == -1 && errno == EINVAL)
passflag = TRUE;
else
passflag = FALSE;
printTestResult("bad size test", passflag, NULL);
printTestHeader (getpid(), "bad priority test");
errno=0;
error = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, 0, &list, (listsize));
printf("\t Expect: error (-1), errno (%d)\n", EINVAL);
printf("\t Actual: error (%d), errno (%d)\n", error, errno);
if (error == -1 && errno == EINVAL)
passflag = TRUE;
else
passflag = FALSE;
printTestResult("bad priority test", passflag, NULL);
}
int
main(int argc, char *argv[])
{
kern_return_t error;
memorystatus_priority_entry_t list[MAXTESTPIDS];
uint32_t pid_count = MAXTESTPIDS;
size_t listsize = 0;
int c;
int i = 0;
if (geteuid() != 0) {
printf("\tMust be run as root\n");
exit(1);
}
listsize = sizeof(memorystatus_priority_entry_t) * MAXTESTPIDS;
memset (list, 0, listsize);
while ((c = getopt (argc, argv, "p:ed:hvg:l")) != -1) {
switch (c) {
case 'p':
g_new_priority = strtol(optarg, NULL, 10);
break;
case 'e':
do_error_flag = TRUE;
break;
case 'v':
verbose_flag = TRUE;
break;
case 'd':
delay_seconds = strtol(optarg, NULL, 10);
break;
case 'l':
list_flag = TRUE;
break;
case 'g':
generate_flag = TRUE;
pid_count = strtol(optarg, NULL, 10);
break;
case 'h':
print_usage();
exit(0);
case '?':
default:
print_usage();
exit(-1);
}
}
argc -= optind;
argv += optind;
errno = 0;
if (generate_flag && list_flag) {
printTestResult(g_testname, FALSE, "Can't use both -g and -l options\n");
exit(g_exit_status);
}
if (generate_flag) {
if (pid_count <= 0 || pid_count > MAXTESTPIDS) {
printTestResult(g_testname, FALSE,
"Pid count out of range (actual: %d), (max: %d)\n", pid_count, MAXTESTPIDS);
exit(g_exit_status);
}
} else if (list_flag) {
pid_count=0;
for (; *argv; ++argv) {
if (pid_count < MAXTESTPIDS){
list[pid_count].pid = strtol(*argv, NULL, 10);
list[pid_count].priority = g_new_priority;
pid_count++;
argc--;
optind++;
} else {
printTestResult(g_testname, FALSE,
"Too many pids (actual: %d), (max: %d)\n", pid_count, MAXTESTPIDS);
exit(g_exit_status);
break;
}
}
if (pid_count <= 0 ) {
printTestResult(g_testname, FALSE,
"Provide at least one pid (actual: %d),(max: %d)\n", pid_count, MAXTESTPIDS);
exit(g_exit_status);
}
} else {
do_error_flag = TRUE;
generate_flag = TRUE;
pid_count = MAXTESTPIDS;
}
if (do_error_flag) {
do_error_test();
}
if (generate_flag) {
do_generate_test(list, pid_count);
}
if (list_flag) {
do_pidlist_test (list, pid_count);
}
return(g_exit_status);
}
static void
do_pidlist_test(memorystatus_priority_entry_t *list, uint32_t pid_count)
{
do_control_list_test(list, pid_count);
}
static void
do_control_list_test(memorystatus_priority_entry_t *list, uint32_t pid_count)
{
int error = 0;
int i;
boolean_t passflag;
pidinfo_t info[MAXTESTPIDS];
printTestHeader (getpid(), "new priority test");
memset (info, 0, MAXTESTPIDS * sizeof(pidinfo_t));
printf ("\tInput: pid_count = %d\n", pid_count);
printf ("\tInput: new_priority = %d\n", g_new_priority);
if (generate_flag)
printf("\tIntentionally killed pid [%d]\n", list[kill_pid_indx].pid);
srandom((u_long)time(NULL));
for (i = 0; i < pid_count; i++) {
info[i].pid = list[i].pid;
info[i].pri_random = random() % MAXPRIORITY;
info[i].pri_before = -1;
info[i].indx_before = -1;
info[i].pri_after = -1;
info[i].exp_after = g_new_priority;
info[i].indx_after = -1;
if (generate_flag) {
memorystatus_priority_properties_t mp;
mp.priority = info[i].pri_random;
mp.user_data = 0;
if(memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, list[i].pid, 0, &mp, sizeof(mp)) == -1) {
printf("\tWarning: set properties failed on pid [%d] (%s)\n", list[i].pid, strerror(errno));
info[i].exp_after = -1;
errno = 0;
}
}
}
if (do_get_priority_list(TRUE, list, pid_count, info) == FALSE) {
error = 1;
goto out;
}
if (delay_seconds > 0) {
printf("\tDelay [%llu] seconds... (before move to new band)\n", delay_seconds);
sleep(delay_seconds);
errno = 0;
}
error = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, 0,
list, (pid_count * sizeof(memorystatus_priority_entry_t)));
if (error) {
printf("\tMEMORYSTATUS_CMD_GRP_SET_PROPERTIES failed (%s)\n", strerror(errno));
goto out;
}
if (do_get_priority_list(FALSE, list, pid_count, info) == FALSE) {
error = 1;
goto out;
}
if (delay_seconds > 0) {
printf("\tDelay [%llu] seconds... (after move to new band)\n", delay_seconds);
sleep(delay_seconds);
errno = 0;
}
qsort ((void *)info, pid_count, sizeof(pidinfo_t),priority_cmp);
error = 0;
for (i=0; i < pid_count; i++) {
if (info[i].pri_before == -1){
continue;
}
if (info[i].pri_after != info[i].exp_after) {
error++;
}
if (i+1 == pid_count)
break;
if (info[i].pid == info[i+1].pid) {
continue;
}
if (info[i].indx_before < info[i+1].indx_before &&
info[i].indx_after < info[i+1].indx_after &&
info[i].pri_before <= info[i+1].pri_before &&
info[i].pri_after <= info[i+1].pri_after ) {
}
else {
error++;
}
}
printf("\tFound [%d] verification errors.\n", error);
if (error || errno || verbose_flag==TRUE) {
dump_info_table(info, pid_count);
}
out:
printf("\n\tExpect: error (0), errno (0)\n");
printf("\tActual: error (%d), errno (%d)\n", error, errno);
if (error != 0 || errno != 0)
passflag = FALSE;
else
passflag = TRUE;
printTestResult(g_testname, passflag, NULL);
}
static int
priority_cmp(const void *x, const void *y)
{
pidinfo_t entry_x = *((pidinfo_t *)x);
pidinfo_t entry_y = *((pidinfo_t *)y);
if (entry_x.pri_before < entry_y.pri_before)
return -1;
if (entry_x.pri_before == entry_y.pri_before) {
if (entry_x.indx_before < entry_y.indx_before)
return -1;
if (entry_x.indx_before == entry_y.indx_before)
return 0;
return 1;
}
return 1;
}
static boolean_t
do_get_priority_list (boolean_t before, memorystatus_priority_entry_t *mypids, size_t pid_count, pidinfo_t *pidinfo)
{
#pragma unused (mypids)
size_t size = 0;
memorystatus_priority_entry_t *list;
size_t list_count = 0;
int found = 0;
int i, j;
size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, NULL, 0);
if (size <= 0 ) {
printf("\tCan't get jetsam priority list size: %s\n", strerror(errno));
return(FALSE);
}
list = (memorystatus_priority_entry_t *)malloc(size);
size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, list, size);
if (size <= 0) {
printf("\tCould not get jetsam priority list: %s\n", strerror(errno));
free(list);
return(FALSE);
}
list_count = size / sizeof(memorystatus_priority_entry_t);
printf("\tFound [%d] jetsam bucket entries (%s move to new band).\n",
(int)list_count, before? "before" : " after");
for (i=0; i < pid_count; i++) {
for (j=0; j < list_count; j++) {
if (list[j].pid == pidinfo[i].pid) {
if (before) {
pidinfo[i].pri_before = list[j].priority;
pidinfo[i].indx_before = j;
}else {
pidinfo[i].pri_after = list[j].priority;
pidinfo[i].indx_after = j;
}
break;
}
}
}
if (list)
free(list);
return(TRUE);
}
static
void do_generate_test (memorystatus_priority_entry_t *list, uint32_t pid_count)
{
int launch_errors = 0;
int i;
memorystatus_priority_properties_t mp;
if (pid_count <= 0)
return;
for (i=0; i < pid_count; i++) {
list[i].pid = fork();
list[i].priority = g_new_priority;
switch (list[i].pid) {
case 0:
do_child_labor();
exit(0);
break;
case -1:
launch_errors++;
break;
default:
continue;
}
}
if (verbose_flag && launch_errors > 0)
printf("\tParent launch errors = %d\n", launch_errors);
kill_pid_indx = pid_count/2 ;
kill(list[kill_pid_indx].pid, SIGKILL);
sleep(5);
do_control_list_test (list, pid_count);
for (i=0; i < pid_count; i++) {
if (i != kill_pid_indx) {
kill(list[i].pid, SIGKILL );
}
}
}
static void
do_child_labor()
{
if (verbose_flag)
printf("\tLaunched child pid [%d]\n", getpid());
while (TRUE) {
random();
sleep(5);
}
}
static void
dump_info_table(pidinfo_t *info, uint32_t count)
{
int i;
printf("\n%10s \t%s \t\t%20s\n", "Pid", "Jetsam Priority", "Relative Bucket Index");
printf("%10s \t%s %20s\n", "", "(before | after | expected)", "(before | after)");
for (i=0; i < count; i++) {
printf("%10d", info[i].pid);
printf("\t(%4d |", info[i].pri_before);
printf("%4d |", info[i].pri_after);
printf("%4d)", info[i].exp_after);
printf("\t\t(%5d |", info[i].indx_before);
printf("%5d)\n", info[i].indx_after);
}
}
static void
print_usage() {
printf("\nUsage:\n");
printf("[-e] [-p] [-v] [-d <seconds>][ -g <count> | -l <list of pids>]\n\n");
printf("Exercise the MEMORYSTATUS_CMD_GRP_SET_PROPERTIES command.\n");
printf("Operates on at most %d pids.\n", MAXTESTPIDS);
printf("Pass in a list of pids or allow the test to generate the pids dynamically.\n\n");
printf("\t -e : exercise error tests\n");
printf("\t -p <priority> : Override default priority band.\n");
printf("\t -v : extra verbosity\n");
printf("\t -d <seconds> : delay before and after idle move (default = 0)\n");
printf("\t -g <count> : dynamically generate <count> processes.\n");
printf("\t -l <list of pids> : operate on the given list of pids\n\n");
printf("\t default : generate %d pids, no delay, priority %d eg: -g %d -p %d\n\n",
MAXTESTPIDS, g_new_priority, MAXTESTPIDS, g_new_priority);
}