| #include <u.h> | 
 | #include <sys/types.h> | 
 | #include <sys/protosw.h> | 
 | #include <sys/socket.h> | 
 | #include <sys/sysctl.h> | 
 | #include <sys/time.h> | 
 | #include <sys/dkstat.h> | 
 | #include <net/if.h> | 
 | #include <net/if_var.h> | 
 | #include <net/if_dl.h> | 
 | #include <net/if_types.h> | 
 | #include <ifaddrs.h> | 
 | #include <sys/ioctl.h> | 
 | #include <limits.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 |  | 
 | #include <mach/mach.h> | 
 | #include <mach/mach_time.h> | 
 | #include <CoreFoundation/CoreFoundation.h> | 
 | #include <IOKit/ps/IOPowerSources.h> | 
 | AUTOFRAMEWORK(CoreFoundation) | 
 | AUTOFRAMEWORK(IOKit) | 
 |  | 
 | #include "dat.h" | 
 |  | 
 | typedef struct Sample Sample; | 
 |  | 
 | struct Sample | 
 | { | 
 | 	uint seq; | 
 | 	host_cpu_load_info_data_t cpu, p_cpu; | 
 | 	vm_size_t pgsize; | 
 | 	double divisor; | 
 | 	uint64_t time, p_time; | 
 | 	vm_statistics_data_t vm_stat, p_vm_stat; | 
 | 	boolean_t purgeable_is_valid; | 
 | #ifdef VM_SWAPUSAGE	/* 10.4+ */ | 
 | 	struct xsw_usage xsu; | 
 | #endif | 
 | 	boolean_t xsu_valid; | 
 | 	integer_t syscalls_mach, p_syscalls_mach; | 
 | 	integer_t syscalls_unix, p_syscalls_unix; | 
 | 	ulong csw, p_csw; | 
 | 	uint net_ifaces; | 
 | 	uvlong net_ipackets, p_net_ipackets; | 
 | 	uvlong net_opackets, p_net_opackets; | 
 | 	uvlong net_ibytes, p_net_ibytes; | 
 | 	uvlong net_obytes, p_net_obytes; | 
 | 	uvlong net_errors, p_net_errors; | 
 | 	ulong usecs; | 
 | }; | 
 |  | 
 | static Sample sample; | 
 |  | 
 | void xsample(int); | 
 | void xapm(int); | 
 | void xloadavg(int); | 
 | void xcpu(int); | 
 | void xswap(int); | 
 | void xvm(int); | 
 | void xnet(int); | 
 |  | 
 | void (*statfn[])(int) = | 
 | { | 
 | 	xsample, | 
 | 	xapm, | 
 | 	xloadavg, | 
 | 	xswap, | 
 | 	xcpu, | 
 | 	xvm, | 
 | 	xnet, | 
 | 	0 | 
 | }; | 
 |  | 
 | static mach_port_t stat_port; | 
 |  | 
 | void | 
 | sampleinit(void) | 
 | { | 
 | 	mach_timebase_info_data_t info; | 
 | 	 | 
 | 	if(stat_port) | 
 | 		return; | 
 |  | 
 | 	stat_port = mach_host_self(); | 
 | 	memset(&sample, 0, sizeof sample); | 
 | 	if(host_page_size(stat_port, &sample.pgsize) != KERN_SUCCESS) | 
 | 		sample.pgsize = 4096; | 
 |  | 
 | 	/* populate clock tick info for timestamps */ | 
 | 	mach_timebase_info(&info); | 
 |         sample.divisor = 1000.0 * (double)info.denom/info.numer; | 
 | 	sample.time = mach_absolute_time(); | 
 | } | 
 |  | 
 | void | 
 | samplenet(void) | 
 | { | 
 | 	struct ifaddrs *ifa_list, *ifa; | 
 | 	struct if_data *if_data; | 
 |  | 
 | 	ifa_list = nil; | 
 | 	sample.net_ifaces = 0; | 
 | 	if(getifaddrs(&ifa_list) == 0){ | 
 | 		sample.p_net_ipackets = sample.net_ipackets; | 
 | 		sample.p_net_opackets = sample.net_opackets; | 
 | 		sample.p_net_ibytes = sample.net_ibytes; | 
 | 		sample.p_net_obytes = sample.net_obytes; | 
 | 		sample.p_net_errors = sample.net_errors; | 
 |  | 
 | 		sample.net_ipackets = 0; | 
 | 		sample.net_opackets = 0; | 
 | 		sample.net_ibytes = 0; | 
 | 		sample.net_obytes = 0; | 
 | 		sample.net_errors = 0; | 
 | 		sample.net_ifaces = 0; | 
 | 		 | 
 | 		for(ifa=ifa_list; ifa; ifa=ifa->ifa_next){ | 
 | 			if(ifa->ifa_addr->sa_family != AF_LINK) | 
 | 				continue; | 
 | 			if((ifa->ifa_flags&(IFF_UP|IFF_RUNNING)) == 0) | 
 | 				continue; | 
 | 			if(ifa->ifa_data == nil) | 
 | 				continue; | 
 | 			if(strncmp(ifa->ifa_name, "lo", 2) == 0)	/* loopback */ | 
 | 				continue; | 
 | 			 | 
 | 			if_data = (struct if_data*)ifa->ifa_data; | 
 | 			sample.net_ipackets += if_data->ifi_ipackets; | 
 | 			sample.net_opackets += if_data->ifi_opackets; | 
 | 			sample.net_ibytes += if_data->ifi_ibytes; | 
 | 			sample.net_obytes += if_data->ifi_obytes; | 
 | 			sample.net_errors += if_data->ifi_ierrors + if_data->ifi_oerrors; | 
 | 			sample.net_ifaces++; | 
 | 		} | 
 | 		freeifaddrs(ifa_list); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * The following forces the program to be run with the suid root as | 
 |  * all the other stat monitoring apps get set: | 
 |  * | 
 |  * -rwsr-xr-x   1 root  wheel  83088 Mar 20  2005 /usr/bin/top | 
 |  * -rwsrwxr-x   1 root  admin  54048 Mar 20  2005  | 
 |  *	/Applications/Utilities/Activity Monitor.app/Contents/Resources/pmTool | 
 |  * | 
 |  * If Darwin eventually encompases more into sysctl then this | 
 |  * won't be required. | 
 |  */ | 
 | void | 
 | sampleevents(void) | 
 | { | 
 | 	uint i, j, pcnt, tcnt; | 
 | 	mach_msg_type_number_t count; | 
 | 	kern_return_t error; | 
 | 	processor_set_t *psets, pset; | 
 | 	task_t *tasks; | 
 | 	task_events_info_data_t events; | 
 |  | 
 | 	if((error = host_processor_sets(stat_port, &psets, &pcnt)) != KERN_SUCCESS){ | 
 | 		Bprint(&bout, "host_processor_sets: %s (make sure auxstats is setuid root)\n", | 
 | 			mach_error_string(error)); | 
 | 		return; | 
 | 	} | 
 | 	 | 
 | 	sample.p_syscalls_mach = sample.syscalls_mach; | 
 | 	sample.p_syscalls_unix = sample.syscalls_unix; | 
 | 	sample.p_csw = sample.csw; | 
 |  | 
 | 	sample.syscalls_mach = 0; | 
 | 	sample.syscalls_unix = 0; | 
 | 	sample.csw = 0; | 
 | 	 | 
 | 	for(i=0; i<pcnt; i++){ | 
 | 		if((error=host_processor_set_priv(stat_port, psets[i], &pset)) != KERN_SUCCESS){ | 
 | 			Bprint(&bout, "host_processor_set_priv: %s\n", mach_error_string(error)); | 
 | 			return; | 
 | 		} | 
 | 		if((error=processor_set_tasks(pset, &tasks, &tcnt)) != KERN_SUCCESS){ | 
 | 			Bprint(&bout, "processor_set_tasks: %s\n", mach_error_string(error)); | 
 | 			return; | 
 | 		} | 
 | 		for(j=0; j<tcnt; j++){ | 
 | 			count = TASK_EVENTS_INFO_COUNT; | 
 | 			if(task_info(tasks[j], TASK_EVENTS_INFO, (task_info_t)&events, &count) == KERN_SUCCESS){ | 
 | 				sample.syscalls_mach += events.syscalls_mach; | 
 | 				sample.syscalls_unix += events.syscalls_unix; | 
 | 				sample.csw += events.csw; | 
 | 			} | 
 | 			 | 
 | 			if(tasks[j] != mach_task_self()) | 
 | 				mach_port_deallocate(mach_task_self(), tasks[j]); | 
 | 		} | 
 | 		 | 
 | 		if((error = vm_deallocate((vm_map_t)mach_task_self(),  | 
 | 				(vm_address_t)tasks, tcnt*sizeof(task_t))) != KERN_SUCCESS){ | 
 | 			Bprint(&bout, "vm_deallocate: %s\n", mach_error_string(error)); | 
 | 			return; | 
 | 		} | 
 | 		 | 
 | 		if((error = mach_port_deallocate(mach_task_self(), pset)) != KERN_SUCCESS | 
 | 		|| (error = mach_port_deallocate(mach_task_self(), psets[i])) != KERN_SUCCESS){ | 
 | 			Bprint(&bout, "mach_port_deallocate: %s\n", mach_error_string(error)); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	if((error = vm_deallocate((vm_map_t)mach_task_self(), (vm_address_t)psets, | 
 | 			pcnt*sizeof(processor_set_t))) != KERN_SUCCESS){ | 
 | 		Bprint(&bout, "vm_deallocate: %s\n", mach_error_string(error)); | 
 | 		return; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | xsample(int first) | 
 | { | 
 | 	int mib[2]; | 
 | 	mach_msg_type_number_t count; | 
 | 	size_t len; | 
 | 	 | 
 | 	if(first){ | 
 | 		sampleinit(); | 
 | 		return; | 
 | 	} | 
 | 	 | 
 | 	sample.seq++; | 
 | 	sample.p_time = sample.time; | 
 | 	sample.time = mach_absolute_time(); | 
 |  | 
 | 	sample.p_vm_stat = sample.vm_stat; | 
 | 	count = sizeof(sample.vm_stat) / sizeof(natural_t); | 
 | 	host_statistics(stat_port, HOST_VM_INFO, (host_info_t)&sample.vm_stat, &count); | 
 | 	 | 
 | 	if(sample.seq == 1) | 
 | 		sample.p_vm_stat = sample.vm_stat; | 
 |  | 
 | #ifdef VM_SWAPUSAGE | 
 | 	mib[0] = CTL_VM; | 
 | 	mib[1] = VM_SWAPUSAGE; | 
 | 	len = sizeof sample.xsu; | 
 | 	sample.xsu_valid = TRUE; | 
 | 	if(sysctl(mib, 2, &sample.xsu, &len, NULL, 0) < 0 && errno == ENOENT) | 
 | 		sample.xsu_valid = FALSE; | 
 | #endif | 
 |  | 
 | 	samplenet(); | 
 | 	sampleevents(); | 
 |  | 
 | 	sample.p_cpu = sample.cpu; | 
 | 	count = HOST_CPU_LOAD_INFO_COUNT; | 
 | 	host_statistics(stat_port, HOST_CPU_LOAD_INFO, (host_info_t)&sample.cpu, &count); | 
 | 	sample.usecs = (double)(sample.time - sample.p_time)/sample.divisor; | 
 | 	Bprint(&bout, "usecs %lud\n", sample.usecs); | 
 | } | 
 |  | 
 | void | 
 | xapm(int first) | 
 | { | 
 | 	int i, battery; | 
 | 	CFArrayRef array; | 
 | 	CFDictionaryRef dict; | 
 | 	CFTypeRef cf, src, value; | 
 |  | 
 | 	if(first) | 
 | 		return; | 
 | 		 | 
 | 	src = IOPSCopyPowerSourcesInfo(); | 
 | 	array = IOPSCopyPowerSourcesList(src); | 
 |  | 
 | 	for(i=0; i<CFArrayGetCount(array); i++){ | 
 | 		cf = CFArrayGetValueAtIndex(array, i); | 
 | 		dict = IOPSGetPowerSourceDescription(src, cf); | 
 | 		if(dict != nil){ | 
 | 			value = CFDictionaryGetValue(dict, CFSTR("Current Capacity")); | 
 | 			if(value != nil){ | 
 | 				if(!CFNumberGetValue(value, kCFNumberIntType, &battery)) | 
 | 					battery = 100; | 
 | 				Bprint(&bout, "battery =%d 100\n", battery); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	CFRelease(array); | 
 | 	CFRelease(src); | 
 | } | 
 |  | 
 | void | 
 | xnet(int first) | 
 | { | 
 | 	uint n; | 
 | 	ulong err, in, inb, out, outb; | 
 | 	 | 
 | 	n = sample.net_ifaces; | 
 | 	in = sample.net_ipackets - sample.p_net_ipackets; | 
 | 	out = sample.net_opackets - sample.p_net_opackets; | 
 | 	inb = sample.net_ibytes - sample.p_net_ibytes; | 
 | 	outb = sample.net_obytes - sample.p_net_obytes; | 
 | 	err = sample.net_errors - sample.p_net_errors; | 
 | 	 | 
 |        Bprint(&bout, "etherb %lud %d\n", inb+outb, n*1000000); | 
 |        Bprint(&bout, "ether %lud %d\n", in+out, n*1000); | 
 |        Bprint(&bout, "ethererr %lud %d\n", err, n*1000); | 
 |        Bprint(&bout, "etherin %lud %d\n", in, n*1000); | 
 |        Bprint(&bout, "etherout %lud %d\n", out, n*1000); | 
 |        Bprint(&bout, "etherinb %lud %d\n", inb, n*1000); | 
 |        Bprint(&bout, "etheroutb %lud %d\n", outb, n*1000); | 
 | } | 
 |  | 
 | int | 
 | rsys(char *name, char *buf, int len) | 
 | { | 
 | 	size_t l; | 
 |  | 
 | 	l = len; | 
 | 	if(sysctlbyname(name, buf, &l, nil, 0) < 0) | 
 | 		return -1; | 
 | 	buf[l] = 0; | 
 | 	return l; | 
 | } | 
 |  | 
 | vlong | 
 | isys(char *name) | 
 | { | 
 | 	ulong u; | 
 | 	size_t l; | 
 |  | 
 | 	l = sizeof u; | 
 | 	if(sysctlbyname(name, &u, &l, nil, 0) < 0) | 
 | 		return 0; | 
 | 	return u; | 
 | } | 
 |  | 
 | void | 
 | xvm(int first) | 
 | { | 
 | 	natural_t total, active; | 
 |  | 
 | 	if(first) | 
 | 		return; | 
 |  | 
 | 	total = sample.vm_stat.free_count | 
 | 		+ sample.vm_stat.active_count | 
 | 		+ sample.vm_stat.inactive_count | 
 | 		+ sample.vm_stat.wire_count; | 
 | 	 | 
 | 	active = sample.vm_stat.active_count | 
 | 		+ sample.vm_stat.inactive_count | 
 | 		+ sample.vm_stat.wire_count; | 
 |  | 
 | 	if(total) | 
 | 		Bprint(&bout, "mem =%lud %lud\n", active, total); | 
 |  | 
 | 	Bprint(&bout, "context %lld 1000\n", (vlong)sample.csw); | 
 | 	Bprint(&bout, "syscall %lld 1000\n", (vlong)sample.syscalls_mach+sample.syscalls_unix); | 
 | 	Bprint(&bout, "intr %lld 1000\n",  | 
 | 		isys("vm.stats.sys.v_intr") | 
 | 		+isys("vm.stats.sys.v_trap")); | 
 | 	 | 
 | 	Bprint(&bout, "fault %lld 1000\n", sample.vm_stat.faults); | 
 | 	Bprint(&bout, "fork %lld 1000\n", | 
 | 		isys("vm.stats.vm.v_rforks") | 
 | 		+isys("vm.stats.vm.v_vforks")); | 
 |  | 
 | /*    Bprint(&bout, "hits %lud of %lud lookups (%d%% hit rate)\n", */ | 
 | /*               (asamp.vm_stat.hits), */ | 
 | /*               (asamp.vm_stat.lookups), */ | 
 | /*               (natural_t)(((double)asamp.vm_stat.hits*100)/ (double)asamp.vm_stat.lookups)); */ | 
 | } | 
 |  | 
 | void | 
 | xcpu(int first) | 
 | { | 
 | 	ulong user, sys, idle, nice, t; | 
 |  | 
 | 	if(first) | 
 | 		return; | 
 |  | 
 | 	sys = sample.cpu.cpu_ticks[CPU_STATE_SYSTEM] - | 
 | 		sample.p_cpu.cpu_ticks[CPU_STATE_SYSTEM]; | 
 | 	idle = sample.cpu.cpu_ticks[CPU_STATE_IDLE] - | 
 | 		sample.p_cpu.cpu_ticks[CPU_STATE_IDLE]; | 
 | 	user = sample.cpu.cpu_ticks[CPU_STATE_USER] - | 
 | 		sample.p_cpu.cpu_ticks[CPU_STATE_USER]; | 
 | 	nice = sample.cpu.cpu_ticks[CPU_STATE_NICE] - | 
 | 		sample.p_cpu.cpu_ticks[CPU_STATE_NICE]; | 
 | 	 | 
 | 	t = sys+idle+user+nice; | 
 |  | 
 | 	Bprint(&bout, "user =%lud %lud\n", user, t); | 
 | 	Bprint(&bout, "sys =%lud %lud\n", sys, t); | 
 | 	Bprint(&bout, "idle =%lud %lud\n", idle, t); | 
 | 	Bprint(&bout, "nice =%lud %lud\n", nice, t); | 
 | } | 
 |  | 
 | void | 
 | xloadavg(int first) | 
 | { | 
 | 	double l[3]; | 
 |  | 
 | 	if(first) | 
 | 		return; | 
 |  | 
 | 	if(getloadavg(l, 3) < 0) | 
 | 		return; | 
 | 	Bprint(&bout, "load %d 1000\n", (int)(l[0]*1000.0)); | 
 | } | 
 |  | 
 | void | 
 | xswap(int first) | 
 | { | 
 | 	if(first) | 
 | 		return; | 
 |  | 
 | #ifdef VM_SWAPUSAGE | 
 | 	if(sample.xsu_valid) | 
 | 		Bprint(&bout, "swap %lld %lld\n",  | 
 | 			(vlong)sample.xsu.xsu_used,  | 
 | 			(vlong)sample.xsu.xsu_total); | 
 | #endif | 
 | } |