1#include <stdio.h> 2#include <stdlib.h> 3#include <ctype.h> 4#include <unistd.h> 5#include <mach/mach.h> 6#include <mach/mach_types.h> 7#include <mach/task.h> 8#include <libproc.h> 9#include <mach/vm_purgable.h> 10 11#define USAGE "Usage: vm_purgeable_stat [-a | -p <pid> | -s <interval>]\n" 12#define PRIV_ERR_MSG "The option specified needs root priveleges." 13#define PROC_NAME_LEN 256 14#define KB 1024 15#define PURGEABLE_PRIO_LEVELS VM_VOLATILE_GROUP_SHIFT 16 17static inline int purge_info_size_adjust(uint64_t size); 18static inline char purge_info_unit(uint64_t size); 19void print_header(int summary_view); 20int get_system_tasks(task_array_t *tasks, mach_msg_type_number_t *count); 21int get_task_from_pid(int pid, task_t *task); 22void print_purge_info_task(task_t task, int pid); 23void print_purge_info_task_array(task_array_t tasks, mach_msg_type_number_t count); 24void print_purge_info_summary(int sleep_duration); 25 26static inline int purge_info_size_adjust(uint64_t size) 27{ 28 while(size > KB) 29 size /= KB; 30 return (int)size; 31} 32 33static inline char purge_info_unit(uint64_t size) 34{ 35 char sizes[] = {'B', 'K', 'M', 'G', 'T'}; 36 int index = 0; 37 38 while(size > KB) { 39 index++; 40 size /= KB; 41 } 42 return sizes[index]; 43} 44 45void print_header(int summary_view) 46{ 47 if (!summary_view) 48 printf("%20s ", "Process-Name"); 49 50 printf("%9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s %9s\n", 51 "FIFO-P0", "FIFO-P1", "FIFO-P2", "FIFO-P3", 52 "FIFO-P4", "FIFO-P5", "FIFO-P6", "FIFO-P7", 53 "OBSOLETE", 54 "LIFO-P0", "LIFO-P1", "LIFO-P2", "LIFO-P3", 55 "LIFO-P4", "LIFO-P5", "LIFO-P6", "LIFO-P7" 56 ); 57} 58 59int get_task_from_pid(int pid, task_t *task) 60{ 61 kern_return_t kr; 62 if (geteuid() != 0) { 63 fprintf(stderr, "%s\n", PRIV_ERR_MSG); 64 return -1; 65 } 66 kr = task_for_pid(mach_task_self(), pid, task); 67 if (kr != KERN_SUCCESS) { 68 fprintf(stderr, "Failed to get task port for pid: %d\n", pid); 69 return -1; 70 } 71 return 0; 72} 73 74int get_system_tasks(task_array_t *tasks, mach_msg_type_number_t *count) 75{ 76 processor_set_name_array_t psets; 77 mach_msg_type_number_t psetCount; 78 mach_port_t pset_priv; 79 kern_return_t ret; 80 81 if (geteuid() != 0) { 82 fprintf(stderr, "%s\n", PRIV_ERR_MSG); 83 return -1; 84 } 85 86 ret = host_processor_sets(mach_host_self(), &psets, &psetCount); 87 if (ret != KERN_SUCCESS) { 88 fprintf(stderr, "host_processor_sets() failed: %s\n", mach_error_string(ret)); 89 return -1; 90 } 91 if (psetCount != 1) { 92 fprintf(stderr, "Assertion Failure: pset count greater than one (%d)\n", psetCount); 93 return -1; 94 } 95 96 /* convert the processor-set-name port to a privileged port */ 97 ret = host_processor_set_priv(mach_host_self(), psets[0], &pset_priv); 98 if (ret != KERN_SUCCESS) { 99 fprintf(stderr, "host_processor_set_priv() failed: %s\n", mach_error_string(ret)); 100 return -1; 101 } 102 mach_port_deallocate(mach_task_self(), psets[0]); 103 vm_deallocate(mach_task_self(), (vm_address_t)psets, (vm_size_t)psetCount * sizeof(mach_port_t)); 104 105 /* convert the processor-set-priv to a list of tasks for the processor set */ 106 ret = processor_set_tasks(pset_priv, tasks, count); 107 if (ret != KERN_SUCCESS) { 108 fprintf(stderr, "processor_set_tasks() failed: %s\n", mach_error_string(ret)); 109 return -1; 110 } 111 mach_port_deallocate(mach_task_self(), pset_priv); 112 return 0; 113} 114 115void print_purge_info_task(task_t task, int pid) 116{ 117 task_purgable_info_t info; 118 kern_return_t kr; 119 int i; 120 char pname[PROC_NAME_LEN]; 121 122 kr = task_purgable_info(task, &info); 123 if (kr != KERN_SUCCESS) { 124 fprintf(stderr, "(pid: %d) task_purgable_info() failed: %s\n", pid, mach_error_string(kr)); 125 return; 126 } 127 if (0 == proc_name(pid, pname, PROC_NAME_LEN)) 128 strncpy(pname, "Unknown", 7); 129 pname[20] = 0; 130 printf("%20s ", pname); 131 for (i=0; i<PURGEABLE_PRIO_LEVELS; i++) 132 printf("%4u/%3d%c ", (unsigned)info.fifo_data[i].count, purge_info_size_adjust(info.fifo_data[i].size), purge_info_unit(info.fifo_data[i].size)); 133 printf("%4u/%3d%c ", (unsigned)info.obsolete_data.count, purge_info_size_adjust(info.obsolete_data.size), purge_info_unit(info.obsolete_data.size)); 134 for (i=0; i<PURGEABLE_PRIO_LEVELS; i++) 135 printf("%4u/%3d%c ", (unsigned)info.lifo_data[i].count, purge_info_size_adjust(info.lifo_data[i].size), purge_info_unit(info.lifo_data[i].size)); 136 printf("\n"); 137 return; 138} 139 140void print_purge_info_task_array(task_array_t tasks, mach_msg_type_number_t count) 141{ 142 int i; 143 int pid; 144 145 for (i=0; i<count; i++) { 146 if (KERN_SUCCESS != pid_for_task(tasks[i], &pid)) 147 continue; 148 print_purge_info_task(tasks[i], pid); 149 } 150 return; 151} 152 153void print_purge_info_summary(int sleep_duration) 154{ 155 host_purgable_info_data_t info; 156 mach_msg_type_number_t count; 157 kern_return_t result; 158 int i; 159 160 while(1) { 161 count = HOST_VM_PURGABLE_COUNT; 162 result = host_info(mach_host_self(), HOST_VM_PURGABLE, (host_info_t)&info, &count); 163 if (result != KERN_SUCCESS) 164 break; 165 for (i=0; i<PURGEABLE_PRIO_LEVELS; i++) 166 printf("%4u/%3d%c ", (unsigned)info.fifo_data[i].count, purge_info_size_adjust(info.fifo_data[i].size), purge_info_unit(info.fifo_data[i].size)); 167 printf("%4u/%3d%c ", (unsigned)info.obsolete_data.count, purge_info_size_adjust(info.obsolete_data.size), purge_info_unit(info.obsolete_data.size)); 168 for (i=0; i<PURGEABLE_PRIO_LEVELS; i++) 169 printf("%4u/%3d%c ", (unsigned)info.lifo_data[i].count, purge_info_size_adjust(info.lifo_data[i].size), purge_info_unit(info.lifo_data[i].size)); 170 printf("\n"); 171 sleep(sleep_duration); 172 } 173 return; 174} 175 176int main(int argc, char *argv[]) 177{ 178 179 char ch; 180 int pid; 181 int sleep_duration; 182 task_array_t tasks; 183 task_t task; 184 mach_msg_type_number_t taskCount; 185 int noargs = 1; 186 187 while(1) { 188 ch = getopt(argc, argv, "ahp:s:"); 189 if (ch == -1) 190 break; 191 noargs = 0; 192 switch(ch) { 193 case 'a': 194 if (get_system_tasks(&tasks, &taskCount) < 0) 195 break; 196 print_header(0); 197 print_purge_info_task_array(tasks, taskCount); 198 break; 199 200 case 'p': 201 pid = (int)strtol(optarg, NULL, 10); 202 if (pid < 0) 203 break; 204 if (get_task_from_pid(pid, &task) < 0) 205 break; 206 print_header(0); 207 print_purge_info_task(task, pid); 208 break; 209 case 's': 210 sleep_duration = (int)strtol(optarg, NULL, 10); 211 if (sleep_duration < 0) 212 break; 213 print_header(1); 214 print_purge_info_summary(sleep_duration); 215 break; 216 case '?': 217 case 'h': 218 default: 219 printf("%s", USAGE); 220 } 221 break; 222 } 223 if (noargs) 224 printf("%s", USAGE); 225 return 0; 226} 227 228 229