1/*
2 * Copyright (c) 2002-2004, 2008, 2009 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <limits.h>
26#include <sys/types.h>
27#include <mach/bootstrap.h>
28#include <mach/host_priv.h>
29#include <mach/mach_error.h>
30#include <mach/mach_time.h>
31#include <mach/mach_host.h>
32#include <mach/mach_port.h>
33#include <mach/mach_vm.h>
34#include <mach/mach_types.h>
35#include <mach/message.h>
36#include <mach/processor_set.h>
37#include <mach/task.h>
38#include <mach/task_policy.h>
39#include <mach/thread_act.h>
40#include <mach/shared_region.h>
41#include <mach/vm_map.h>
42#include <mach/vm_page_size.h>
43
44#define IOKIT 1 /* For io_name_t in device/device_types.h. */
45#include <device/device_types.h>
46#include <CoreFoundation/CoreFoundation.h>
47#include <IOKit/IOKitLib.h>
48#include <IOKit/storage/IOBlockStorageDriver.h>
49
50#include <libproc.h>
51
52#include <fcntl.h>
53#include <nlist.h>
54#include <sys/param.h>
55#include <sys/sysctl.h>
56#include <pwd.h>
57
58#include <sys/resource.h>
59
60#include <sys/socket.h>
61#include <net/if.h>
62#include <net/route.h>
63#include <net/if_types.h>
64#include <ifaddrs.h>
65
66#define LIBTOP_DBG
67#ifndef LIBTOP_DBG
68/* Disable assertions. */
69#ifndef NDEBUG
70#define NDEBUG
71#endif
72#endif
73#include <assert.h>
74
75#include "libtop.h"
76#include "rb.h"
77
78/*
79 * Process info.
80 */
81typedef struct libtop_pinfo_s libtop_pinfo_t;
82struct libtop_pinfo_s {
83	/* Sample data that are exposed to the library user. */
84	libtop_psamp_t		psamp;
85
86	/* Linkage for pid-ordered tree. */
87	rb_node(libtop_pinfo_t)	pnode;
88
89	/* Linkage for sorted tree. */
90	rb_node(libtop_pinfo_t)	snode;
91
92	int			flag; /* Set, but not used. */
93
94	/* Manual override for memory region reporting. */
95	libtop_preg_t		preg;
96
97	/* TRUE if the globally shared text and data segments are mapped in. */
98	boolean_t		split;
99};
100
101/* Memory object info. */
102typedef struct libtop_oinfo_s libtop_oinfo_t;
103struct libtop_oinfo_s {
104	/*
105	 * pinfo structure that was most recently used to access this
106	 * structure.
107	 */
108	libtop_pinfo_t	*pinfo;
109
110	/* Memory object ID. */
111	int		obj_id;
112
113	/* Memory object size, in pages. */
114	int		size;
115
116	/* SM_PRIVATE, SM_SHARED, SM_PRIVATE_ALIASED, SM_COW, ... */
117	int		share_type;
118
119	/* Number of pages resident in memory. */
120	int		resident_page_count;
121
122	/* Number of references to memory object. */
123	int		ref_count;
124
125	/* Number of references to memory object that pinfo holds. */
126	int		proc_ref_count;
127
128	/*
129	 * Rollback fields.  These are used to store old values that need to be
130	 * rolled back to their previous values if an object is referenced more
131	 * than once by a process.
132	 *
133	 * The reason rolling back is necessary has to do with the fact that
134	 * memory usage statistics are tallied on the fly, but this can only be
135	 * accurately done for each memory region a process refers to once all
136	 * of the references have been encountered.  Since the code
137	 * optimistically updates the statistics, changes have to be backed out
138	 * when another reference to the same memory region is encountered.
139	 */
140	int			rb_share_type; /* SM_EMPTY == "no rollback" */
141	uint64_t	rb_aliased;
142	uint64_t	rb_vprvt;
143	uint64_t	rb_rshrd;
144};
145
146static boolean_t ignore_PPP;
147
148/* Sample data that are exposed to the library user. */
149static libtop_tsamp_t tsamp;
150
151/* Function pointer that points to an abstract printing function. */
152static libtop_print_t *libtop_print;
153static void *libtop_user_data;
154
155/* Temporary storage for sorting function and opaque data pointer. */
156static libtop_sort_t *libtop_sort;
157static void *libtop_sort_data;
158
159/* Mach port, used for various Mach calls. */
160static mach_port_t libtop_port;
161
162/* Buffer that is large enough to hold the entire argument area of a process. */
163static char *libtop_arg;
164static int libtop_argmax;
165
166static mach_port_t libtop_master_port;
167
168static uint32_t interval;
169
170/*
171 * Memory object hash table and list.  For each sample, a hash table of memory
172 * objects is created, and it is used to determine various per-process memory
173 * statistics, as well as the total number of memory objects.  Rather than
174 * allocating and freeing oinfo structures for every sample, previously
175 * allocated structures are linked into a list, and objects in the list are used
176 * in preference to allocation.
177 */
178static CFMutableDictionaryRef libtop_oinfo_hash;
179
180/* Tree of all pinfo's, always ordered by -pid. */
181static rb_tree(libtop_pinfo_t) libtop_ptree;
182/*
183 * Transient tree of all pinfo's, created for each sample according to a
184 * sorting function.
185 */
186static rb_tree(libtop_pinfo_t) libtop_stree;
187
188/* TRUE if the most recent sample is sorted. */
189static boolean_t libtop_sorted;
190
191/* Pointer to the most recently seen pinfo structure in libtop_piterate(). */
192static libtop_pinfo_t *libtop_piter;
193
194/* Cache of uid->username translations. */
195static CFMutableDictionaryRef libtop_uhash;
196
197#define	TIME_VALUE_TO_TIMEVAL(a, r) do {				\
198	(r)->tv_sec = (a)->seconds;					\
199	(r)->tv_usec = (a)->microseconds;				\
200} while (0)
201
202enum libtop_status {
203	LIBTOP_NO_ERR = 0,
204	LIBTOP_ERR_INVALID = 1, /* An invalid task. */
205	LIBTOP_ERR_ALLOC  /* An allocation error. */
206};
207
208mach_timebase_info_data_t timebase_info;
209typedef enum libtop_status libtop_status_t;
210
211/* Function prototypes. */
212static boolean_t libtop_p_print(void *user_data, const char *format, ...);
213static int libtop_p_mach_state_order(int state, long sleep_time);
214static int libtop_p_load_get(host_info_t r_load);
215static int libtop_p_loadavg_update(void);
216static bool in_shared_region(mach_vm_address_t addr, cpu_type_t type);
217static void libtop_p_fw_scan(task_t task, mach_vm_address_t region_base,
218	mach_vm_size_t region_size);
219static void libtop_p_fw_sample(boolean_t fw);
220static int libtop_p_vm_sample(void);
221static void libtop_p_networks_sample(void);
222static int libtop_p_disks_sample(void);
223static int libtop_p_proc_table_read(boolean_t reg);
224static libtop_status_t libtop_p_cputype(pid_t pid, cpu_type_t *cputype);
225static mach_vm_size_t libtop_p_shreg_size(cpu_type_t);
226static libtop_status_t libtop_p_task_update(task_t task, boolean_t reg);
227static libtop_status_t libtop_p_proc_command(libtop_pinfo_t *pinfo,
228	struct kinfo_proc *kinfo);
229static void libtop_p_pinsert(libtop_pinfo_t *pinfo);
230static void libtop_p_premove(libtop_pinfo_t *pinfo);
231static void libtop_p_destroy_pinfo(libtop_pinfo_t *pinfo);
232static libtop_pinfo_t* libtop_p_psearch(pid_t pid);
233static int libtop_p_pinfo_pid_comp(libtop_pinfo_t *a, libtop_pinfo_t *b);
234static int libtop_p_pinfo_comp(libtop_pinfo_t *a, libtop_pinfo_t *b);
235static libtop_oinfo_t* libtop_p_oinfo_insert(int obj_id, int share_type,
236	int resident_page_count, int ref_count, int size,
237	libtop_pinfo_t *pinfo);
238static int libtop_p_wq_update(libtop_pinfo_t *pinfo);
239static void libtop_i64_test(void);
240static void update_pages_stolen(libtop_tsamp_t *tsamp);
241
242
243/* CFDictionary callbacks */
244static void
245simpleFree(CFAllocatorRef allocator, const void *value)
246{
247	free((void *)value);
248}
249
250static const void *
251stringRetain(CFAllocatorRef allocator, const void *value)
252{
253	return strdup(value);
254}
255
256static Boolean
257stringEqual(const void *value1, const void *value2)
258{
259	return strcmp(value1, value2) == 0;
260}
261
262int
263libtop_init(libtop_print_t *print, void *user_data)
264{
265	//libtop_i64_test();
266
267	if (print != NULL) {
268		libtop_print = print;
269		libtop_user_data = user_data;
270	} else {
271		/* Use a noop printing function. */
272		libtop_print = libtop_p_print;
273		libtop_user_data = NULL;
274	}
275
276	tsamp.seq = 0;
277	interval = 1;
278	libtop_port = mach_host_self();
279	tsamp.pagesize = vm_kernel_page_size;
280
281	/* Get the physical memory size of the system. */
282	{
283		int mib[2];
284		size_t size;
285
286		mib[0] = CTL_HW;
287		mib[1] = HW_MEMSIZE;
288
289		size = sizeof(tsamp.memsize);
290		if (sysctl(mib, 2, &tsamp.memsize, &size, NULL, 0) == -1) {
291			libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
292			    __FUNCTION__, strerror(errno));
293			return -1;
294		}
295	}
296
297	update_pages_stolen(&tsamp);
298
299	/* Initialize the pinfo tree. */
300	rb_tree_new(&libtop_ptree, pnode);
301
302	/* Initialized the user hash. */
303	CFDictionaryValueCallBacks stringValueCallBacks = { 0, stringRetain, simpleFree, NULL, stringEqual };
304	libtop_uhash = CFDictionaryCreateMutable(NULL, 0, NULL, &stringValueCallBacks);
305
306	/* Initialize the memory object hash table and spares ring. */
307	CFDictionaryValueCallBacks oinfoValueCallBacks = { 0, NULL, simpleFree, NULL, NULL };
308	libtop_oinfo_hash = CFDictionaryCreateMutable(NULL, 0, NULL, &oinfoValueCallBacks);
309
310	/*
311	 * Allocate a buffer that is large enough to hold the maximum arguments
312	 * to execve().  This is used when getting the arguments to programs.
313	 */
314	{
315		int	mib[2];
316		size_t	size;
317
318		mib[0] = CTL_KERN;
319		mib[1] = KERN_ARGMAX;
320
321		size = sizeof(libtop_argmax);
322		if (sysctl(mib, 2, &libtop_argmax, &size, NULL, 0) == -1) {
323			libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
324			    __FUNCTION__, strerror(errno));
325			return -1;
326		}
327
328		libtop_arg = (char *)malloc(libtop_argmax);
329		if (libtop_arg == NULL) return -1;
330	}
331
332	/*
333	 * Get ports and services for drive statistics.
334	 */
335
336	if (IOMasterPort(bootstrap_port, &libtop_master_port)) {
337		libtop_print(libtop_user_data, "Error in IOMasterPort()");
338		return -1;
339	}
340
341	/* Initialize the load statistics. */
342	if (libtop_p_load_get((host_info_t)&tsamp.b_cpu)) return -1;
343
344	tsamp.p_cpu = tsamp.b_cpu;
345	tsamp.cpu = tsamp.b_cpu;
346
347	/* Initialize the time. */
348	gettimeofday(&tsamp.b_time, NULL);
349	tsamp.p_time = tsamp.b_time;
350	tsamp.time = tsamp.b_time;
351	mach_timebase_info(&timebase_info);
352
353	ignore_PPP = FALSE;
354	return 0;
355}
356
357void
358libtop_fini(void)
359{
360	libtop_pinfo_t *pinfo, *ppinfo;
361
362	/* Deallocate the arg string. */
363	free(libtop_arg);
364
365	/* Clean up the oinfo structures. */
366	CFRelease(libtop_oinfo_hash);
367
368	/* Clean up the pinfo structures. */
369	rb_first(&libtop_ptree, pnode, pinfo);
370	for (;
371	     pinfo != rb_tree_nil(&libtop_ptree);
372	     pinfo = ppinfo) {
373		rb_next(&libtop_ptree, pinfo, libtop_pinfo_t, pnode, ppinfo);
374
375		/* This removes the pinfo from the tree, and frees pinfo and its data. */
376		libtop_p_destroy_pinfo(pinfo);
377	}
378
379	/* Clean up the uid->username translation cache. */
380	CFRelease(libtop_uhash);
381}
382
383/*
384 * Set the interval between framework updates.
385 */
386int
387libtop_set_interval(uint32_t ival)
388{
389	if (ival < 0 || ival > LIBTOP_MAX_INTERVAL) {
390		return -1;
391	}
392	interval = ival;
393	return 0;
394}
395
396/* Take a sample. */
397int
398libtop_sample(boolean_t reg, boolean_t fw)
399{
400	int res = 0;
401
402	/* Increment the sample sequence number. */
403	tsamp.seq++;
404
405	/*
406	 * Make a note that the results haven't been sorted (reset by
407	 * libtop_psort()).
408	 */
409	libtop_sorted = FALSE;
410	libtop_piter = NULL;
411
412	/* Clear state breakdown. */
413	memset(tsamp.state_breakdown, 0, sizeof(tsamp.state_breakdown));
414
415	/* Get time. */
416	if (tsamp.seq != 1) {
417		tsamp.p_time = tsamp.time;
418		res = gettimeofday(&tsamp.time, NULL);
419	}
420
421	if (res == 0) res = libtop_p_proc_table_read(reg);
422	if (res == 0) res = libtop_p_loadavg_update();
423
424	/* Get CPU usage counters. */
425	tsamp.p_cpu = tsamp.cpu;
426	if (res == 0) libtop_p_load_get((host_info_t)&tsamp.cpu);
427
428	if (res == 0) libtop_p_fw_sample(fw);
429	if (res == 0) libtop_p_vm_sample();
430	if (res == 0) libtop_p_networks_sample();
431	if (res == 0) libtop_p_disks_sample();
432
433	return res;
434}
435
436/* Return a pointer to the structure that contains libtop-wide data. */
437const libtop_tsamp_t *
438libtop_tsamp(void)
439{
440	return &tsamp;
441}
442
443/*
444 * Given a tree of pinfo structures, create another tree that is sorted
445 * according to sort().
446 */
447void
448libtop_psort(libtop_sort_t *sort, void *data)
449{
450	libtop_pinfo_t	*pinfo, *ppinfo;
451
452	assert(tsamp.seq != 0);
453
454	/* Reset the iteration pointer. */
455	libtop_piter = NULL;
456
457	/* Initialize the sorted tree. */
458	rb_tree_new(&libtop_stree, snode);
459
460	/* Note that the results are sorted. */
461	libtop_sorted = TRUE;
462
463	/*
464	 * Set the sorting function and opaque data in preparation for building
465	 * the sorted tree.
466	 */
467	libtop_sort = sort;
468	libtop_sort_data = data;
469
470	/*
471	 * Iterate through ptree and insert the pinfo's into a sorted tree.
472	 * At the same time, prune pinfo's that were associated with processes
473	 * that were not found during the most recent sample.
474	 */
475	tsamp.nprocs = 0;
476	rb_first(&libtop_ptree, pnode, pinfo);
477	for (;
478	     pinfo != rb_tree_nil(&libtop_ptree);
479	     pinfo = ppinfo) {
480		/*
481		 * Get the next pinfo before potentially removing this one from
482		 * the tree.
483		 */
484		rb_next(&libtop_ptree, pinfo, libtop_pinfo_t, pnode, ppinfo);
485
486		if (pinfo->psamp.seq == tsamp.seq) {
487			/* Insert the pinfo into the sorted tree. */
488			rb_node_new(&libtop_stree, pinfo, snode);
489			rb_insert(&libtop_stree, pinfo, libtop_p_pinfo_comp,
490			    libtop_pinfo_t, snode);
491
492			tsamp.nprocs++;
493		} else {
494			/* The associated process has gone away. */
495			libtop_p_destroy_pinfo(pinfo);
496		}
497	}
498}
499
500/*
501 * Iteratively return a pointer to each process that was in the most recent
502 * sample.  The order depends on if/how libtop_psort() was called.
503 */
504const libtop_psamp_t *
505libtop_piterate(void)
506{
507	assert(tsamp.seq != 0);
508
509	if (libtop_sorted) {
510		/* Use the order set by libtop_psort(). */
511		if (libtop_piter == NULL) {
512			rb_first(&libtop_stree, snode, libtop_piter);
513		} else {
514			rb_next(&libtop_stree, libtop_piter, libtop_pinfo_t,
515			    snode, libtop_piter);
516		}
517		if (libtop_piter == rb_tree_nil(&libtop_stree)) {
518			libtop_piter = NULL;
519		}
520	} else {
521		boolean_t	dead;
522
523		/*
524		 * Return results in ascending pid order.  Since dead processes
525		 * weren't cleaned out by libtop_psamp(), take care to do so
526		 * here on the fly.
527		 */
528		if (libtop_piter == NULL) {
529			rb_first(&libtop_ptree, pnode, libtop_piter);
530		} else {
531			rb_next(&libtop_ptree, libtop_piter, libtop_pinfo_t,
532			    pnode, libtop_piter);
533		}
534
535		do {
536			dead = FALSE;
537
538			if (libtop_piter == rb_tree_nil(&libtop_ptree)) {
539				/* No more tree nodes. */
540				libtop_piter = NULL;
541				break;
542			}
543			if (libtop_piter->psamp.seq != tsamp.seq) {
544				libtop_pinfo_t	*pinfo;
545
546				/*
547				 * Dead process.  Get the next pinfo tree node
548				 * before removing this one.
549				 */
550				pinfo = libtop_piter;
551				rb_next(&libtop_ptree, libtop_piter,
552				    libtop_pinfo_t, pnode, libtop_piter);
553
554				libtop_p_destroy_pinfo(pinfo);
555
556				dead = TRUE;
557			}
558		} while (dead);
559	}
560
561	return &libtop_piter->psamp;
562}
563
564/*
565 * Set whether to collect memory region information for the process with pid
566 * pid.
567 */
568int
569libtop_preg(pid_t pid, libtop_preg_t preg)
570{
571	libtop_pinfo_t	*pinfo;
572
573	pinfo = libtop_p_psearch(pid);
574	if (pinfo == NULL) return -1;
575	pinfo->preg = preg;
576
577	return 0;
578}
579
580/* Return a pointer to the username string associated with uid. */
581const char *
582libtop_username(uid_t uid)
583{
584	const void *k = (const void *)(uintptr_t)uid;
585
586	if (!CFDictionaryContainsKey(libtop_uhash, k)) {
587		struct passwd *pwd = getpwuid(uid);
588		if (pwd == NULL)
589			return NULL;
590		CFDictionarySetValue(libtop_uhash, k, pwd->pw_name);
591	}
592
593	return CFDictionaryGetValue(libtop_uhash, k);
594}
595
596/* Return a pointer to a string representation of a process state. */
597const char *
598libtop_state_str(uint32_t state)
599{
600	const char *strings[] = {
601		"zombie",
602#define LIBTOP_STATE_ZOMBIE	0
603		"running",
604#define LIBTOP_STATE_RUN	1
605		"stuck",
606#define LIBTOP_STATE_STUCK	2
607		"sleeping",
608#define LIBTOP_STATE_SLEEP	3
609		"idle",
610#define LIBTOP_STATE_IDLE	4
611		"stopped",
612#define LIBTOP_STATE_STOP	5
613		"halted",
614#define LIBTOP_STATE_HALT	6
615		"unknown"
616#define LIBTOP_STATE_UNKNOWN	7
617	};
618
619	assert(LIBTOP_NSTATES == sizeof(strings) / sizeof(char *));
620	assert(state <= LIBTOP_STATE_MAX);
621	assert(LIBTOP_STATE_MAXLEN >= 8); /* "sleeping" */
622
623	return strings[state];
624}
625
626/*
627 * Noop printing function, used when the user doesn't supply a printing
628 * function.
629 */
630static boolean_t
631libtop_p_print(void *user_data, const char *format, ...)
632{
633	/* Do nothing. */
634	return 0;
635}
636
637/* Translate a mach state to a state in the state breakdown array. */
638static int
639libtop_p_mach_state_order(int state, long sleeptime)
640{
641	switch (state) {
642		case TH_STATE_RUNNING:
643			return LIBTOP_STATE_RUN;
644		case TH_STATE_UNINTERRUPTIBLE:
645			return LIBTOP_STATE_STUCK;
646		case TH_STATE_STOPPED:
647			return LIBTOP_STATE_STOP;
648		case TH_STATE_HALTED:
649			return LIBTOP_STATE_HALT;
650		case TH_STATE_WAITING:
651			return (sleeptime > 0) ? LIBTOP_STATE_IDLE : LIBTOP_STATE_SLEEP;
652		default:
653			return LIBTOP_STATE_UNKNOWN;
654	}
655}
656
657/* Get CPU load. */
658static int
659libtop_p_load_get(host_info_t r_load)
660{
661	kern_return_t kr;
662	mach_msg_type_number_t count;
663
664	count = HOST_CPU_LOAD_INFO_COUNT;
665	kr = host_statistics(libtop_port, HOST_CPU_LOAD_INFO, r_load, &count);
666	if (kr != KERN_SUCCESS) {
667		libtop_print(libtop_user_data, "Error in host_statistics(): %s",
668		    mach_error_string(kr));
669		return -1;
670	}
671
672	return 0;
673}
674
675/* Update load averages. */
676static int
677libtop_p_loadavg_update(void)
678{
679	double avg[3];
680
681	if (getloadavg(avg, sizeof(avg)) < 0) {
682		libtop_print(libtop_user_data,  "Error in getloadavg(): %s",
683			strerror(errno));
684		return -1;
685	}
686
687	tsamp.loadavg[0] = avg[0];
688	tsamp.loadavg[1] = avg[1];
689	tsamp.loadavg[2] = avg[2];
690
691	return 0;
692}
693
694/*
695 * Test whether the virtual address is within the architecture's shared region.
696 */
697static bool
698in_shared_region(mach_vm_address_t addr, cpu_type_t type) {
699	mach_vm_address_t base = 0, size = 0;
700
701	switch(type) {
702		case CPU_TYPE_ARM:
703			base = SHARED_REGION_BASE_ARM;
704			size = SHARED_REGION_SIZE_ARM;
705		break;
706
707
708		case CPU_TYPE_X86_64:
709			base = SHARED_REGION_BASE_X86_64;
710			size = SHARED_REGION_SIZE_X86_64;
711		break;
712
713		case CPU_TYPE_I386:
714			base = SHARED_REGION_BASE_I386;
715			size = SHARED_REGION_SIZE_I386;
716		break;
717
718		case CPU_TYPE_POWERPC:
719			base = SHARED_REGION_BASE_PPC;
720			size = SHARED_REGION_SIZE_PPC;
721		break;
722
723		case CPU_TYPE_POWERPC64:
724			base = SHARED_REGION_BASE_PPC64;
725			size = SHARED_REGION_SIZE_PPC64;
726		break;
727
728		default: {
729			int t = type;
730
731			fprintf(stderr, "unknown CPU type: 0x%x\n", t);
732			abort();
733		}
734		break;
735	}
736
737
738	return(addr >= base && addr < (base + size));
739}
740
741/* Iterate through a given region of memory, adding up the various
742   submap regions found therein.  Modifies tsamp. */
743static void
744libtop_p_fw_scan(task_t task, mach_vm_address_t region_base, mach_vm_size_t region_size) {
745	mach_vm_size_t	vsize = 0;
746	mach_vm_size_t	code = 0;
747	mach_vm_size_t	data = 0;
748	mach_vm_size_t	linkedit = 0;
749	mach_vm_size_t	pagesize = tsamp.pagesize;
750
751	mach_vm_address_t	addr;
752	mach_vm_size_t		size;
753
754
755	for (addr = region_base; addr < (region_base + region_size); addr += size) {
756		kern_return_t kr;
757		uint32_t depth = 1;
758		vm_region_submap_info_data_64_t sinfo;
759		mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
760
761		// Get the next submap in the specified region of memory
762		kr = mach_vm_region_recurse(task, &addr, &size, &depth,
763									(vm_region_recurse_info_t)&sinfo, &count);
764		if (kr != KERN_SUCCESS) break;
765
766		/*
767		 * There should be no reason to test if addr is in a shared
768		 * region, because the for loop limits the region space to the
769		 * passed SHARED_REGION_BASE and SHARED_REGION_SIZE.
770		 */
771
772		vsize += size;
773
774		switch (sinfo.share_mode) {
775			case SM_SHARED:
776			case SM_COW:
777			case SM_TRUESHARED:
778				if (sinfo.max_protection & VM_PROT_EXECUTE) {
779					// CODE
780					code += sinfo.pages_resident;
781					tsamp.fw_count++;
782				} else if (sinfo.max_protection & VM_PROT_WRITE) {
783					// DATA
784					data += sinfo.pages_resident;
785				} else {
786					// LINKEDIT
787					linkedit += sinfo.pages_resident;
788				}
789				break;
790		}
791	}
792
793	tsamp.fw_vsize += vsize;
794	tsamp.fw_code += code * pagesize;
795	tsamp.fw_data += data * pagesize;
796	tsamp.fw_linkedit += linkedit * pagesize;
797}
798
799/* Sample framework memory statistics (if fw is TRUE). */
800static void
801libtop_p_fw_sample(boolean_t fw)
802{
803	if (!fw) return;
804	if ((interval != 1) && ((tsamp.seq % interval) != 1)) return;
805
806	tsamp.fw_count = 0;
807	tsamp.fw_code = 0;
808	tsamp.fw_data = 0;
809	tsamp.fw_linkedit = 0;
810	tsamp.fw_vsize = 0;
811	tsamp.fw_private = 0;
812
813#ifdef	__arm__
814	libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_ARM, SHARED_REGION_SIZE_ARM);
815#else
816	libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_PPC, SHARED_REGION_SIZE_PPC);
817	libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_PPC64, SHARED_REGION_SIZE_PPC64);
818
819	assert(SHARED_REGION_BASE_PPC <= SHARED_REGION_BASE_I386);
820	assert(SHARED_REGION_SIZE_PPC >= SHARED_REGION_SIZE_I386);
821	// Skip: i386 region is a subset of PPC region.
822	//libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386);
823
824	assert(SHARED_REGION_BASE_PPC64 <= SHARED_REGION_BASE_X86_64);
825	assert(SHARED_REGION_SIZE_PPC64 >= SHARED_REGION_SIZE_X86_64);
826	// Skip: x86_64 region is a subset of ppc64 region.
827	//libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_X86_64, SHARED_REGION_SIZE_X86_64);
828#endif
829
830	// Iterate through all processes, collecting their individual fw stats
831	libtop_piter = NULL;
832	while (libtop_piterate()) {
833		tsamp.fw_private += libtop_piter->psamp.fw_private;
834	}
835}
836
837static int
838libtop_tsamp_update_swap_usage(libtop_tsamp_t* tsamp) {
839	int mib[2] = { CTL_VM, VM_SWAPUSAGE };
840	int miblen = 2;
841	size_t len = sizeof(tsamp->xsu);
842	int res = sysctl(mib, miblen, &tsamp->xsu, &len, NULL, 0);
843	tsamp->xsu_is_valid = (res == 0);
844	return res;
845}
846
847/* This is for <rdar://problem/6410098>. */
848static uint64_t
849round_down_wired(uint64_t value) {
850	return (value & ~((128 * 1024 * 1024ULL) - 1));
851}
852
853/* This is for <rdar://problem/6410098>. */
854static void
855update_pages_stolen(libtop_tsamp_t *tsamp) {
856	static int mib_reserved[CTL_MAXNAME];
857	static int mib_unusable[CTL_MAXNAME];
858	static int mib_other[CTL_MAXNAME];
859	static size_t mib_reserved_len = 0;
860	static size_t mib_unusable_len = 0;
861	static size_t mib_other_len = 0;
862	int r;
863
864	tsamp->pages_stolen = 0;
865
866	/* This can be used for testing: */
867	//tsamp->pages_stolen = (256 * 1024 * 1024ULL) / tsamp->pagesize;
868
869	if(0 == mib_reserved_len) {
870		mib_reserved_len = CTL_MAXNAME;
871
872		r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved,
873				    &mib_reserved_len);
874
875		if(-1 == r) {
876			mib_reserved_len = 0;
877			return;
878		}
879
880		mib_unusable_len = CTL_MAXNAME;
881
882		r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable,
883				    &mib_unusable_len);
884
885		if(-1 == r) {
886			mib_reserved_len = 0;
887			return;
888		}
889
890
891		mib_other_len = CTL_MAXNAME;
892
893		r = sysctlnametomib("machdep.memmap.Other", mib_other,
894				    &mib_other_len);
895
896		if(-1 == r) {
897			mib_reserved_len = 0;
898			return;
899		}
900	}
901
902	if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0) {
903		uint64_t reserved = 0, unusable = 0, other = 0;
904		size_t reserved_len;
905		size_t unusable_len;
906		size_t other_len;
907
908		reserved_len = sizeof(reserved);
909		unusable_len = sizeof(unusable);
910		other_len = sizeof(other);
911
912		/* These are all declared as QUAD/uint64_t sysctls in the kernel. */
913
914		if(-1 == sysctl(mib_reserved, mib_reserved_len, &reserved,
915				&reserved_len, NULL, 0)) {
916			return;
917		}
918
919		if(-1 == sysctl(mib_unusable, mib_unusable_len, &unusable,
920				&unusable_len, NULL, 0)) {
921			return;
922		}
923
924		if(-1 == sysctl(mib_other, mib_other_len, &other,
925				&other_len, NULL, 0)) {
926			return;
927		}
928
929		if(reserved_len == sizeof(reserved)
930		   && unusable_len == sizeof(unusable)
931		   && other_len == sizeof(other)) {
932			uint64_t stolen = reserved + unusable + other;
933			uint64_t mb128 = 128 * 1024 * 1024ULL;
934
935			if(stolen >= mb128) {
936				tsamp->pages_stolen = round_down_wired(stolen) / tsamp->pagesize;
937			}
938		}
939	}
940}
941
942
943
944static int
945libtop_tsamp_update_vm_stats(libtop_tsamp_t* tsamp) {
946	kern_return_t kr;
947	tsamp->p_vm_stat = tsamp->vm_stat;
948
949	mach_msg_type_number_t count = sizeof(tsamp->vm_stat) / sizeof(natural_t);
950	kr = host_statistics64(libtop_port, HOST_VM_INFO64, (host_info64_t)&tsamp->vm_stat, &count);
951	if (kr != KERN_SUCCESS) {
952		return kr;
953	}
954
955	if (tsamp->pages_stolen > 0) {
956		tsamp->vm_stat.wire_count += tsamp->pages_stolen;
957	}
958
959	// Check whether we got purgeable memory statistics
960	tsamp->purgeable_is_valid = (count == (sizeof(tsamp->vm_stat)/sizeof(natural_t)));
961	if (!tsamp->purgeable_is_valid) {
962		tsamp->vm_stat.purgeable_count = 0;
963		tsamp->vm_stat.purges = 0;
964	}
965
966	if (tsamp->seq == 1) {
967		tsamp->p_vm_stat = tsamp->vm_stat;
968		tsamp->b_vm_stat = tsamp->vm_stat;
969	}
970
971	return kr;
972}
973
974static void
975sum_rshrd(const void *key, const void *value, void *context)
976{
977	libtop_oinfo_t *oinfo = (libtop_oinfo_t *)value;
978	mach_vm_size_t *rshrd = (mach_vm_size_t *)context;
979
980	switch (oinfo->share_type) {
981	case SM_SHARED:
982	case SM_COW:
983		*rshrd += oinfo->resident_page_count;
984		break;
985	}
986}
987
988/* Sample general VM statistics. */
989static int
990libtop_p_vm_sample(void)
991{
992	/* Get VM statistics. */
993	libtop_tsamp_update_vm_stats(&tsamp);
994
995	/* Get swap usage */
996	libtop_tsamp_update_swap_usage(&tsamp);
997
998	/*
999	 * Iterate through the oinfo hash table and add up the collective size
1000	 * of the shared objects.
1001	 */
1002
1003	mach_vm_size_t reg = 0;
1004	mach_vm_size_t rprvt = 0;
1005	mach_vm_size_t rshrd = 0;
1006	mach_vm_size_t vsize = 0;
1007
1008	CFDictionaryApplyFunction(libtop_oinfo_hash, sum_rshrd, &rshrd);
1009
1010	/* Iterate through all processes, collecting their individual vm stats */
1011	libtop_piter = NULL;
1012	while (libtop_piterate()) {
1013		reg += libtop_piter->psamp.reg;
1014		rprvt += libtop_piter->psamp.rprvt;
1015		vsize += libtop_piter->psamp.vsize;
1016	}
1017
1018	tsamp.reg = reg;
1019	tsamp.rprvt = rprvt;
1020	tsamp.rshrd = rshrd * tsamp.pagesize;
1021	tsamp.vsize = vsize;
1022
1023	return 0;
1024}
1025
1026/*
1027 * Sample network usage.
1028 *
1029 * The algorithm used does not deal with the following condition, which can
1030 * cause the statistics to be invalid:
1031 *
1032 *  Interfaces are dynamic -- they can appear and disappear at any time.
1033 *  There is no way to get statistics on an interface that has disappeared, so
1034 *  it isn't possible to determine the amount of data transfer between the
1035 *  previous sample and when the interface went away.
1036 *
1037 *  Due to this problem, if an interface disappears, it is possible for the
1038 *  current sample values to be lower than those of the beginning or previous
1039 *  samples.
1040 */
1041static void
1042libtop_p_networks_sample(void)
1043{
1044	short network_layer;
1045	short link_layer;
1046 	int mib[6];
1047     	char *buf = NULL, *lim, *next;
1048	size_t len;
1049	struct if_msghdr *ifm;
1050
1051
1052	tsamp.p_net_ipackets = tsamp.net_ipackets;
1053	tsamp.p_net_opackets = tsamp.net_opackets;
1054	tsamp.p_net_ibytes = tsamp.net_ibytes;
1055	tsamp.p_net_obytes = tsamp.net_obytes;
1056
1057	tsamp.net_ipackets = 0;
1058	tsamp.net_opackets = 0;
1059	tsamp.net_ibytes = 0;
1060	tsamp.net_obytes = 0;
1061
1062	mib[0]	= CTL_NET;			// networking subsystem
1063	mib[1]	= PF_ROUTE;			// type of information
1064	mib[2]	= 0;				// protocol (IPPROTO_xxx)
1065	mib[3]	= 0;				// address family
1066	mib[4]	= NET_RT_IFLIST2;	// operation
1067	mib[5]	= 0;
1068	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) return;
1069	if ((buf = malloc(len)) == NULL) {
1070		printf("malloc failed\n");
1071		exit(1);
1072	}
1073	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
1074		if (buf) free(buf);
1075		return;
1076	}
1077
1078	lim = buf + len;
1079	for (next = buf; next < lim; ) {
1080		network_layer = link_layer = 0;
1081	        ifm = (struct if_msghdr *)next;
1082		next += ifm->ifm_msglen;
1083
1084	        if (ifm->ifm_type == RTM_IFINFO2) {
1085			struct if_msghdr2 	*if2m = (struct if_msghdr2 *)ifm;
1086
1087			if(if2m->ifm_data.ifi_type==IFT_ETHER)  /* If we've seen any ethernet traffic, */
1088				ignore_PPP=TRUE; 		/* ignore any PPP traffic (PPPoE or VPN) */
1089
1090
1091			if(/*We want to count them all in top.*/ 1
1092				|| ((if2m->ifm_data.ifi_type!=IFT_LOOP)   /* do not count loopback traffic */
1093			   && !(ignore_PPP && if2m->ifm_data.ifi_type==IFT_PPP))) { /* or VPN/PPPoE */
1094				tsamp.net_opackets += if2m->ifm_data.ifi_opackets;
1095				tsamp.net_ipackets += if2m->ifm_data.ifi_ipackets;
1096				tsamp.net_obytes   += if2m->ifm_data.ifi_obytes;
1097				tsamp.net_ibytes   += if2m->ifm_data.ifi_ibytes;
1098			}
1099		}
1100	}
1101
1102	if (tsamp.seq == 1) {
1103		tsamp.b_net_ipackets = tsamp.net_ipackets;
1104		tsamp.p_net_ipackets = tsamp.net_ipackets;
1105
1106		tsamp.b_net_opackets = tsamp.net_opackets;
1107		tsamp.p_net_opackets = tsamp.net_opackets;
1108
1109		tsamp.b_net_ibytes = tsamp.net_ibytes;
1110		tsamp.p_net_ibytes = tsamp.net_ibytes;
1111
1112		tsamp.b_net_obytes = tsamp.net_obytes;
1113		tsamp.p_net_obytes = tsamp.net_obytes;
1114	}
1115
1116	free(buf);
1117}
1118
1119/*
1120 * Sample disk usage.  The algorithm used has the same limitations as that used
1121 * for libtop_p_networks_sample().
1122 */
1123static int
1124libtop_p_disks_sample(void)
1125{
1126	int retval;
1127	io_registry_entry_t	drive;
1128	io_iterator_t		drive_list;
1129	CFNumberRef		number;
1130	CFDictionaryRef		properties, statistics;
1131	UInt64			value;
1132
1133	/* Get the list of all drive objects. */
1134	if (IOServiceGetMatchingServices(libtop_master_port,
1135	    IOServiceMatching("IOBlockStorageDriver"), &drive_list)) {
1136		libtop_print(libtop_user_data,
1137		    "Error in IOServiceGetMatchingServices()");
1138		return -1;
1139	}
1140
1141	tsamp.p_disk_rops = tsamp.disk_rops;
1142	tsamp.p_disk_wops = tsamp.disk_wops;
1143	tsamp.p_disk_rbytes = tsamp.disk_rbytes;
1144	tsamp.p_disk_wbytes = tsamp.disk_wbytes;
1145
1146	tsamp.disk_rops = 0;
1147	tsamp.disk_wops = 0;
1148	tsamp.disk_rbytes = 0;
1149	tsamp.disk_wbytes = 0;
1150	while ((drive = IOIteratorNext(drive_list)) != 0) {
1151		number = 0;
1152		properties = 0;
1153		statistics = 0;
1154		value = 0;
1155
1156		/* Obtain the properties for this drive object. */
1157		if (IORegistryEntryCreateCFProperties(drive,
1158		    (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0)) {
1159			libtop_print(libtop_user_data,
1160			    "Error in IORegistryEntryCreateCFProperties()");
1161			retval = -1;
1162			goto RETURN;  // We must use a goto here to clean up drive_list
1163		}
1164
1165		if (properties != 0) {
1166			/* Obtain the statistics from the drive properties. */
1167			statistics
1168			    = (CFDictionaryRef)CFDictionaryGetValue(properties,
1169			    CFSTR(kIOBlockStorageDriverStatisticsKey));
1170
1171			if (statistics != 0) {
1172				/* Get number of reads. */
1173				number =
1174				    (CFNumberRef)CFDictionaryGetValue(statistics,
1175				    CFSTR(kIOBlockStorageDriverStatisticsReadsKey));
1176				if (number != 0) {
1177					CFNumberGetValue(number,
1178					    kCFNumberSInt64Type, &value);
1179					tsamp.disk_rops += value;
1180				}
1181
1182				/* Get bytes read. */
1183				number =
1184				    (CFNumberRef)CFDictionaryGetValue(statistics,
1185				    CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
1186				if (number != 0) {
1187					CFNumberGetValue(number,
1188					    kCFNumberSInt64Type, &value);
1189					tsamp.disk_rbytes += value;
1190				}
1191
1192				/* Get number of writes. */
1193				number =
1194				    (CFNumberRef)CFDictionaryGetValue(statistics,
1195				    CFSTR(kIOBlockStorageDriverStatisticsWritesKey));
1196				if (number != 0) {
1197					CFNumberGetValue(number,
1198					    kCFNumberSInt64Type, &value);
1199					tsamp.disk_wops += value;
1200				}
1201
1202				/* Get bytes written. */
1203				number =
1204				    (CFNumberRef)CFDictionaryGetValue(statistics,
1205				    CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
1206				if (number != 0) {
1207					CFNumberGetValue(number,
1208					    kCFNumberSInt64Type, &value);
1209					tsamp.disk_wbytes += value;
1210				}
1211			}
1212
1213			/* Release. */
1214			CFRelease(properties);
1215		}
1216
1217		/* Release. */
1218		IOObjectRelease(drive);
1219	}
1220	IOIteratorReset(drive_list);
1221	if (tsamp.seq == 1) {
1222		tsamp.b_disk_rops = tsamp.disk_rops;
1223		tsamp.p_disk_rops = tsamp.disk_rops;
1224
1225		tsamp.b_disk_wops = tsamp.disk_wops;
1226		tsamp.p_disk_wops = tsamp.disk_wops;
1227
1228		tsamp.b_disk_rbytes = tsamp.disk_rbytes;
1229		tsamp.p_disk_rbytes = tsamp.disk_rbytes;
1230
1231		tsamp.b_disk_wbytes = tsamp.disk_wbytes;
1232		tsamp.p_disk_wbytes = tsamp.disk_wbytes;
1233	}
1234
1235	retval = 0;
1236	RETURN:
1237	/* Release. */
1238	IOObjectRelease(drive_list);
1239	return retval;
1240}
1241
1242
1243/* Iterate through all processes and update their statistics. */
1244static int
1245libtop_p_proc_table_read(boolean_t reg)
1246{
1247	kern_return_t	kr;
1248	processor_set_name_array_t psets;
1249	processor_set_t	pset;
1250	task_array_t tasks;
1251	mach_msg_type_number_t	i, j, pcnt, tcnt;
1252	bool fatal_error_occurred = false;
1253
1254	kr = host_processor_sets(libtop_port, &psets, &pcnt);
1255	if (kr != KERN_SUCCESS) {
1256		libtop_print(libtop_user_data, "Error in host_processor_sets(): %s",
1257		    mach_error_string(kr));
1258		return -1;
1259	}
1260
1261	for (i = 0; i < pcnt; i++) {
1262		kr = host_processor_set_priv(libtop_port, psets[i], &pset);
1263		if (kr != KERN_SUCCESS) {
1264			libtop_print(libtop_user_data,
1265				"Error in host_processor_set_priv(): %s",
1266			    mach_error_string(kr));
1267			return -1;
1268		}
1269
1270		kr = processor_set_tasks(pset, &tasks, &tcnt);
1271		if (kr != KERN_SUCCESS) {
1272			libtop_print(libtop_user_data, "Error in processor_set_tasks(): %s",
1273			    mach_error_string(kr));
1274			return -1;
1275		}
1276
1277		tsamp.reg = 0;
1278		tsamp.rprvt = 0;
1279		tsamp.vsize = 0;
1280		tsamp.threads = 0;
1281//		libtop_p_oinfo_reset();
1282
1283		for (j = 0; j < tcnt; j++) {
1284			switch(libtop_p_task_update(tasks[j], reg)) {
1285				case LIBTOP_ERR_INVALID:
1286					/*
1287					 * The task became invalid.
1288					 * The called function takes care of destroying the pinfo in the tree, if needed.
1289					 */
1290					break;
1291
1292				case LIBTOP_ERR_ALLOC:
1293					fatal_error_occurred = true;
1294					break;
1295			}
1296
1297			mach_port_deallocate(mach_task_self(), tasks[j]);
1298		}
1299
1300		kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)tasks, tcnt * sizeof(*tasks));
1301		kr = mach_port_deallocate(mach_task_self(), pset);
1302		kr = mach_port_deallocate(mach_task_self(), psets[i]);
1303	}
1304
1305	kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)psets, pcnt * sizeof(*psets));
1306
1307	return (fatal_error_occurred) ? -1 : 0;
1308}
1309
1310/*
1311 * Return the CPU type of the process.
1312 */
1313static libtop_status_t
1314libtop_p_cputype(pid_t pid, cpu_type_t *cputype) {
1315	int res = -1;
1316	static int mib[CTL_MAXNAME];
1317	static size_t miblen = 0;
1318
1319	*cputype = 0;
1320
1321	if (miblen == 0) {
1322		miblen = CTL_MAXNAME;
1323		res = sysctlnametomib("sysctl.proc_cputype", mib, &miblen);
1324		if (res != 0) {
1325			miblen = 0;
1326		}
1327	}
1328
1329	if (miblen > 0) {
1330		mib[miblen] = pid;
1331		size_t len = sizeof(*cputype);
1332		res = sysctl(mib, miblen + 1, cputype, &len, NULL, 0);
1333	}
1334
1335	/* res will be 0 if the sysctl was successful. */
1336	return (res == 0) ? LIBTOP_NO_ERR : LIBTOP_ERR_INVALID;
1337}
1338
1339/*
1340 * Return the shared region size for an architecture.
1341 */
1342static mach_vm_size_t
1343libtop_p_shreg_size(cpu_type_t type) {
1344	switch(type)
1345	{
1346		case CPU_TYPE_ARM:			return SHARED_REGION_SIZE_ARM;
1347		case CPU_TYPE_POWERPC:		return SHARED_REGION_SIZE_PPC;
1348		case CPU_TYPE_POWERPC64:	return SHARED_REGION_SIZE_PPC64;
1349		case CPU_TYPE_I386:			return SHARED_REGION_SIZE_I386;
1350		case CPU_TYPE_X86_64:		return SHARED_REGION_SIZE_X86_64;
1351		default:					return 0;
1352	}
1353}
1354
1355static int __attribute__((noinline))
1356kinfo_for_pid(struct kinfo_proc* kinfo, pid_t pid) {
1357	size_t miblen = 4, len;
1358	int mib[miblen];
1359	int res;
1360
1361	mib[0] = CTL_KERN;
1362	mib[1] = KERN_PROC;
1363	mib[2] = KERN_PROC_PID;
1364	mib[3] = pid;
1365	len = sizeof(struct kinfo_proc);
1366	res = sysctl(mib, miblen, kinfo, &len, NULL, 0);
1367	if (res != 0) {
1368		libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
1369					 __FUNCTION__, strerror(errno));
1370	}
1371	return res;
1372}
1373
1374static kern_return_t
1375libtop_pinfo_update_boosts(task_t task, libtop_pinfo_t* pinfo) {
1376
1377#if !defined(TASK_POLICY_STATE_COUNT)
1378	struct task_policy_state {
1379		uint64_t requested;
1380		uint64_t effective;
1381		uint64_t pending;
1382		uint32_t imp_assertcnt;
1383		uint32_t imp_externcnt;
1384		uint64_t flags;
1385		uint64_t imp_transitions;
1386		uint64_t reserved[2];
1387	};
1388
1389	typedef struct task_policy_state *task_policy_state_t;
1390
1391#define TASK_POLICY_STATE_COUNT ((mach_msg_type_number_t) \
1392				 (sizeof (struct task_policy_state) / sizeof (integer_t)))
1393
1394	/* task policy state flags */
1395#define TASK_IMP_RECEIVER       0x00000001
1396#define TASK_IMP_DONOR          0x00000002
1397#define TASK_IMP_LIVE_DONOR     0x00000004
1398#endif
1399
1400	struct task_policy_state policy_state = {0};
1401	mach_msg_type_number_t count = TASK_POLICY_STATE_COUNT;
1402	boolean_t get_default = FALSE;
1403	kern_return_t kr;
1404
1405	pinfo->psamp.p_boosts = pinfo->psamp.boosts;
1406	pinfo->psamp.p_assertcnt = pinfo->psamp.assertcnt;
1407
1408	kr = task_policy_get(task, TASK_POLICY_STATE, (task_info_t)&policy_state, &count, &get_default);
1409	if (kr != KERN_SUCCESS) return kr;
1410
1411	pinfo->psamp.boosts = policy_state.imp_transitions;
1412	pinfo->psamp.assertcnt = policy_state.imp_assertcnt;
1413
1414	/*
1415	 * even if we aren't donating now,
1416	 * if we got boosted during the last seq,
1417	 * we must have been sometime during the seq.
1418	 */
1419	pinfo->psamp.boost_donating = (((policy_state.flags & TASK_IMP_DONOR) != 0) ||
1420				       (((policy_state.flags & TASK_IMP_RECEIVER) != 0) &&
1421					((policy_state.imp_assertcnt != 0) ||
1422					 (pinfo->psamp.boosts > pinfo->psamp.p_boosts))));
1423
1424	/*
1425	 * keep track of the last sequence we were seen donating:
1426	 * we'll use this to sort.
1427	 */
1428	if (pinfo->psamp.boost_donating && pinfo->psamp.p_boosts > 1) {
1429			pinfo->psamp.boost_last_donating_seq = pinfo->psamp.seq;
1430	}
1431
1432	/* Set initial values */
1433	if (pinfo->psamp.p_seq == 0) {
1434		pinfo->psamp.b_boosts = pinfo->psamp.boosts;
1435		pinfo->psamp.b_assertcnt = pinfo->psamp.assertcnt;
1436	}
1437
1438	return KERN_SUCCESS;
1439}
1440
1441
1442static kern_return_t
1443libtop_pinfo_update_mach_ports(task_t task, libtop_pinfo_t* pinfo) {
1444	kern_return_t kr;
1445	mach_msg_type_number_t ncnt, tcnt;
1446	mach_port_name_array_t names;
1447	mach_port_type_array_t types;
1448
1449	pinfo->psamp.p_prt = pinfo->psamp.prt;
1450
1451	kr = mach_port_names(task, &names, &ncnt, &types, &tcnt);
1452	if (kr != KERN_SUCCESS) return 0;
1453
1454	pinfo->psamp.prt = ncnt;
1455
1456	kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)names, ncnt * sizeof(*names));
1457	kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)types, tcnt * sizeof(*types));
1458
1459	return kr;
1460}
1461
1462static kern_return_t
1463libtop_pinfo_update_events_info(task_t task, libtop_pinfo_t* pinfo) {
1464	kern_return_t kr;
1465	mach_msg_type_number_t count = TASK_EVENTS_INFO_COUNT;
1466
1467	pinfo->psamp.p_events = pinfo->psamp.events;
1468
1469	pinfo->psamp.faults.previous = pinfo->psamp.faults.now;
1470	pinfo->psamp.pageins.previous = pinfo->psamp.pageins.now;
1471	pinfo->psamp.cow_faults.previous = pinfo->psamp.cow_faults.now;
1472	pinfo->psamp.messages_sent.previous = pinfo->psamp.messages_sent.now;
1473	pinfo->psamp.messages_recv.previous = pinfo->psamp.messages_recv.now;
1474	pinfo->psamp.syscalls_mach.previous = pinfo->psamp.syscalls_mach.now;
1475	pinfo->psamp.syscalls_bsd.previous = pinfo->psamp.syscalls_bsd.now;
1476	pinfo->psamp.csw.previous = pinfo->psamp.csw.now;
1477
1478	kr = task_info(task, TASK_EVENTS_INFO, (task_info_t)&pinfo->psamp.events, &count);
1479	if (kr != KERN_SUCCESS) return kr;
1480
1481
1482#if 0
1483	if(pinfo->psamp.pid == 0)  {
1484		/* Used for testing libtop_i64_*(). */
1485		static int i;
1486		pinfo->psamp.events.faults = ((INT_MAX - pinfo->psamp.events.faults) - 20000) + i;
1487
1488		/* This tests the libtop_i64_init initial overflow behavior. */
1489		pinfo->psamp.events.csw += INT_MAX;
1490
1491		i += INT_MAX / 2;
1492	}
1493#endif
1494
1495	if (pinfo->psamp.p_seq == 0) {
1496		pinfo->psamp.b_events = pinfo->psamp.events;
1497		pinfo->psamp.p_events = pinfo->psamp.events;
1498
1499
1500		pinfo->psamp.faults.i64 = libtop_i64_init(0, pinfo->psamp.events.faults);
1501		pinfo->psamp.pageins.i64 = libtop_i64_init(0, pinfo->psamp.events.pageins);
1502		pinfo->psamp.cow_faults.i64 = libtop_i64_init(0, pinfo->psamp.events.cow_faults);
1503		pinfo->psamp.messages_sent.i64 = libtop_i64_init(0, pinfo->psamp.events.messages_sent);
1504		pinfo->psamp.messages_recv.i64 = libtop_i64_init(0, pinfo->psamp.events.messages_received);
1505		pinfo->psamp.syscalls_mach.i64 = libtop_i64_init(0, pinfo->psamp.events.syscalls_mach);
1506		pinfo->psamp.syscalls_bsd.i64 = libtop_i64_init(0, pinfo->psamp.events.syscalls_unix);
1507		pinfo->psamp.csw.i64 = libtop_i64_init(0, pinfo->psamp.events.csw);
1508	}
1509
1510	libtop_i64_update(&pinfo->psamp.faults.i64, pinfo->psamp.events.faults);
1511	libtop_i64_update(&pinfo->psamp.pageins.i64, pinfo->psamp.events.pageins);
1512	libtop_i64_update(&pinfo->psamp.cow_faults.i64, pinfo->psamp.events.cow_faults);
1513	libtop_i64_update(&pinfo->psamp.messages_sent.i64, pinfo->psamp.events.messages_sent);
1514	libtop_i64_update(&pinfo->psamp.messages_recv.i64, pinfo->psamp.events.messages_received);
1515	libtop_i64_update(&pinfo->psamp.syscalls_mach.i64, pinfo->psamp.events.syscalls_mach);
1516	libtop_i64_update(&pinfo->psamp.syscalls_bsd.i64, pinfo->psamp.events.syscalls_unix);
1517	libtop_i64_update(&pinfo->psamp.csw.i64, pinfo->psamp.events.csw);
1518
1519	pinfo->psamp.faults.now = libtop_i64_value(&pinfo->psamp.faults.i64);
1520	pinfo->psamp.pageins.now = libtop_i64_value(&pinfo->psamp.pageins.i64);
1521	pinfo->psamp.cow_faults.now = libtop_i64_value(&pinfo->psamp.cow_faults.i64);
1522	pinfo->psamp.messages_sent.now = libtop_i64_value(&pinfo->psamp.messages_sent.i64);
1523	pinfo->psamp.messages_recv.now = libtop_i64_value(&pinfo->psamp.messages_recv.i64);
1524	pinfo->psamp.syscalls_mach.now = libtop_i64_value(&pinfo->psamp.syscalls_mach.i64);
1525	pinfo->psamp.syscalls_bsd.now = libtop_i64_value(&pinfo->psamp.syscalls_bsd.i64);
1526	pinfo->psamp.csw.now = libtop_i64_value(&pinfo->psamp.csw.i64);
1527
1528	return kr;
1529}
1530
1531static kern_return_t
1532libtop_pinfo_update_kernmem_info(task_t task, libtop_pinfo_t* pinfo) {
1533	kern_return_t kr;
1534
1535	mach_msg_type_number_t count = TASK_KERNELMEMORY_INFO_COUNT;
1536
1537	pinfo->psamp.p_palloc = pinfo->psamp.palloc;
1538	pinfo->psamp.p_pfree = pinfo->psamp.pfree;
1539	pinfo->psamp.p_salloc = pinfo->psamp.salloc;
1540	pinfo->psamp.p_sfree = pinfo->psamp.sfree;
1541
1542	kr = task_info(task, TASK_KERNELMEMORY_INFO, (task_info_t)&pinfo->psamp.palloc, &count);
1543	return kr;
1544}
1545
1546static kern_return_t
1547libtop_pinfo_update_power_info(task_t task, libtop_pinfo_t *pinfo)
1548{
1549	kern_return_t kr;
1550	mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
1551
1552	pinfo->psamp.p_power = pinfo->psamp.power;
1553
1554	kr = task_info(task, TASK_POWER_INFO, (task_info_t)&pinfo->psamp.power, &count);
1555	if (kr != KERN_SUCCESS) return kr;
1556
1557	if (pinfo->psamp.p_seq == 0) {
1558		pinfo->psamp.b_power = pinfo->psamp.power;
1559		pinfo->psamp.p_power = pinfo->psamp.power;
1560	}
1561
1562	return kr;
1563}
1564
1565#ifndef TASK_VM_INFO_PURGEABLE
1566// cribbed from sysmond
1567static uint64_t
1568sum_vm_purgeable_info(const vm_purgeable_info_t info)
1569{
1570	uint64_t sum = 0;
1571	int i;
1572
1573	for (i = 0; i < 8; i++) {
1574		sum += info->fifo_data[i].size;
1575	}
1576	sum += info->obsolete_data.size;
1577	for (i = 0; i < 8; i++) {
1578		sum += info->lifo_data[i].size;
1579	}
1580
1581	return sum;
1582}
1583#endif /* !TASK_VM_INFO_PURGEABLE */
1584
1585static kern_return_t
1586libtop_pinfo_update_vm_info(task_t task, libtop_pinfo_t *pinfo)
1587{
1588	kern_return_t kr;
1589#ifndef TASK_VM_INFO_PURGEABLE
1590	task_purgable_info_t purgeable_info;
1591	uint64_t purgeable_sum = 0;
1592#endif /* !TASK_VM_INFO_PURGEABLE */
1593	mach_msg_type_number_t info_count;
1594	task_vm_info_data_t vm_info;
1595
1596	pinfo->psamp.p_purgeable = pinfo->psamp.purgeable;
1597	pinfo->psamp.p_anonymous = pinfo->psamp.anonymous;
1598	pinfo->psamp.p_compressed = pinfo->psamp.compressed;
1599
1600#ifndef TASK_VM_INFO_PURGEABLE
1601	kr = task_purgable_info(task, &purgeable_info);
1602	if (kr == KERN_SUCCESS) {
1603		purgeable_sum = sum_vm_purgeable_info(&purgeable_info);
1604		pinfo->psamp.purgeable = purgeable_sum;
1605	}
1606#endif /* !TASK_VM_INFO_PURGEABLE */
1607
1608	info_count = TASK_VM_INFO_COUNT;
1609#ifdef TASK_VM_INFO_PURGEABLE
1610	kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
1611#else
1612	kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count);
1613#endif
1614	if (kr == KERN_SUCCESS) {
1615#ifdef TASK_VM_INFO_PURGEABLE
1616		pinfo->psamp.purgeable = vm_info.purgeable_volatile_resident;
1617		pinfo->psamp.anonymous = vm_info.internal - vm_info.purgeable_volatile_pmap;
1618#else
1619		if (purgeable_sum < vm_info.internal) {
1620			pinfo->psamp.anonymous = vm_info.internal - purgeable_sum;
1621		} else {
1622			/* radar:13816348 */
1623			pinfo->psamp.anonymous = 0;
1624		}
1625#endif
1626		pinfo->psamp.compressed = vm_info.compressed;
1627	}
1628
1629	return kr;
1630}
1631
1632static kern_return_t
1633libtop_pinfo_update_cpu_usage(task_t task, libtop_pinfo_t* pinfo, int *state) {
1634	kern_return_t kr;
1635	thread_act_array_t threads;
1636	mach_msg_type_number_t tcnt;
1637
1638	// Update thread status
1639
1640	*state = LIBTOP_STATE_MAX;
1641	pinfo->psamp.state = LIBTOP_STATE_MAX;
1642
1643	kr = task_threads(task, &threads, &tcnt);
1644	if (kr != KERN_SUCCESS) return kr;
1645
1646	if(tsamp.seq > 1) {
1647		pinfo->psamp.p_th = pinfo->psamp.th;
1648		pinfo->psamp.p_running_th = pinfo->psamp.running_th;
1649	} else {
1650		pinfo->psamp.p_th = 0;
1651		pinfo->psamp.p_running_th = 0;
1652	}
1653
1654	pinfo->psamp.th = tcnt;
1655	tsamp.threads += tcnt;
1656
1657	pinfo->psamp.running_th = 0;
1658
1659	int i;
1660	for (i = 0; i < tcnt; i++) {
1661		thread_basic_info_data_t info;
1662		mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
1663
1664		kr = thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&info, &count);
1665		if (kr != KERN_SUCCESS) continue;
1666
1667		if ((info.flags & TH_FLAGS_IDLE) == 0) {
1668			struct timeval tv;
1669			TIME_VALUE_TO_TIMEVAL(&info.user_time, &tv);
1670			timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time);
1671			TIME_VALUE_TO_TIMEVAL(&info.system_time, &tv);
1672			timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time);
1673		}
1674
1675		if(info.run_state == TH_STATE_RUNNING) {
1676			pinfo->psamp.running_th++;
1677		}
1678
1679		uint32_t tstate;
1680		tstate = libtop_p_mach_state_order(info.run_state, info.sleep_time);
1681		if (tstate < *state) {
1682			*state = tstate;
1683			pinfo->psamp.state = tstate;
1684		}
1685
1686		kr = mach_port_deallocate(mach_task_self(), threads[i]);
1687	}
1688	kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, tcnt * sizeof(*threads));
1689	return kr;
1690}
1691
1692#ifdef TOP_DEBUG_VM
1693static char*
1694share_mode_to_string(int share_mode) {
1695	switch(share_mode) {
1696		case SM_COW: return "COW";
1697		case SM_PRIVATE: return "PRV";
1698		case SM_EMPTY: return "NUL";
1699		case SM_SHARED: return "ALI";
1700		default: return "???";
1701	}
1702}
1703#endif
1704
1705static kern_return_t
1706libtop_update_vm_regions(task_t task, libtop_pinfo_t* pinfo) {
1707	kern_return_t kr;
1708
1709	mach_vm_size_t rprvt = 0;
1710	mach_vm_size_t vprvt = 0;
1711	mach_vm_size_t rshrd = 0;
1712	mach_vm_size_t empty = 0;
1713	mach_vm_size_t aliased = 0;
1714	mach_vm_size_t fw_private = 0;
1715	mach_vm_size_t pagesize = tsamp.pagesize;
1716	mach_vm_size_t regs = 0;
1717
1718	mach_vm_address_t addr;
1719	mach_vm_size_t size;
1720
1721	libtop_oinfo_t *oinfo;
1722
1723	pinfo->split = FALSE;
1724
1725#ifdef TOP_DEBUG_VM
1726	fprintf(stderr, "pid %d\n", pinfo->psamp.pid);
1727	fprintf(stderr, "range\tsize\tprivate\tshared\trc\toid\tmode\tvprvt\trprvt\trshrd\tempty\taliased\n");
1728#endif
1729
1730	for (addr = 0; ; addr += size) {
1731		vm_region_top_info_data_t info;
1732		mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
1733		mach_port_t object_name;
1734
1735		kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
1736		if (kr != KERN_SUCCESS) break;
1737
1738#ifdef TOP_DEBUG_VM
1739		fprintf(stderr, "%016llx-%016llx\t%lld\t%d\t%d\t%d\t%d\t%s",
1740				addr, addr+size, size, info.private_pages_resident, info.shared_pages_resident, info.ref_count, info.obj_id, share_mode_to_string(info.share_mode));
1741#endif
1742
1743		if (in_shared_region(addr, pinfo->psamp.cputype)) {
1744			// Private Shared
1745			fw_private += info.private_pages_resident * pagesize;
1746
1747			/*
1748			 * Check if this process has the globally shared
1749			 * text and data regions mapped in.  If so, set
1750			 * pinfo->split to TRUE and avoid checking
1751			 * again.
1752			 */
1753			if (pinfo->split == FALSE && info.share_mode == SM_EMPTY) {
1754				vm_region_basic_info_data_64_t	b_info;
1755				mach_vm_address_t b_addr = addr;
1756				mach_vm_size_t b_size = size;
1757				count = VM_REGION_BASIC_INFO_COUNT_64;
1758
1759				kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name);
1760				if (kr != KERN_SUCCESS) break;
1761
1762				if (b_info.reserved) {
1763					pinfo->split = TRUE;
1764				}
1765			}
1766
1767			/*
1768			 * Short circuit the loop if this isn't a shared
1769			 * private region, since that's the only region
1770			 * type we care about within the current address
1771			 * range.
1772			 */
1773			if (info.share_mode != SM_PRIVATE) {
1774#ifdef TOP_DEBUG_VM
1775				fprintf(stderr, "\n");
1776#endif
1777				continue;
1778			}
1779		}
1780
1781		regs++;
1782
1783		/*
1784		 * Update counters according to the region type.
1785		 */
1786
1787		if (info.share_mode == SM_COW && info.ref_count == 1) {
1788			// Treat single reference SM_COW as SM_PRIVATE
1789			info.share_mode = SM_PRIVATE;
1790		}
1791
1792		switch (info.share_mode) {
1793			case SM_LARGE_PAGE:
1794				// Treat SM_LARGE_PAGE the same as SM_PRIVATE
1795				// since they are not shareable and are wired.
1796			case SM_PRIVATE:
1797				rprvt += info.private_pages_resident * pagesize;
1798				rprvt += info.shared_pages_resident * pagesize;
1799				vprvt += size;
1800				break;
1801
1802			case SM_EMPTY:
1803				empty += size;
1804				break;
1805
1806			case SM_COW:
1807			case SM_SHARED:
1808				if (pinfo->psamp.pid == 0) {
1809					// Treat kernel_task specially
1810					if (info.share_mode == SM_COW) {
1811						rprvt += info.private_pages_resident * pagesize;
1812						vprvt += size;
1813					}
1814					break;
1815				}
1816
1817				oinfo = libtop_p_oinfo_insert(info.obj_id, info.share_mode, info.shared_pages_resident, info.ref_count, size, pinfo);
1818				if (!oinfo) {
1819					return -1;
1820				}
1821
1822				// roll back if necessary
1823				if (oinfo->proc_ref_count > 1) {
1824					if (oinfo->rb_share_type != SM_EMPTY) {
1825						oinfo->share_type = oinfo->rb_share_type;
1826					}
1827					aliased -= oinfo->rb_aliased;
1828					vprvt -= oinfo->rb_vprvt;
1829					rshrd -= oinfo->rb_rshrd;
1830				}
1831				// clear rollback fields
1832				oinfo->rb_share_type = SM_EMPTY;
1833				oinfo->rb_aliased = 0;
1834				oinfo->rb_vprvt = 0;
1835				oinfo->rb_rshrd = 0;
1836
1837				// XXX: SM_SHARED make sense here for the SM_COW case?
1838				if (oinfo->share_type == SM_SHARED && oinfo->ref_count == oinfo->proc_ref_count) {
1839					// Private aliased object
1840					oinfo->rb_share_type = oinfo->share_type;
1841					oinfo->share_type = SM_PRIVATE_ALIASED;
1842
1843					oinfo->rb_aliased += oinfo->resident_page_count * pagesize;
1844					aliased += oinfo->resident_page_count * pagesize;
1845
1846					oinfo->rb_vprvt += oinfo->size;
1847					vprvt += oinfo->size;
1848				}
1849
1850				if (oinfo->share_type != SM_PRIVATE_ALIASED) {
1851					oinfo->rb_rshrd += oinfo->resident_page_count * pagesize;
1852					rshrd += oinfo->resident_page_count * pagesize;
1853				}
1854
1855				if (info.share_mode == SM_COW) {
1856					rprvt += info.private_pages_resident * pagesize;
1857					vprvt += info.private_pages_resident * pagesize; // XXX: size?
1858				}
1859				break;
1860
1861			default:
1862				assert(0);
1863				break;
1864		}
1865#ifdef TOP_DEBUG_VM
1866		fprintf(stderr, "\t%lld\t%lld\t%lld\t%lld\t%lld\n", vprvt, rprvt, rshrd, empty, aliased);
1867#endif
1868	}
1869
1870	pinfo->psamp.rprvt = rprvt;
1871	pinfo->psamp.vprvt = vprvt;
1872	pinfo->psamp.rshrd = rshrd;
1873	pinfo->psamp.empty = empty;
1874	pinfo->psamp.rprvt += aliased;
1875	pinfo->psamp.fw_private = fw_private;
1876
1877	if(tsamp.seq > 1) {
1878		pinfo->psamp.p_reg = pinfo->psamp.reg;
1879	} else {
1880		pinfo->psamp.p_reg = 0;
1881	}
1882	pinfo->psamp.reg = regs;
1883
1884	return kr;
1885}
1886
1887/*
1888 * This may destroy the pinfo associated with a pid/task.
1889 * The caller should not make assumptions about the lifetime of the pinfo.
1890 */
1891static libtop_status_t __attribute__((noinline))
1892libtop_p_task_update(task_t task, boolean_t reg)
1893{
1894	kern_return_t kr;
1895	int res;
1896	struct kinfo_proc	kinfo;
1897	pid_t pid;
1898	mach_msg_type_number_t	count;
1899	libtop_pinfo_t		*pinfo;
1900	struct task_basic_info_64	ti;
1901	int			state;
1902
1903	state = LIBTOP_STATE_ZOMBIE;
1904
1905	/* Get pid for this task. */
1906	kr = pid_for_task(task, &pid);
1907	if (kr != KERN_SUCCESS) {
1908		return LIBTOP_ERR_INVALID;
1909	}
1910
1911	res = kinfo_for_pid(&kinfo, pid);
1912	if (res != 0) {
1913		return LIBTOP_ERR_INVALID;
1914	}
1915
1916	if (kinfo.kp_proc.p_stat == SZOMB) {
1917		/* Zombie process. */
1918	}
1919
1920	/*
1921	 * Search for the process.  If we haven't seen it before, allocate and
1922	 * insert a new pinfo structure.
1923	 */
1924	pinfo = libtop_p_psearch((pid_t)pid);
1925	if (pinfo == NULL) {
1926		pinfo = (libtop_pinfo_t *)calloc(1, sizeof(libtop_pinfo_t));
1927		if (pinfo == NULL) {
1928			return LIBTOP_ERR_ALLOC;
1929		}
1930		pinfo->psamp.pid = (pid_t)pid;
1931		libtop_p_pinsert(pinfo);
1932	}
1933
1934	/*
1935	 * Get command name/args.
1936	 * This is expected to return a LIBTOP_ERR* or LIBTOP_NO_ERR (when successful).
1937	 */
1938	res = libtop_p_proc_command(pinfo, &kinfo);
1939	switch(res) {
1940		case LIBTOP_NO_ERR:
1941			break;
1942
1943		case LIBTOP_ERR_INVALID:
1944			/*
1945			 * Something failed while fetching the command string (the pid is probably gone).
1946			 * Remove the pinfo struct from the tree, and free it with destroy_pinfo.
1947			 */
1948			libtop_p_destroy_pinfo(pinfo);
1949			return res;
1950
1951		case LIBTOP_ERR_ALLOC:
1952			return res;
1953	}
1954
1955	pinfo->psamp.uid = kinfo.kp_eproc.e_ucred.cr_uid;
1956	pinfo->psamp.ppid = kinfo.kp_eproc.e_ppid;
1957	pinfo->psamp.pgrp = kinfo.kp_eproc.e_pgid;
1958	pinfo->flag = kinfo.kp_proc.p_flag;
1959	pinfo->psamp.started = kinfo.kp_proc.p_starttime;
1960
1961	pinfo->psamp.p_seq = pinfo->psamp.seq;
1962	pinfo->psamp.seq = tsamp.seq;
1963
1964	/*
1965	 * 6255752: CPU type of a process can change due to a re-exec.
1966	 * We may see some transient oddities due to this behavior, but
1967	 * it should stabilize after one refresh.
1968	 */
1969	if(LIBTOP_NO_ERR != libtop_p_cputype(pinfo->psamp.pid, &pinfo->psamp.cputype)) {
1970		/*
1971		 * This pid is most likely invalid now, because the
1972		 * cputype couldn't be found.
1973		 *
1974		 */
1975
1976		libtop_p_destroy_pinfo(pinfo);
1977		return LIBTOP_ERR_INVALID;
1978	}
1979
1980	/*
1981	 * Get task_info, which is used for memory usage and CPU usage
1982	 * statistics.
1983	 */
1984	count = TASK_BASIC_INFO_64_COUNT;
1985	kr = task_info(task, TASK_BASIC_INFO_64, (task_info_t)&ti, &count);
1986	if (kr != KERN_SUCCESS) {
1987		libtop_p_destroy_pinfo(pinfo);
1988		return LIBTOP_ERR_INVALID;
1989	}
1990
1991	/*
1992	 * Get memory usage statistics.
1993	 */
1994
1995	/* Make copies of previous sample values. */
1996	pinfo->psamp.p_rsize = pinfo->psamp.rsize;
1997	pinfo->psamp.p_vsize = pinfo->psamp.vsize;
1998	pinfo->psamp.p_rprvt = pinfo->psamp.rprvt;
1999	pinfo->psamp.p_vprvt = pinfo->psamp.vprvt;
2000	pinfo->psamp.p_rshrd = pinfo->psamp.rshrd;
2001	pinfo->psamp.p_empty = pinfo->psamp.empty;
2002
2003	/* Clear sizes in preparation for determining their current values. */
2004	//pinfo->psamp.rprvt = 0;
2005	//pinfo->psamp.vprvt = 0;
2006	//pinfo->psamp.rshrd = 0;
2007	//pinfo->psamp.empty = 0;
2008	//pinfo->psamp.reg = 0;
2009	//pinfo->psamp.fw_private = 0;
2010
2011	/*
2012	 * Do memory object traversal if any of the following is true:
2013	 *
2014	 * 1) Region reporting is enabled for this sample, and it isn't
2015	 *    explicitly disabled for this process.
2016	 *
2017	 * 2) Region reporting is explicitly enabled for this process.
2018	 *
2019	 * 3) This is the first sample for this process.
2020	 *
2021	 * 4) A previous sample detected that the globally shared text and data
2022	 *    segments were mapped in, but if we were to subtract them out,
2023	 *    the process's calculated vsize would be less than 0.
2024	 */
2025
2026	if ((reg && pinfo->preg != LIBTOP_PREG_off)
2027		|| pinfo->preg == LIBTOP_PREG_on
2028		|| pinfo->psamp.p_seq == 0
2029		|| (pinfo->split && ti.virtual_size
2030		    < libtop_p_shreg_size(pinfo->psamp.cputype))) {
2031
2032		libtop_update_vm_regions(task, pinfo);
2033	}
2034
2035	/*
2036	 * These need to be copied regardless of the region reporting above.
2037	 * The previous (p_) values were copied earlier.
2038	 */
2039	pinfo->psamp.rsize = ti.resident_size;
2040	pinfo->psamp.vsize = ti.virtual_size;
2041
2042	// Update total time.
2043	pinfo->psamp.p_total_time = pinfo->psamp.total_time;
2044
2045	//Store the previous on-behalf CPU time
2046	pinfo->psamp.p_cpu_billed_to_me = pinfo->psamp.cpu_billed_to_me;
2047	pinfo->psamp.p_cpu_billed_to_others = pinfo->psamp.cpu_billed_to_others;
2048
2049	//Update the on-behalf CPU time
2050	struct rusage_info_v3 ri;
2051	proc_pid_rusage(pid, RUSAGE_INFO_V3, (rusage_info_t)&ri);
2052	pinfo->psamp.cpu_billed_to_me = ri.ri_billed_system_time;
2053	pinfo->psamp.cpu_billed_to_others = ri.ri_serviced_system_time;
2054
2055	if (pinfo->psamp.p_seq == 0) {
2056		/* Set initial values. */
2057		pinfo->psamp.b_cpu_billed_to_me = pinfo->psamp.cpu_billed_to_me;
2058		pinfo->psamp.b_cpu_billed_to_others = pinfo->psamp.cpu_billed_to_others;
2059		pinfo->psamp.p_cpu_billed_to_me = pinfo->psamp.cpu_billed_to_me;
2060		pinfo->psamp.p_cpu_billed_to_others = pinfo->psamp.cpu_billed_to_others;
2061	}
2062
2063	/*
2064	 * Get boost transition counts
2065	 */
2066	kr = libtop_pinfo_update_boosts(task, pinfo);
2067
2068	struct timeval tv;
2069	TIME_VALUE_TO_TIMEVAL(&ti.user_time, &pinfo->psamp.total_time);
2070	TIME_VALUE_TO_TIMEVAL(&ti.system_time, &tv);
2071	timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time);
2072
2073	/*
2074	 * Get CPU usage statistics.
2075	 */
2076	kr = libtop_pinfo_update_cpu_usage(task, pinfo, &state);
2077
2078	if (pinfo->psamp.p_seq == 0) {
2079		/* Set initial values. */
2080		pinfo->psamp.b_total_time = pinfo->psamp.total_time;
2081		pinfo->psamp.p_total_time = pinfo->psamp.total_time;
2082	}
2083
2084	/*
2085	 * Get number of Mach ports.
2086	 */
2087	kr = libtop_pinfo_update_mach_ports(task, pinfo);
2088
2089	/*
2090	 * Get event counters.
2091	 */
2092	kr = libtop_pinfo_update_events_info(task, pinfo);
2093
2094	/*
2095	 * Get updated wired memory usage numbers
2096	 */
2097	kr = libtop_pinfo_update_kernmem_info(task, pinfo);
2098
2099	/*
2100	 * Get power info (platform idle wakeups)
2101	 */
2102	kr = libtop_pinfo_update_power_info(task, pinfo);
2103
2104	/*
2105	 * Get VM info (anonymous, purgeable, and compressed memory).
2106	 */
2107	kr = libtop_pinfo_update_vm_info(task, pinfo);
2108
2109	libtop_p_wq_update(pinfo);
2110
2111	tsamp.state_breakdown[state]++;
2112
2113	return LIBTOP_NO_ERR;
2114}
2115
2116/*
2117 * Get the command name for the process associated with pinfo.  For CFM
2118 * applications, this requires substantial extra work, since the basename of the
2119 * first program argument is the actual command name.
2120 *
2121 * Due to limitations in the KERN_PROCARGS sysctl as implemented in OS X 10.2,
2122 * changes were made to the sysctl to make finding the process arguments more
2123 * deterministic.  If TOP_JAGUAR is defined, the old algorithm is used, rather
2124 * than the simpler new one.
2125 */
2126static libtop_status_t
2127libtop_p_proc_command(libtop_pinfo_t *pinfo, struct kinfo_proc *kinfo)
2128{
2129#if 0
2130	uint32_t bufferSize = 30;
2131	int result;
2132
2133	if (pinfo->psamp.command) {
2134		free(pinfo->psamp.command);
2135		pinfo->psamp.command = NULL;
2136	}
2137
2138	pinfo->psamp.command = malloc(bufferSize);
2139	if (NULL == pinfo->psamp.command)
2140		return LIBTOP_ERR_ALLOC;
2141
2142	pinfo->psamp.command[0] = '\0';
2143	proc_name(pinfo->psamp.pid, pinfo->psamp.command, bufferSize);
2144#if 0
2145	if(result) {
2146		pinfo->psamp.command[0] = '\0';
2147		return LIBTOP_ERR_INVALID;
2148	}
2149#endif
2150
2151	return 0;
2152#endif
2153
2154
2155	uint32_t	len;
2156
2157	if (pinfo->psamp.command) {
2158		free(pinfo->psamp.command);
2159		pinfo->psamp.command = NULL;
2160	}
2161
2162	len = strlen(kinfo->kp_proc.p_comm);
2163
2164	if (strncmp(kinfo->kp_proc.p_comm, "LaunchCFMApp",len) != 0) {
2165		/* Normal program. */
2166		pinfo->psamp.command = strdup(kinfo->kp_proc.p_comm);
2167		if (!pinfo->psamp.command) {
2168			return LIBTOP_ERR_ALLOC;
2169		}
2170	} else {
2171		int	mib[3];
2172		size_t	procargssize;
2173#ifdef TOP_JAGUAR
2174		char 	*arg_end, *exec_path;
2175		int	*ip;
2176#else
2177		char	*cp;
2178#endif
2179		char	*command_beg, *command, *command_end;
2180
2181		assert(pinfo->psamp.pid != 0);
2182
2183		/*
2184		 * CFM application.  Get the basename of the first argument and
2185		 * use that as the command string.
2186		 */
2187
2188		/*
2189		 * Make a sysctl() call to get the raw argument space of the
2190		 * process.  The layout is documented in start.s, which is part
2191		 * of the Csu project.  In summary, it looks like:
2192		 *
2193		 * /---------------\ 0x00000000
2194		 * :               :
2195		 * :               :
2196		 * |---------------|
2197		 * | argc          |
2198		 * |---------------|
2199		 * | arg[0]        |
2200		 * |---------------|
2201		 * :               :
2202		 * :               :
2203		 * |---------------|
2204		 * | arg[argc - 1] |
2205		 * |---------------|
2206		 * | 0             |
2207		 * |---------------|
2208		 * | env[0]        |
2209		 * |---------------|
2210		 * :               :
2211		 * :               :
2212		 * |---------------|
2213		 * | env[n]        |
2214		 * |---------------|
2215		 * | 0             |
2216		 * |---------------| <-- Beginning of data returned by sysctl()
2217		 * | exec_path     |     is here.
2218		 * |:::::::::::::::|
2219		 * |               |
2220		 * | String area.  |
2221		 * |               |
2222		 * |---------------| <-- Top of stack.
2223		 * :               :
2224		 * :               :
2225		 * \---------------/ 0xffffffff
2226		 */
2227		mib[0] = CTL_KERN;
2228		mib[1] = KERN_PROCARGS2;
2229		mib[2] = pinfo->psamp.pid;
2230
2231		procargssize = libtop_argmax;
2232#ifdef TOP_JAGUAR
2233		/* Hack to avoid kernel bug. */
2234		if (procargssize > 8192) {
2235			procargssize = 8192;
2236		}
2237#endif
2238		if (sysctl(mib, 3, libtop_arg, &procargssize, NULL, 0) == -1) {
2239			libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
2240			    __FUNCTION__, strerror(errno));
2241			/* sysctl probably failed due to an invalid pid. */
2242			return LIBTOP_ERR_INVALID;
2243		}
2244
2245#ifdef TOP_JAGUAR
2246		/* Set ip just above the end of libtop_arg. */
2247		arg_end = &libtop_arg[procargssize];
2248		ip = (int *)arg_end;
2249
2250		/*
2251		 * Skip the last 2 words, since the last is a 0 word, and
2252		 * the second to last may be as well, if there are no
2253		 * arguments.
2254		 */
2255		ip -= 3;
2256
2257		/* Iterate down the arguments until a 0 word is found. */
2258		for (; *ip != 0; ip--) {
2259			if (ip == (int *)libtop_arg) {
2260				goto ERROR;
2261			}
2262		}
2263
2264		/* The saved exec_path is just above the 0 word. */
2265		ip++;
2266		exec_path = (char *)ip;
2267
2268		/*
2269		 * Get the beginning of the first argument.  It is word-aligned,
2270		 * so skip padding '\0' bytes.
2271		 */
2272		command_beg = exec_path + strlen(exec_path);
2273		for (; *command_beg == '\0'; command_beg++) {
2274			if (command_beg >= arg_end) {
2275				goto ERROR;
2276			}
2277		}
2278
2279		/* Get the basename of command. */
2280		command = command_end = command_beg + strlen(command_beg) + 1;
2281		for (command--; command >= command_beg; command--) {
2282			if (*command == '/') {
2283				break;
2284			}
2285		}
2286		command++;
2287
2288		/* Allocate space for the command and copy. */
2289		len = command_end - command;
2290		pinfo->psamp.command = (char *)malloc(len + 1);
2291		if (pinfo->psamp.command == NULL) {
2292			return LIBTOP_ERR_ALLOC;
2293		}
2294		memcpy(pinfo->psamp.command, command, len + 1);
2295#else
2296		/* Skip the saved exec_path. */
2297		for (cp = libtop_arg; cp < &libtop_arg[procargssize]; cp++) {
2298			if (*cp == '\0') {
2299				/* End of exec_path reached. */
2300				break;
2301			}
2302		}
2303		if (cp == &libtop_arg[procargssize]) {
2304			goto ERROR;
2305		}
2306
2307		/* Skip trailing '\0' characters. */
2308		for (; cp < &libtop_arg[procargssize]; cp++) {
2309			if (*cp != '\0') {
2310				/* Beginning of first argument reached. */
2311				break;
2312			}
2313		}
2314		if (cp == &libtop_arg[procargssize]) {
2315			goto ERROR;
2316		}
2317		command_beg = cp;
2318
2319		/*
2320		 * Make sure that the command is '\0'-terminated.  This protects
2321		 * against malicious programs; under normal operation this never
2322		 * ends up being a problem..
2323		 */
2324		for (; cp < &libtop_arg[procargssize]; cp++) {
2325			if (*cp == '\0') {
2326				/* End of first argument reached. */
2327				break;
2328			}
2329		}
2330		if (cp == &libtop_arg[procargssize]) {
2331			goto ERROR;
2332		}
2333		command_end = command = cp;
2334
2335		/* Get the basename of command. */
2336		for (command--; command >= command_beg; command--) {
2337			if (*command == '/') {
2338				command++;
2339				break;
2340			}
2341		}
2342
2343		/* Allocate space for the command and copy. */
2344		len = command_end - command;
2345		pinfo->psamp.command = (char *)malloc(len + 1);
2346		if (pinfo->psamp.command == NULL) {
2347			return LIBTOP_ERR_ALLOC;
2348		}
2349		memcpy(pinfo->psamp.command, command, len + 1);
2350#endif
2351	}
2352
2353	return LIBTOP_NO_ERR;
2354
2355ERROR:
2356	/*
2357	 * Unable to parse the arguments.  Set the command name to
2358	 * "(LaunchCFMApp)".
2359	 */
2360	pinfo->psamp.command = strdup("(LaunchCFMApp)");
2361	return LIBTOP_NO_ERR;
2362}
2363
2364/* Insert a pinfo structure into the pid-ordered tree. */
2365static void
2366libtop_p_pinsert(libtop_pinfo_t *pinfo)
2367{
2368	rb_node_new(&libtop_ptree, pinfo, pnode);
2369	rb_insert(&libtop_ptree, pinfo, libtop_p_pinfo_pid_comp,
2370	    libtop_pinfo_t, pnode);
2371}
2372
2373/* Remove a pinfo structure from the pid-ordered tree. */
2374static void
2375libtop_p_premove(libtop_pinfo_t *pinfo)
2376{
2377	rb_remove(&libtop_ptree, pinfo, libtop_pinfo_t, pnode);
2378}
2379
2380/* Search for a pinfo structure with pid pid. */
2381static libtop_pinfo_t *
2382libtop_p_psearch(pid_t pid)
2383{
2384	libtop_pinfo_t	*retval, key;
2385
2386	key.psamp.pid = pid;
2387	rb_search(&libtop_ptree, &key, libtop_p_pinfo_pid_comp, pnode, retval);
2388	if (retval == rb_tree_nil(&libtop_ptree)) {
2389		retval = NULL;
2390	}
2391
2392	return retval;
2393}
2394
2395/*
2396 * Compare two pinfo structures according to pid.  This function is used for
2397 * operations on the pid-sorted tree of pinfo structures.
2398 */
2399static int
2400libtop_p_pinfo_pid_comp(libtop_pinfo_t *a, libtop_pinfo_t *b)
2401{
2402	assert(a != NULL);
2403	assert(b != NULL);
2404	if (a->psamp.pid < b->psamp.pid) return -1;
2405	if (a->psamp.pid > b->psamp.pid) return 1;
2406	return 0;
2407}
2408
2409/* Process comparison wrapper function, used by the red-black tree code. */
2410static int
2411libtop_p_pinfo_comp(libtop_pinfo_t *a, libtop_pinfo_t *b)
2412{
2413	return libtop_sort(libtop_sort_data, &a->psamp, &b->psamp);
2414}
2415
2416/* Insert or update a memory object info entry. */
2417static libtop_oinfo_t *
2418libtop_p_oinfo_insert(int obj_id, int share_type, int resident_page_count,
2419    int ref_count, int size, libtop_pinfo_t *pinfo)
2420{
2421	const void *k = (const void *)(intptr_t)obj_id;
2422	libtop_oinfo_t *oinfo = (libtop_oinfo_t *)CFDictionaryGetValue(libtop_oinfo_hash, k);
2423	int clear_rollback = 0;
2424
2425	if (oinfo != NULL) {
2426		/* Use existing record. */
2427		if (oinfo->pinfo != pinfo) {
2428			oinfo->proc_ref_count = 0;
2429			oinfo->pinfo = pinfo;
2430			clear_rollback = 1;
2431		}
2432
2433		oinfo->size += size;
2434		oinfo->proc_ref_count++;
2435	} else {
2436		oinfo = (libtop_oinfo_t *)malloc(sizeof(libtop_oinfo_t));
2437
2438		if (oinfo == NULL)
2439			return NULL;
2440
2441		oinfo->pinfo = pinfo;
2442		oinfo->obj_id = obj_id;
2443		oinfo->size = size;
2444		oinfo->share_type = share_type;
2445		oinfo->resident_page_count = resident_page_count;
2446		oinfo->ref_count = ref_count;
2447		oinfo->proc_ref_count = 1;
2448		clear_rollback = 1;
2449
2450		CFDictionarySetValue(libtop_oinfo_hash, k, oinfo);
2451	}
2452
2453	if (clear_rollback) {
2454		// clear rollback fields
2455		oinfo->rb_share_type = SM_EMPTY;
2456		oinfo->rb_aliased = 0;
2457		oinfo->rb_vprvt = 0;
2458		oinfo->rb_rshrd = 0;
2459	}
2460
2461	return oinfo;
2462}
2463
2464static void
2465libtop_p_destroy_pinfo(libtop_pinfo_t *pinfo) {
2466	libtop_p_premove(pinfo);
2467
2468	if(pinfo->psamp.command) {
2469		free(pinfo->psamp.command);
2470		/* Guard against reuse even though we are freeing. */
2471		pinfo->psamp.command = NULL;
2472	}
2473
2474	free(pinfo);
2475}
2476
2477static int
2478libtop_p_wq_update(libtop_pinfo_t *pinfo) {
2479#ifdef PROC_PIDWORKQUEUEINFO
2480	struct proc_workqueueinfo wqinfo;
2481	int result;
2482#endif
2483
2484	if (tsamp.seq > 1) {
2485		pinfo->psamp.p_wq_nthreads = pinfo->psamp.wq_nthreads;
2486		pinfo->psamp.p_wq_run_threads = pinfo->psamp.wq_run_threads;
2487		pinfo->psamp.p_wq_blocked_threads = pinfo->psamp.wq_blocked_threads;
2488	}
2489
2490	pinfo->psamp.wq_nthreads = 0;
2491	pinfo->psamp.wq_run_threads = 0;
2492	pinfo->psamp.wq_blocked_threads = 0;
2493
2494#ifdef PROC_PIDWORKQUEUEINFO
2495	result = proc_pidinfo(pinfo->psamp.pid, PROC_PIDWORKQUEUEINFO,
2496			/*arg is UNUSED with this flavor*/ 0ULL,
2497			&wqinfo, PROC_PIDWORKQUEUEINFO_SIZE);
2498	if(!result)
2499		return -1; /*error*/
2500
2501	pinfo->psamp.wq_nthreads = wqinfo.pwq_nthreads;
2502	pinfo->psamp.wq_run_threads = wqinfo.pwq_runthreads;
2503	pinfo->psamp.wq_blocked_threads = wqinfo.pwq_blockedthreads;
2504#endif
2505
2506	return 0;
2507}
2508
2509libtop_i64_t
2510libtop_i64_init(uint64_t acc, int last_value) {
2511	libtop_i64_t r;
2512
2513	r.accumulator = acc;
2514	r.last_value = last_value;
2515
2516	if(0 == acc) {
2517		if(last_value >= 0) {
2518			r.accumulator += last_value;
2519		} else {
2520			/* The initial value has already overflowed. */
2521			r.accumulator += INT_MAX;
2522			r.accumulator += abs(INT_MIN - last_value) + 1;
2523		}
2524	}
2525
2526	return r;
2527}
2528
2529void
2530libtop_i64_update(struct libtop_i64 *i, int value) {
2531	int delta = 0;
2532
2533	if(value >= 0) {
2534		if(i->last_value >= 0) {
2535			delta = value - i->last_value;
2536		} else {
2537			delta = value + (-i->last_value);
2538		}
2539	} else {
2540		if(i->last_value >= 0) {
2541			delta = abs(INT_MIN - value) + 1;
2542			delta += INT_MAX - i->last_value;
2543		} else {
2544			delta = value - i->last_value;
2545		}
2546	}
2547
2548	i->accumulator += delta;
2549
2550#ifdef LIBTOP_I64_DEBUG
2551	if(delta == 0)
2552		assert((value - i->last_value) == 0);
2553
2554	fprintf(stderr, "%s: delta %u  :: value %d  :: i->last_value %d\n", __func__,
2555		delta, value, i->last_value);
2556	fprintf(stderr, "%s: accumulator > INT_MAX %s\n", __func__,
2557		(i->accumulator > INT_MAX) ? "YES" : "NO");
2558
2559	fprintf(stderr, "%s: value %x value unsigned %u > UINT_MAX %s\n", __func__,
2560		value, value, (i->accumulator > UINT_MAX) ? "YES" : "NO");
2561#endif
2562
2563	i->last_value = value;
2564}
2565
2566uint64_t
2567libtop_i64_value(libtop_i64_t *i) {
2568	return i->accumulator;
2569}
2570
2571/* This tests every branch in libtop_i64_update(). */
2572static void
2573libtop_i64_test(void) {
2574	libtop_i64_t i64;
2575	int i;
2576	int value = 3;
2577	int incr = INT_MAX / 4;
2578	uint64_t accum = value;
2579
2580	i64 = libtop_i64_init(0, value);
2581
2582	i = 0;
2583
2584	while(1) {
2585		fprintf(stderr, "test: i %d value %d i64 value %" PRIu64 " matches? %" PRIu64 "\n", i, value, libtop_i64_value(&i64), accum);
2586		fprintf(stderr, "+ %d\n", incr);
2587		value += incr;
2588		accum += incr;
2589		libtop_i64_update(&i64, value);
2590		++i;
2591	}
2592
2593}
2594