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