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