#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <mach/bootstrap.h>
#include <mach/host_priv.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
#include <mach/mach_vm.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/processor_set.h>
#include <mach/task.h>
#include <mach/task_policy.h>
#include <mach/thread_act.h>
#include <mach/shared_region.h>
#include <mach/vm_map.h>
#include <mach/vm_page_size.h>
#define IOKIT 1
#include <device/device_types.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <libproc.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <pwd.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <net/if_types.h>
#include <ifaddrs.h>
#define LIBTOP_DBG
#ifndef LIBTOP_DBG
#ifndef NDEBUG
#define NDEBUG
#endif
#endif
#include <assert.h>
#include "libtop.h"
#include "rb.h"
#define NS_TO_TIMEVAL(NS) \
(struct timeval){ .tv_sec = (NS) / NSEC_PER_SEC, \
.tv_usec = ((NS) % NSEC_PER_SEC) / NSEC_PER_USEC, }
typedef struct libtop_pinfo_s libtop_pinfo_t;
struct libtop_pinfo_s {
libtop_psamp_t psamp;
rb_node(libtop_pinfo_t) pnode;
rb_node(libtop_pinfo_t) snode;
int flag;
libtop_preg_t preg;
boolean_t split;
};
typedef struct libtop_oinfo_s libtop_oinfo_t;
struct libtop_oinfo_s {
libtop_pinfo_t *pinfo;
int obj_id;
int size;
int share_type;
int resident_page_count;
int ref_count;
int proc_ref_count;
int rb_share_type;
uint64_t rb_aliased;
uint64_t rb_vprvt;
uint64_t rb_rshrd;
};
static boolean_t ignore_PPP;
static libtop_tsamp_t tsamp;
static libtop_print_t *libtop_print;
static void *libtop_user_data;
static libtop_sort_t *libtop_sort;
static void *libtop_sort_data;
static mach_port_t libtop_port;
static char *libtop_arg;
static int libtop_argmax;
static mach_port_t libtop_master_port;
static uint32_t interval;
static CFMutableDictionaryRef libtop_oinfo_hash;
static rb_tree(libtop_pinfo_t) libtop_ptree;
static rb_tree(libtop_pinfo_t) libtop_stree;
static boolean_t libtop_sorted;
static libtop_pinfo_t *libtop_piter;
static CFMutableDictionaryRef libtop_uhash;
#define TIME_VALUE_TO_NS(a) \
(((uint64_t)((a)->seconds) * NSEC_PER_SEC) + \
((uint64_t)((a)->microseconds) * NSEC_PER_USEC))
enum libtop_status {
LIBTOP_NO_ERR = 0,
LIBTOP_ERR_INVALID = 1,
LIBTOP_ERR_ALLOC
};
mach_timebase_info_data_t timebase_info;
typedef enum libtop_status libtop_status_t;
static boolean_t libtop_p_print(void *user_data, const char *format, ...);
static int libtop_p_mach_state_order(int state, long sleep_time);
static int libtop_p_load_get(host_info_t r_load);
static int libtop_p_loadavg_update(void);
static bool in_shared_region(mach_vm_address_t addr, cpu_type_t type);
static void libtop_p_fw_scan(task_t task, mach_vm_address_t region_base,
mach_vm_size_t region_size);
static void libtop_p_fw_sample(boolean_t fw);
static int libtop_p_vm_sample(void);
static void libtop_p_networks_sample(void);
static int libtop_p_disks_sample(void);
static int libtop_p_proc_table_read(boolean_t reg);
static libtop_status_t libtop_p_cputype(pid_t pid, cpu_type_t *cputype);
static mach_vm_size_t libtop_p_shreg_size(cpu_type_t);
static libtop_status_t libtop_p_task_update(task_t task, boolean_t reg);
static libtop_status_t libtop_p_proc_command(libtop_pinfo_t *pinfo,
struct kinfo_proc *kinfo);
static void libtop_p_pinsert(libtop_pinfo_t *pinfo);
static void libtop_p_premove(libtop_pinfo_t *pinfo);
static void libtop_p_destroy_pinfo(libtop_pinfo_t *pinfo);
static libtop_pinfo_t* libtop_p_psearch(pid_t pid);
static int libtop_p_pinfo_pid_comp(libtop_pinfo_t *a, libtop_pinfo_t *b);
static int libtop_p_pinfo_comp(libtop_pinfo_t *a, libtop_pinfo_t *b);
static libtop_oinfo_t* libtop_p_oinfo_insert(int obj_id, int share_type,
int resident_page_count, int ref_count, int size,
libtop_pinfo_t *pinfo);
static int libtop_p_wq_update(libtop_pinfo_t *pinfo);
static void libtop_i64_test(void);
static void update_pages_stolen(libtop_tsamp_t *tsamp);
static void
simpleFree(CFAllocatorRef allocator, const void *value)
{
free((void *)value);
}
static const void *
stringRetain(CFAllocatorRef allocator, const void *value)
{
return strdup(value);
}
static Boolean
stringEqual(const void *value1, const void *value2)
{
return strcmp(value1, value2) == 0;
}
int
libtop_init(libtop_print_t *print, void *user_data)
{
if (print != NULL) {
libtop_print = print;
libtop_user_data = user_data;
} else {
libtop_print = libtop_p_print;
libtop_user_data = NULL;
}
tsamp.seq = 0;
interval = 1;
libtop_port = mach_host_self();
tsamp.pagesize = vm_kernel_page_size;
{
int mib[2];
size_t size;
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
size = sizeof(tsamp.memsize);
if (sysctl(mib, 2, &tsamp.memsize, &size, NULL, 0) == -1) {
libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
__FUNCTION__, strerror(errno));
return -1;
}
}
update_pages_stolen(&tsamp);
rb_tree_new(&libtop_ptree, pnode);
CFDictionaryValueCallBacks stringValueCallBacks = { 0, stringRetain, simpleFree, NULL, stringEqual };
libtop_uhash = CFDictionaryCreateMutable(NULL, 0, NULL, &stringValueCallBacks);
CFDictionaryValueCallBacks oinfoValueCallBacks = { 0, NULL, simpleFree, NULL, NULL };
libtop_oinfo_hash = CFDictionaryCreateMutable(NULL, 0, NULL, &oinfoValueCallBacks);
{
int mib[2];
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
size = sizeof(libtop_argmax);
if (sysctl(mib, 2, &libtop_argmax, &size, NULL, 0) == -1) {
libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
__FUNCTION__, strerror(errno));
return -1;
}
libtop_arg = (char *)malloc(libtop_argmax);
if (libtop_arg == NULL) return -1;
}
if (IOMasterPort(bootstrap_port, &libtop_master_port)) {
libtop_print(libtop_user_data, "Error in IOMasterPort()");
return -1;
}
if (libtop_p_load_get((host_info_t)&tsamp.b_cpu)) return -1;
tsamp.p_cpu = tsamp.b_cpu;
tsamp.cpu = tsamp.b_cpu;
mach_timebase_info(&timebase_info);
tsamp.b_timens = tsamp.p_timens = tsamp.timens =
clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW);
tsamp.b_time = tsamp.p_time = tsamp.time = NS_TO_TIMEVAL(tsamp.timens);
ignore_PPP = FALSE;
return 0;
}
void
libtop_fini(void)
{
libtop_pinfo_t *pinfo, *ppinfo;
free(libtop_arg);
CFRelease(libtop_oinfo_hash);
rb_first(&libtop_ptree, pnode, pinfo);
for (;
pinfo != rb_tree_nil(&libtop_ptree);
pinfo = ppinfo) {
rb_next(&libtop_ptree, pinfo, libtop_pinfo_t, pnode, ppinfo);
libtop_p_destroy_pinfo(pinfo);
}
CFRelease(libtop_uhash);
}
int
libtop_set_interval(uint32_t ival)
{
if ((ival == 0) || (ival > LIBTOP_MAX_INTERVAL)) {
return -1;
}
interval = ival;
return 0;
}
int
libtop_sample(boolean_t reg, boolean_t fw)
{
int res = 0;
tsamp.seq++;
libtop_sorted = FALSE;
libtop_piter = NULL;
memset(tsamp.state_breakdown, 0, sizeof(tsamp.state_breakdown));
if (tsamp.seq != 1) {
tsamp.p_timens = tsamp.timens;
tsamp.p_time = NS_TO_TIMEVAL(tsamp.timens);
tsamp.timens = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW);
tsamp.time = NS_TO_TIMEVAL(tsamp.timens);
}
res = libtop_p_proc_table_read(reg);
if (res == 0) res = libtop_p_loadavg_update();
tsamp.p_cpu = tsamp.cpu;
if (res == 0) libtop_p_load_get((host_info_t)&tsamp.cpu);
if (res == 0) libtop_p_fw_sample(fw);
if (res == 0) libtop_p_vm_sample();
if (res == 0) libtop_p_networks_sample();
if (res == 0) libtop_p_disks_sample();
return res;
}
const libtop_tsamp_t *
libtop_tsamp(void)
{
return &tsamp;
}
void
libtop_psort(libtop_sort_t *sort, void *data)
{
libtop_pinfo_t *pinfo, *ppinfo;
assert(tsamp.seq != 0);
libtop_piter = NULL;
rb_tree_new(&libtop_stree, snode);
libtop_sorted = TRUE;
libtop_sort = sort;
libtop_sort_data = data;
tsamp.nprocs = 0;
rb_first(&libtop_ptree, pnode, pinfo);
for (;
pinfo != rb_tree_nil(&libtop_ptree);
pinfo = ppinfo) {
rb_next(&libtop_ptree, pinfo, libtop_pinfo_t, pnode, ppinfo);
if (pinfo->psamp.seq == tsamp.seq) {
rb_node_new(&libtop_stree, pinfo, snode);
rb_insert(&libtop_stree, pinfo, libtop_p_pinfo_comp,
libtop_pinfo_t, snode);
tsamp.nprocs++;
} else {
libtop_p_destroy_pinfo(pinfo);
}
}
}
const libtop_psamp_t *
libtop_piterate(void)
{
assert(tsamp.seq != 0);
if (libtop_sorted) {
if (libtop_piter == NULL) {
rb_first(&libtop_stree, snode, libtop_piter);
} else {
rb_next(&libtop_stree, libtop_piter, libtop_pinfo_t,
snode, libtop_piter);
}
if (libtop_piter == rb_tree_nil(&libtop_stree)) {
libtop_piter = NULL;
}
} else {
boolean_t dead;
if (libtop_piter == NULL) {
rb_first(&libtop_ptree, pnode, libtop_piter);
} else {
rb_next(&libtop_ptree, libtop_piter, libtop_pinfo_t,
pnode, libtop_piter);
}
do {
dead = FALSE;
if (libtop_piter == rb_tree_nil(&libtop_ptree)) {
libtop_piter = NULL;
break;
}
if (libtop_piter->psamp.seq != tsamp.seq) {
libtop_pinfo_t *pinfo;
pinfo = libtop_piter;
rb_next(&libtop_ptree, libtop_piter,
libtop_pinfo_t, pnode, libtop_piter);
libtop_p_destroy_pinfo(pinfo);
dead = TRUE;
}
} while (dead);
}
return &libtop_piter->psamp;
}
int
libtop_preg(pid_t pid, libtop_preg_t preg)
{
libtop_pinfo_t *pinfo;
pinfo = libtop_p_psearch(pid);
if (pinfo == NULL) return -1;
pinfo->preg = preg;
return 0;
}
const char *
libtop_username(uid_t uid)
{
const void *k = (const void *)(uintptr_t)uid;
if (!CFDictionaryContainsKey(libtop_uhash, k)) {
struct passwd *pwd = getpwuid(uid);
if (pwd == NULL)
return NULL;
CFDictionarySetValue(libtop_uhash, k, pwd->pw_name);
}
return CFDictionaryGetValue(libtop_uhash, k);
}
const char *
libtop_state_str(uint32_t state)
{
const char *strings[] = {
"zombie",
#define LIBTOP_STATE_ZOMBIE 0
"running",
#define LIBTOP_STATE_RUN 1
"stuck",
#define LIBTOP_STATE_STUCK 2
"sleeping",
#define LIBTOP_STATE_SLEEP 3
"idle",
#define LIBTOP_STATE_IDLE 4
"stopped",
#define LIBTOP_STATE_STOP 5
"halted",
#define LIBTOP_STATE_HALT 6
"unknown"
#define LIBTOP_STATE_UNKNOWN 7
};
assert(LIBTOP_NSTATES == sizeof(strings) / sizeof(char *));
assert(state <= LIBTOP_STATE_MAX);
assert(LIBTOP_STATE_MAXLEN >= 8);
return strings[state];
}
static boolean_t
libtop_p_print(void *user_data, const char *format, ...)
{
return 0;
}
static int
libtop_p_mach_state_order(int state, long sleeptime)
{
switch (state) {
case TH_STATE_RUNNING:
return LIBTOP_STATE_RUN;
case TH_STATE_UNINTERRUPTIBLE:
return LIBTOP_STATE_STUCK;
case TH_STATE_STOPPED:
return LIBTOP_STATE_STOP;
case TH_STATE_HALTED:
return LIBTOP_STATE_HALT;
case TH_STATE_WAITING:
return (sleeptime > 0) ? LIBTOP_STATE_IDLE : LIBTOP_STATE_SLEEP;
default:
return LIBTOP_STATE_UNKNOWN;
}
}
static int
libtop_p_load_get(host_info_t r_load)
{
kern_return_t kr;
mach_msg_type_number_t count;
count = HOST_CPU_LOAD_INFO_COUNT;
kr = host_statistics(libtop_port, HOST_CPU_LOAD_INFO, r_load, &count);
if (kr != KERN_SUCCESS) {
libtop_print(libtop_user_data, "Error in host_statistics(): %s",
mach_error_string(kr));
return -1;
}
return 0;
}
static int
libtop_p_loadavg_update(void)
{
double avg[3];
if (getloadavg(avg, sizeof(avg)) < 0) {
libtop_print(libtop_user_data, "Error in getloadavg(): %s",
strerror(errno));
return -1;
}
tsamp.loadavg[0] = avg[0];
tsamp.loadavg[1] = avg[1];
tsamp.loadavg[2] = avg[2];
return 0;
}
static bool
in_shared_region(mach_vm_address_t addr, cpu_type_t type) {
mach_vm_address_t base = 0, size = 0;
switch(type) {
case CPU_TYPE_ARM:
base = SHARED_REGION_BASE_ARM;
size = SHARED_REGION_SIZE_ARM;
break;
case CPU_TYPE_X86_64:
base = SHARED_REGION_BASE_X86_64;
size = SHARED_REGION_SIZE_X86_64;
break;
case CPU_TYPE_I386:
base = SHARED_REGION_BASE_I386;
size = SHARED_REGION_SIZE_I386;
break;
case CPU_TYPE_POWERPC:
base = SHARED_REGION_BASE_PPC;
size = SHARED_REGION_SIZE_PPC;
break;
case CPU_TYPE_POWERPC64:
base = SHARED_REGION_BASE_PPC64;
size = SHARED_REGION_SIZE_PPC64;
break;
default: {
int t = type;
fprintf(stderr, "unknown CPU type: 0x%x\n", t);
abort();
}
break;
}
return(addr >= base && addr < (base + size));
}
static void
libtop_p_fw_scan(task_t task, mach_vm_address_t region_base, mach_vm_size_t region_size) {
mach_vm_size_t vsize = 0;
mach_vm_size_t code = 0;
mach_vm_size_t data = 0;
mach_vm_size_t linkedit = 0;
mach_vm_size_t pagesize = tsamp.pagesize;
mach_vm_size_t size;
for (mach_vm_address_t addr = region_base; addr < (region_base + region_size); addr += size) {
uint32_t depth = 1;
vm_region_submap_info_data_64_t sinfo;
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
kern_return_t kr = mach_vm_region_recurse(task, &addr, &size, &depth,
(vm_region_recurse_info_t)&sinfo, &count);
if (kr != KERN_SUCCESS) break;
vsize += size;
switch (sinfo.share_mode) {
case SM_SHARED:
case SM_COW:
case SM_TRUESHARED:
if (sinfo.max_protection & VM_PROT_EXECUTE) {
code += sinfo.pages_resident;
tsamp.fw_count++;
} else if (sinfo.max_protection & VM_PROT_WRITE) {
data += sinfo.pages_resident;
} else {
linkedit += sinfo.pages_resident;
}
break;
default:
break;
}
}
tsamp.fw_vsize += vsize;
tsamp.fw_code += code * pagesize;
tsamp.fw_data += data * pagesize;
tsamp.fw_linkedit += linkedit * pagesize;
}
static void
libtop_p_fw_sample(boolean_t fw)
{
if (!fw) return;
if ((interval != 1) && ((tsamp.seq % interval) != 1)) return;
tsamp.fw_count = 0;
tsamp.fw_code = 0;
tsamp.fw_data = 0;
tsamp.fw_linkedit = 0;
tsamp.fw_vsize = 0;
tsamp.fw_private = 0;
#if defined(__arm__)
libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_ARM, SHARED_REGION_SIZE_ARM);
#elif defined(__arm64__)
libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_ARM64, SHARED_REGION_SIZE_ARM64);
#elif defined(__x86_64__) || defined(__i386__)
libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_I386, SHARED_REGION_BASE_I386);
libtop_p_fw_scan(mach_task_self(), SHARED_REGION_BASE_X86_64, SHARED_REGION_BASE_X86_64);
#else
#error "unsupported architecture"
#endif
libtop_piter = NULL;
while (libtop_piterate()) {
tsamp.fw_private += libtop_piter->psamp.fw_private;
}
}
static int
libtop_tsamp_update_swap_usage(libtop_tsamp_t* tsamp) {
int mib[2] = { CTL_VM, VM_SWAPUSAGE };
int miblen = 2;
size_t len = sizeof(tsamp->xsu);
int res = sysctl(mib, miblen, &tsamp->xsu, &len, NULL, 0);
tsamp->xsu_is_valid = (res == 0);
return res;
}
static uint64_t
round_down_wired(uint64_t value) {
return (value & ~((128 * 1024 * 1024ULL) - 1));
}
static void
update_pages_stolen(libtop_tsamp_t *tsamp) {
static int mib_reserved[CTL_MAXNAME];
static int mib_unusable[CTL_MAXNAME];
static int mib_other[CTL_MAXNAME];
static size_t mib_reserved_len = 0;
static size_t mib_unusable_len = 0;
static size_t mib_other_len = 0;
int r;
tsamp->pages_stolen = 0;
if(0 == mib_reserved_len) {
mib_reserved_len = CTL_MAXNAME;
r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved,
&mib_reserved_len);
if(-1 == r) {
mib_reserved_len = 0;
return;
}
mib_unusable_len = CTL_MAXNAME;
r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable,
&mib_unusable_len);
if(-1 == r) {
mib_reserved_len = 0;
return;
}
mib_other_len = CTL_MAXNAME;
r = sysctlnametomib("machdep.memmap.Other", mib_other,
&mib_other_len);
if(-1 == r) {
mib_reserved_len = 0;
return;
}
}
if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0) {
uint64_t reserved = 0, unusable = 0, other = 0;
size_t reserved_len;
size_t unusable_len;
size_t other_len;
reserved_len = sizeof(reserved);
unusable_len = sizeof(unusable);
other_len = sizeof(other);
if(-1 == sysctl(mib_reserved, mib_reserved_len, &reserved,
&reserved_len, NULL, 0)) {
return;
}
if(-1 == sysctl(mib_unusable, mib_unusable_len, &unusable,
&unusable_len, NULL, 0)) {
return;
}
if(-1 == sysctl(mib_other, mib_other_len, &other,
&other_len, NULL, 0)) {
return;
}
if(reserved_len == sizeof(reserved)
&& unusable_len == sizeof(unusable)
&& other_len == sizeof(other)) {
uint64_t stolen = reserved + unusable + other;
uint64_t mb128 = 128 * 1024 * 1024ULL;
if(stolen >= mb128) {
tsamp->pages_stolen = round_down_wired(stolen) / tsamp->pagesize;
}
}
}
}
static int
libtop_tsamp_update_vm_stats(libtop_tsamp_t* tsamp) {
kern_return_t kr;
tsamp->p_vm_stat = tsamp->vm_stat;
mach_msg_type_number_t count = sizeof(tsamp->vm_stat) / sizeof(natural_t);
kr = host_statistics64(libtop_port, HOST_VM_INFO64, (host_info64_t)&tsamp->vm_stat, &count);
if (kr != KERN_SUCCESS) {
return kr;
}
if (tsamp->pages_stolen > 0) {
tsamp->vm_stat.wire_count += tsamp->pages_stolen;
}
tsamp->purgeable_is_valid = (count == (sizeof(tsamp->vm_stat)/sizeof(natural_t)));
if (!tsamp->purgeable_is_valid) {
tsamp->vm_stat.purgeable_count = 0;
tsamp->vm_stat.purges = 0;
}
if (tsamp->seq == 1) {
tsamp->p_vm_stat = tsamp->vm_stat;
tsamp->b_vm_stat = tsamp->vm_stat;
}
return kr;
}
static void
sum_rshrd(const void *key, const void *value, void *context)
{
libtop_oinfo_t *oinfo = (libtop_oinfo_t *)value;
mach_vm_size_t *rshrd = (mach_vm_size_t *)context;
switch (oinfo->share_type) {
case SM_SHARED:
case SM_COW:
*rshrd += oinfo->resident_page_count;
break;
}
}
static int
libtop_p_vm_sample(void)
{
libtop_tsamp_update_vm_stats(&tsamp);
libtop_tsamp_update_swap_usage(&tsamp);
mach_vm_size_t reg = 0;
mach_vm_size_t rprvt = 0;
mach_vm_size_t rshrd = 0;
mach_vm_size_t vsize = 0;
CFDictionaryApplyFunction(libtop_oinfo_hash, sum_rshrd, &rshrd);
libtop_piter = NULL;
while (libtop_piterate()) {
reg += libtop_piter->psamp.reg;
rprvt += libtop_piter->psamp.rprvt;
vsize += libtop_piter->psamp.vsize;
}
tsamp.reg = reg;
tsamp.rprvt = rprvt;
tsamp.rshrd = rshrd * tsamp.pagesize;
tsamp.vsize = vsize;
return 0;
}
static void
libtop_p_networks_sample(void)
{
short network_layer;
short link_layer;
int mib[6];
char *buf = NULL, *lim, *next;
size_t len;
struct if_msghdr *ifm;
tsamp.p_net_ipackets = tsamp.net_ipackets;
tsamp.p_net_opackets = tsamp.net_opackets;
tsamp.p_net_ibytes = tsamp.net_ibytes;
tsamp.p_net_obytes = tsamp.net_obytes;
tsamp.net_ipackets = 0;
tsamp.net_opackets = 0;
tsamp.net_ibytes = 0;
tsamp.net_obytes = 0;
mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = 0; mib[4] = NET_RT_IFLIST2; mib[5] = 0;
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) return;
if ((buf = malloc(len)) == NULL) {
printf("malloc failed\n");
exit(1);
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
if (buf) free(buf);
return;
}
lim = buf + len;
for (next = buf; next < lim; ) {
network_layer = link_layer = 0;
ifm = (struct if_msghdr *)next;
next += ifm->ifm_msglen;
if (ifm->ifm_type == RTM_IFINFO2) {
struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
if(if2m->ifm_data.ifi_type==IFT_ETHER)
ignore_PPP=TRUE;
if( 1
|| ((if2m->ifm_data.ifi_type!=IFT_LOOP)
&& !(ignore_PPP && if2m->ifm_data.ifi_type==IFT_PPP))) {
tsamp.net_opackets += if2m->ifm_data.ifi_opackets;
tsamp.net_ipackets += if2m->ifm_data.ifi_ipackets;
tsamp.net_obytes += if2m->ifm_data.ifi_obytes;
tsamp.net_ibytes += if2m->ifm_data.ifi_ibytes;
}
}
}
if (tsamp.seq == 1) {
tsamp.b_net_ipackets = tsamp.net_ipackets;
tsamp.p_net_ipackets = tsamp.net_ipackets;
tsamp.b_net_opackets = tsamp.net_opackets;
tsamp.p_net_opackets = tsamp.net_opackets;
tsamp.b_net_ibytes = tsamp.net_ibytes;
tsamp.p_net_ibytes = tsamp.net_ibytes;
tsamp.b_net_obytes = tsamp.net_obytes;
tsamp.p_net_obytes = tsamp.net_obytes;
}
free(buf);
}
static int
libtop_p_disks_sample(void)
{
int retval;
io_registry_entry_t drive;
io_iterator_t drive_list;
CFNumberRef number;
CFDictionaryRef properties, statistics;
UInt64 value;
if (IOServiceGetMatchingServices(libtop_master_port,
IOServiceMatching("IOBlockStorageDriver"), &drive_list)) {
libtop_print(libtop_user_data,
"Error in IOServiceGetMatchingServices()");
return -1;
}
tsamp.p_disk_rops = tsamp.disk_rops;
tsamp.p_disk_wops = tsamp.disk_wops;
tsamp.p_disk_rbytes = tsamp.disk_rbytes;
tsamp.p_disk_wbytes = tsamp.disk_wbytes;
tsamp.disk_rops = 0;
tsamp.disk_wops = 0;
tsamp.disk_rbytes = 0;
tsamp.disk_wbytes = 0;
while ((drive = IOIteratorNext(drive_list)) != 0) {
number = 0;
properties = 0;
statistics = 0;
value = 0;
if (IORegistryEntryCreateCFProperties(drive,
(CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0)) {
libtop_print(libtop_user_data,
"Error in IORegistryEntryCreateCFProperties()");
retval = -1;
goto RETURN; }
if (properties != 0) {
statistics
= (CFDictionaryRef)CFDictionaryGetValue(properties,
CFSTR(kIOBlockStorageDriverStatisticsKey));
if (statistics != 0) {
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsReadsKey));
if (number != 0) {
CFNumberGetValue(number,
kCFNumberSInt64Type, &value);
tsamp.disk_rops += value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
if (number != 0) {
CFNumberGetValue(number,
kCFNumberSInt64Type, &value);
tsamp.disk_rbytes += value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsWritesKey));
if (number != 0) {
CFNumberGetValue(number,
kCFNumberSInt64Type, &value);
tsamp.disk_wops += value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
if (number != 0) {
CFNumberGetValue(number,
kCFNumberSInt64Type, &value);
tsamp.disk_wbytes += value;
}
}
CFRelease(properties);
}
IOObjectRelease(drive);
}
IOIteratorReset(drive_list);
if (tsamp.seq == 1) {
tsamp.b_disk_rops = tsamp.disk_rops;
tsamp.p_disk_rops = tsamp.disk_rops;
tsamp.b_disk_wops = tsamp.disk_wops;
tsamp.p_disk_wops = tsamp.disk_wops;
tsamp.b_disk_rbytes = tsamp.disk_rbytes;
tsamp.p_disk_rbytes = tsamp.disk_rbytes;
tsamp.b_disk_wbytes = tsamp.disk_wbytes;
tsamp.p_disk_wbytes = tsamp.disk_wbytes;
}
retval = 0;
RETURN:
IOObjectRelease(drive_list);
return retval;
}
static int
libtop_p_proc_table_read(boolean_t reg)
{
kern_return_t kr;
processor_set_name_array_t psets;
processor_set_t pset;
task_array_t tasks;
mach_msg_type_number_t i, j, pcnt, tcnt;
bool fatal_error_occurred = false;
kr = host_processor_sets(libtop_port, &psets, &pcnt);
if (kr != KERN_SUCCESS) {
libtop_print(libtop_user_data, "Error in host_processor_sets(): %s",
mach_error_string(kr));
return -1;
}
for (i = 0; i < pcnt; i++) {
kr = host_processor_set_priv(libtop_port, psets[i], &pset);
if (kr != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in host_processor_set_priv(): %s",
mach_error_string(kr));
return -1;
}
kr = processor_set_tasks(pset, &tasks, &tcnt);
if (kr != KERN_SUCCESS) {
libtop_print(libtop_user_data, "Error in processor_set_tasks(): %s",
mach_error_string(kr));
return -1;
}
tsamp.reg = 0;
tsamp.rprvt = 0;
tsamp.vsize = 0;
tsamp.threads = 0;
for (j = 0; j < tcnt; j++) {
switch(libtop_p_task_update(tasks[j], reg)) {
case LIBTOP_ERR_INVALID:
break;
case LIBTOP_ERR_ALLOC:
fatal_error_occurred = true;
break;
case LIBTOP_NO_ERR:
break;
}
mach_port_deallocate(mach_task_self(), tasks[j]);
}
kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)tasks, tcnt * sizeof(*tasks));
kr = mach_port_deallocate(mach_task_self(), pset);
kr = mach_port_deallocate(mach_task_self(), psets[i]);
}
kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)psets, pcnt * sizeof(*psets));
return (fatal_error_occurred) ? -1 : 0;
}
static libtop_status_t
libtop_p_cputype(pid_t pid, cpu_type_t *cputype) {
int res = -1;
static int mib[CTL_MAXNAME];
static size_t miblen = 0;
*cputype = 0;
if (miblen == 0) {
miblen = CTL_MAXNAME;
res = sysctlnametomib("sysctl.proc_cputype", mib, &miblen);
if (res != 0) {
miblen = 0;
}
}
if (miblen > 0) {
mib[miblen] = pid;
size_t len = sizeof(*cputype);
res = sysctl(mib, miblen + 1, cputype, &len, NULL, 0);
}
return (res == 0) ? LIBTOP_NO_ERR : LIBTOP_ERR_INVALID;
}
static mach_vm_size_t
libtop_p_shreg_size(cpu_type_t type) {
switch(type)
{
case CPU_TYPE_ARM: return SHARED_REGION_SIZE_ARM;
case CPU_TYPE_POWERPC: return SHARED_REGION_SIZE_PPC;
case CPU_TYPE_POWERPC64: return SHARED_REGION_SIZE_PPC64;
case CPU_TYPE_I386: return SHARED_REGION_SIZE_I386;
case CPU_TYPE_X86_64: return SHARED_REGION_SIZE_X86_64;
default: return 0;
}
}
static int __attribute__((noinline))
kinfo_for_pid(struct kinfo_proc* kinfo, pid_t pid) {
size_t miblen = 4, len;
int mib[miblen];
int res;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
len = sizeof(struct kinfo_proc);
res = sysctl(mib, miblen, kinfo, &len, NULL, 0);
if (res != 0) {
libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
__FUNCTION__, strerror(errno));
}
return res;
}
static kern_return_t
libtop_pinfo_update_boosts(task_t task, libtop_pinfo_t* pinfo) {
#if !defined(TASK_POLICY_STATE_COUNT)
struct task_policy_state {
uint64_t requested;
uint64_t effective;
uint64_t pending;
uint32_t imp_assertcnt;
uint32_t imp_externcnt;
uint64_t flags;
uint64_t imp_transitions;
uint64_t reserved[2];
};
typedef struct task_policy_state *task_policy_state_t;
#define TASK_POLICY_STATE_COUNT ((mach_msg_type_number_t) \
(sizeof (struct task_policy_state) / sizeof (integer_t)))
#define TASK_IMP_RECEIVER 0x00000001
#define TASK_IMP_DONOR 0x00000002
#define TASK_IMP_LIVE_DONOR 0x00000004
#endif
struct task_policy_state policy_state = {0};
mach_msg_type_number_t count = TASK_POLICY_STATE_COUNT;
boolean_t get_default = FALSE;
kern_return_t kr;
pinfo->psamp.p_boosts = pinfo->psamp.boosts;
pinfo->psamp.p_assertcnt = pinfo->psamp.assertcnt;
kr = task_policy_get(task, TASK_POLICY_STATE, (task_info_t)&policy_state, &count, &get_default);
if (kr != KERN_SUCCESS) return kr;
pinfo->psamp.boosts = policy_state.imp_transitions;
pinfo->psamp.assertcnt = policy_state.imp_assertcnt;
pinfo->psamp.boost_donating = (((policy_state.flags & TASK_IMP_DONOR) != 0) ||
(((policy_state.flags & TASK_IMP_RECEIVER) != 0) &&
((policy_state.imp_assertcnt != 0) ||
(pinfo->psamp.boosts > pinfo->psamp.p_boosts))));
if (pinfo->psamp.boost_donating && pinfo->psamp.p_boosts > 1) {
pinfo->psamp.boost_last_donating_seq = pinfo->psamp.seq;
}
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_boosts = pinfo->psamp.boosts;
pinfo->psamp.b_assertcnt = pinfo->psamp.assertcnt;
}
return KERN_SUCCESS;
}
static kern_return_t
libtop_pinfo_update_mach_ports(task_t task, libtop_pinfo_t* pinfo) {
kern_return_t kr;
ipc_info_space_basic_t info;
pinfo->psamp.p_prt = pinfo->psamp.prt;
kr = mach_port_space_basic_info(task, &info);
if (kr == KERN_SUCCESS) {
pinfo->psamp.prt = info.iisb_table_inuse;
}
return kr;
}
static kern_return_t
libtop_pinfo_update_events_info(task_t task, libtop_pinfo_t* pinfo) {
kern_return_t kr;
mach_msg_type_number_t count = TASK_EVENTS_INFO_COUNT;
pinfo->psamp.p_events = pinfo->psamp.events;
pinfo->psamp.faults.previous = pinfo->psamp.faults.now;
pinfo->psamp.pageins.previous = pinfo->psamp.pageins.now;
pinfo->psamp.cow_faults.previous = pinfo->psamp.cow_faults.now;
pinfo->psamp.messages_sent.previous = pinfo->psamp.messages_sent.now;
pinfo->psamp.messages_recv.previous = pinfo->psamp.messages_recv.now;
pinfo->psamp.syscalls_mach.previous = pinfo->psamp.syscalls_mach.now;
pinfo->psamp.syscalls_bsd.previous = pinfo->psamp.syscalls_bsd.now;
pinfo->psamp.csw.previous = pinfo->psamp.csw.now;
kr = task_info(task, TASK_EVENTS_INFO, (task_info_t)&pinfo->psamp.events, &count);
if (kr != KERN_SUCCESS) return kr;
#if 0
if(pinfo->psamp.pid == 0) {
static int i;
pinfo->psamp.events.faults = ((INT_MAX - pinfo->psamp.events.faults) - 20000) + i;
pinfo->psamp.events.csw += INT_MAX;
i += INT_MAX / 2;
}
#endif
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_events = pinfo->psamp.events;
pinfo->psamp.p_events = pinfo->psamp.events;
pinfo->psamp.faults.i64 = libtop_i64_init(0, pinfo->psamp.events.faults);
pinfo->psamp.pageins.i64 = libtop_i64_init(0, pinfo->psamp.events.pageins);
pinfo->psamp.cow_faults.i64 = libtop_i64_init(0, pinfo->psamp.events.cow_faults);
pinfo->psamp.messages_sent.i64 = libtop_i64_init(0, pinfo->psamp.events.messages_sent);
pinfo->psamp.messages_recv.i64 = libtop_i64_init(0, pinfo->psamp.events.messages_received);
pinfo->psamp.syscalls_mach.i64 = libtop_i64_init(0, pinfo->psamp.events.syscalls_mach);
pinfo->psamp.syscalls_bsd.i64 = libtop_i64_init(0, pinfo->psamp.events.syscalls_unix);
pinfo->psamp.csw.i64 = libtop_i64_init(0, pinfo->psamp.events.csw);
}
libtop_i64_update(&pinfo->psamp.faults.i64, pinfo->psamp.events.faults);
libtop_i64_update(&pinfo->psamp.pageins.i64, pinfo->psamp.events.pageins);
libtop_i64_update(&pinfo->psamp.cow_faults.i64, pinfo->psamp.events.cow_faults);
libtop_i64_update(&pinfo->psamp.messages_sent.i64, pinfo->psamp.events.messages_sent);
libtop_i64_update(&pinfo->psamp.messages_recv.i64, pinfo->psamp.events.messages_received);
libtop_i64_update(&pinfo->psamp.syscalls_mach.i64, pinfo->psamp.events.syscalls_mach);
libtop_i64_update(&pinfo->psamp.syscalls_bsd.i64, pinfo->psamp.events.syscalls_unix);
libtop_i64_update(&pinfo->psamp.csw.i64, pinfo->psamp.events.csw);
pinfo->psamp.faults.now = libtop_i64_value(&pinfo->psamp.faults.i64);
pinfo->psamp.pageins.now = libtop_i64_value(&pinfo->psamp.pageins.i64);
pinfo->psamp.cow_faults.now = libtop_i64_value(&pinfo->psamp.cow_faults.i64);
pinfo->psamp.messages_sent.now = libtop_i64_value(&pinfo->psamp.messages_sent.i64);
pinfo->psamp.messages_recv.now = libtop_i64_value(&pinfo->psamp.messages_recv.i64);
pinfo->psamp.syscalls_mach.now = libtop_i64_value(&pinfo->psamp.syscalls_mach.i64);
pinfo->psamp.syscalls_bsd.now = libtop_i64_value(&pinfo->psamp.syscalls_bsd.i64);
pinfo->psamp.csw.now = libtop_i64_value(&pinfo->psamp.csw.i64);
return kr;
}
static kern_return_t
libtop_pinfo_update_kernmem_info(task_t task, libtop_pinfo_t* pinfo) {
kern_return_t kr;
mach_msg_type_number_t count = TASK_KERNELMEMORY_INFO_COUNT;
pinfo->psamp.p_palloc = pinfo->psamp.palloc;
pinfo->psamp.p_pfree = pinfo->psamp.pfree;
pinfo->psamp.p_salloc = pinfo->psamp.salloc;
pinfo->psamp.p_sfree = pinfo->psamp.sfree;
kr = task_info(task, TASK_KERNELMEMORY_INFO, (task_info_t)&pinfo->psamp.palloc, &count);
return kr;
}
static kern_return_t
libtop_pinfo_update_power_info(task_t task, libtop_pinfo_t *pinfo)
{
kern_return_t kr;
mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
pinfo->psamp.p_power = pinfo->psamp.power;
kr = task_info(task, TASK_POWER_INFO, (task_info_t)&pinfo->psamp.power, &count);
if (kr != KERN_SUCCESS) return kr;
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_power = pinfo->psamp.power;
pinfo->psamp.p_power = pinfo->psamp.power;
}
return kr;
}
static kern_return_t
libtop_pinfo_update_vm_info(task_t task, libtop_pinfo_t *pinfo)
{
kern_return_t kr;
mach_msg_type_number_t info_count;
task_vm_info_data_t vm_info;
pinfo->psamp.p_purgeable = pinfo->psamp.purgeable;
pinfo->psamp.p_pfootprint = pinfo->psamp.pfootprint;
pinfo->psamp.p_compressed = pinfo->psamp.compressed;
info_count = TASK_VM_INFO_COUNT;
kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
if (kr == KERN_SUCCESS) {
pinfo->psamp.purgeable = vm_info.purgeable_volatile_resident;
pinfo->psamp.pfootprint = vm_info.phys_footprint;
pinfo->psamp.compressed = vm_info.compressed;
}
return kr;
}
static kern_return_t
libtop_pinfo_update_cpu_usage(task_t task, libtop_pinfo_t* pinfo, int *state) {
kern_return_t kr;
thread_act_array_t threads;
mach_msg_type_number_t tcnt;
*state = LIBTOP_STATE_MAX;
pinfo->psamp.state = LIBTOP_STATE_MAX;
kr = task_threads(task, &threads, &tcnt);
if (kr != KERN_SUCCESS) return kr;
if(tsamp.seq > 1) {
pinfo->psamp.p_th = pinfo->psamp.th;
pinfo->psamp.p_running_th = pinfo->psamp.running_th;
} else {
pinfo->psamp.p_th = 0;
pinfo->psamp.p_running_th = 0;
}
pinfo->psamp.th = tcnt;
tsamp.threads += tcnt;
pinfo->psamp.running_th = 0;
int i;
for (i = 0; i < tcnt; i++) {
thread_basic_info_data_t info;
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
kr = thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&info, &count);
if (kr != KERN_SUCCESS) continue;
if ((info.flags & (TH_FLAGS_IDLE | TH_FLAGS_GLOBAL_FORCED_IDLE)) == 0) {
pinfo->psamp.total_timens += TIME_VALUE_TO_NS(&info.user_time) +
TIME_VALUE_TO_NS(&info.system_time);
}
if(info.run_state == TH_STATE_RUNNING) {
pinfo->psamp.running_th++;
}
uint32_t tstate;
tstate = libtop_p_mach_state_order(info.run_state, info.sleep_time);
if (tstate < *state) {
*state = tstate;
pinfo->psamp.state = tstate;
}
kr = mach_port_deallocate(mach_task_self(), threads[i]);
}
kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, tcnt * sizeof(*threads));
return kr;
}
#ifdef TOP_DEBUG_VM
static char*
share_mode_to_string(int share_mode) {
switch(share_mode) {
case SM_COW: return "COW";
case SM_PRIVATE: return "PRV";
case SM_EMPTY: return "NUL";
case SM_SHARED: return "ALI";
default: return "???";
}
}
#endif
static kern_return_t
libtop_update_vm_regions(task_t task, libtop_pinfo_t* pinfo) {
kern_return_t kr;
mach_vm_size_t rprvt = 0;
mach_vm_size_t vprvt = 0;
mach_vm_size_t rshrd = 0;
mach_vm_size_t empty = 0;
mach_vm_size_t aliased = 0;
mach_vm_size_t fw_private = 0;
mach_vm_size_t pagesize = tsamp.pagesize;
mach_vm_size_t regs = 0;
mach_vm_address_t addr;
mach_vm_size_t size;
libtop_oinfo_t *oinfo;
pinfo->split = FALSE;
#ifdef TOP_DEBUG_VM
fprintf(stderr, "pid %d\n", pinfo->psamp.pid);
fprintf(stderr, "range\tsize\tprivate\tshared\trc\toid\tmode\tvprvt\trprvt\trshrd\tempty\taliased\n");
#endif
for (addr = 0; ; addr += size) {
vm_region_top_info_data_t info;
mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
mach_port_t object_name;
kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
if (kr != KERN_SUCCESS) break;
#ifdef TOP_DEBUG_VM
fprintf(stderr, "%016llx-%016llx\t%lld\t%d\t%d\t%d\t%d\t%s",
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));
#endif
if (in_shared_region(addr, pinfo->psamp.cputype)) {
fw_private += info.private_pages_resident * pagesize;
if (pinfo->split == FALSE && info.share_mode == SM_EMPTY) {
vm_region_basic_info_data_64_t b_info;
mach_vm_address_t b_addr = addr;
mach_vm_size_t b_size = size;
count = VM_REGION_BASIC_INFO_COUNT_64;
kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name);
if (kr != KERN_SUCCESS) break;
if (b_info.reserved) {
pinfo->split = TRUE;
}
}
if (info.share_mode != SM_PRIVATE) {
#ifdef TOP_DEBUG_VM
fprintf(stderr, "\n");
#endif
continue;
}
}
regs++;
if (info.share_mode == SM_COW && info.ref_count == 1) {
info.share_mode = SM_PRIVATE;
}
switch (info.share_mode) {
case SM_LARGE_PAGE:
case SM_PRIVATE:
rprvt += info.private_pages_resident * pagesize;
rprvt += info.shared_pages_resident * pagesize;
vprvt += size;
break;
case SM_EMPTY:
empty += size;
break;
case SM_COW:
case SM_SHARED:
if (pinfo->psamp.pid == 0) {
if (info.share_mode == SM_COW) {
rprvt += info.private_pages_resident * pagesize;
vprvt += size;
}
break;
}
oinfo = libtop_p_oinfo_insert(info.obj_id, info.share_mode, info.shared_pages_resident, info.ref_count, size, pinfo);
if (!oinfo) {
return -1;
}
if (oinfo->proc_ref_count > 1) {
if (oinfo->rb_share_type != SM_EMPTY) {
oinfo->share_type = oinfo->rb_share_type;
}
aliased -= oinfo->rb_aliased;
vprvt -= oinfo->rb_vprvt;
rshrd -= oinfo->rb_rshrd;
}
oinfo->rb_share_type = SM_EMPTY;
oinfo->rb_aliased = 0;
oinfo->rb_vprvt = 0;
oinfo->rb_rshrd = 0;
if (oinfo->share_type == SM_SHARED && oinfo->ref_count == oinfo->proc_ref_count) {
oinfo->rb_share_type = oinfo->share_type;
oinfo->share_type = SM_PRIVATE_ALIASED;
oinfo->rb_aliased += oinfo->resident_page_count * pagesize;
aliased += oinfo->resident_page_count * pagesize;
oinfo->rb_vprvt += oinfo->size;
vprvt += oinfo->size;
}
if (oinfo->share_type != SM_PRIVATE_ALIASED) {
oinfo->rb_rshrd += oinfo->resident_page_count * pagesize;
rshrd += oinfo->resident_page_count * pagesize;
}
if (info.share_mode == SM_COW) {
rprvt += info.private_pages_resident * pagesize;
vprvt += info.private_pages_resident * pagesize; }
break;
default:
assert(0);
break;
}
#ifdef TOP_DEBUG_VM
fprintf(stderr, "\t%lld\t%lld\t%lld\t%lld\t%lld\n", vprvt, rprvt, rshrd, empty, aliased);
#endif
}
pinfo->psamp.rprvt = rprvt;
pinfo->psamp.vprvt = vprvt;
pinfo->psamp.rshrd = rshrd;
pinfo->psamp.empty = empty;
pinfo->psamp.rprvt += aliased;
pinfo->psamp.fw_private = fw_private;
if(tsamp.seq > 1) {
pinfo->psamp.p_reg = pinfo->psamp.reg;
} else {
pinfo->psamp.p_reg = 0;
}
pinfo->psamp.reg = regs;
return kr;
}
static libtop_status_t __attribute__((noinline))
libtop_p_task_update(task_t task, boolean_t reg)
{
kern_return_t kr;
int res;
struct kinfo_proc kinfo;
pid_t pid;
mach_msg_type_number_t count;
libtop_pinfo_t *pinfo;
struct task_basic_info_64 ti;
int state;
state = LIBTOP_STATE_ZOMBIE;
kr = pid_for_task(task, &pid);
if (kr != KERN_SUCCESS) {
return LIBTOP_ERR_INVALID;
}
res = kinfo_for_pid(&kinfo, pid);
if (res != 0) {
return LIBTOP_ERR_INVALID;
}
if (kinfo.kp_proc.p_stat == SZOMB) {
}
pinfo = libtop_p_psearch((pid_t)pid);
if (pinfo == NULL) {
pinfo = (libtop_pinfo_t *)calloc(1, sizeof(libtop_pinfo_t));
if (pinfo == NULL) {
return LIBTOP_ERR_ALLOC;
}
pinfo->psamp.pid = (pid_t)pid;
libtop_p_pinsert(pinfo);
}
res = libtop_p_proc_command(pinfo, &kinfo);
switch(res) {
case LIBTOP_NO_ERR:
break;
case LIBTOP_ERR_INVALID:
libtop_p_destroy_pinfo(pinfo);
return res;
case LIBTOP_ERR_ALLOC:
return res;
}
pinfo->psamp.uid = kinfo.kp_eproc.e_ucred.cr_uid;
pinfo->psamp.ppid = kinfo.kp_eproc.e_ppid;
pinfo->psamp.pgrp = kinfo.kp_eproc.e_pgid;
pinfo->flag = kinfo.kp_proc.p_flag;
pinfo->psamp.started = kinfo.kp_proc.p_starttime;
pinfo->psamp.p_seq = pinfo->psamp.seq;
pinfo->psamp.seq = tsamp.seq;
if(LIBTOP_NO_ERR != libtop_p_cputype(pinfo->psamp.pid, &pinfo->psamp.cputype)) {
libtop_p_destroy_pinfo(pinfo);
return LIBTOP_ERR_INVALID;
}
count = TASK_BASIC_INFO_64_COUNT;
kr = task_info(task, TASK_BASIC_INFO_64, (task_info_t)&ti, &count);
if (kr != KERN_SUCCESS) {
libtop_p_destroy_pinfo(pinfo);
return LIBTOP_ERR_INVALID;
}
pinfo->psamp.p_rsize = pinfo->psamp.rsize;
pinfo->psamp.p_vsize = pinfo->psamp.vsize;
pinfo->psamp.p_rprvt = pinfo->psamp.rprvt;
pinfo->psamp.p_vprvt = pinfo->psamp.vprvt;
pinfo->psamp.p_rshrd = pinfo->psamp.rshrd;
pinfo->psamp.p_empty = pinfo->psamp.empty;
if ((reg && pinfo->preg != LIBTOP_PREG_off)
|| pinfo->preg == LIBTOP_PREG_on
|| pinfo->psamp.p_seq == 0
|| (pinfo->split && ti.virtual_size
< libtop_p_shreg_size(pinfo->psamp.cputype))) {
libtop_update_vm_regions(task, pinfo);
}
pinfo->psamp.rsize = ti.resident_size;
pinfo->psamp.vsize = ti.virtual_size;
pinfo->psamp.p_total_timens = pinfo->psamp.total_timens;
pinfo->psamp.p_cpu_billed_to_me = pinfo->psamp.cpu_billed_to_me;
pinfo->psamp.p_cpu_billed_to_others = pinfo->psamp.cpu_billed_to_others;
pinfo->psamp.p_instructions = pinfo->psamp.instructions;
pinfo->psamp.p_cycles = pinfo->psamp.cycles;
struct rusage_info_v4 ri;
proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t)&ri);
pinfo->psamp.cpu_billed_to_me = ri.ri_billed_system_time;
pinfo->psamp.cpu_billed_to_others = ri.ri_serviced_system_time;
pinfo->psamp.instructions = ri.ri_instructions;
pinfo->psamp.cycles = ri.ri_cycles;
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_cpu_billed_to_me = pinfo->psamp.cpu_billed_to_me;
pinfo->psamp.b_cpu_billed_to_others = pinfo->psamp.cpu_billed_to_others;
pinfo->psamp.p_cpu_billed_to_me = pinfo->psamp.cpu_billed_to_me;
pinfo->psamp.p_cpu_billed_to_others = pinfo->psamp.cpu_billed_to_others;
pinfo->psamp.b_instructions = pinfo->psamp.instructions;
pinfo->psamp.b_cycles = pinfo->psamp.cycles;
pinfo->psamp.p_instructions = pinfo->psamp.instructions;
pinfo->psamp.p_cycles = pinfo->psamp.cycles;
}
kr = libtop_pinfo_update_boosts(task, pinfo);
pinfo->psamp.total_timens = TIME_VALUE_TO_NS(&ti.user_time) +
TIME_VALUE_TO_NS(&ti.system_time);
kr = libtop_pinfo_update_cpu_usage(task, pinfo, &state);
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_total_timens = pinfo->psamp.total_timens;
pinfo->psamp.p_total_timens = pinfo->psamp.total_timens;
}
kr = libtop_pinfo_update_mach_ports(task, pinfo);
kr = libtop_pinfo_update_events_info(task, pinfo);
kr = libtop_pinfo_update_kernmem_info(task, pinfo);
kr = libtop_pinfo_update_power_info(task, pinfo);
kr = libtop_pinfo_update_vm_info(task, pinfo);
libtop_p_wq_update(pinfo);
tsamp.state_breakdown[state]++;
return LIBTOP_NO_ERR;
}
static libtop_status_t
libtop_p_proc_command(libtop_pinfo_t *pinfo, struct kinfo_proc *kinfo)
{
#if 0
uint32_t bufferSize = 30;
int result;
if (pinfo->psamp.command) {
free(pinfo->psamp.command);
pinfo->psamp.command = NULL;
}
pinfo->psamp.command = malloc(bufferSize);
if (NULL == pinfo->psamp.command)
return LIBTOP_ERR_ALLOC;
pinfo->psamp.command[0] = '\0';
proc_name(pinfo->psamp.pid, pinfo->psamp.command, bufferSize);
#if 0
if(result) {
pinfo->psamp.command[0] = '\0';
return LIBTOP_ERR_INVALID;
}
#endif
return 0;
#endif
uint32_t len;
if (pinfo->psamp.command) {
free(pinfo->psamp.command);
pinfo->psamp.command = NULL;
}
len = strlen(kinfo->kp_proc.p_comm);
if (strncmp(kinfo->kp_proc.p_comm, "LaunchCFMApp",len) != 0) {
pinfo->psamp.command = strdup(kinfo->kp_proc.p_comm);
if (!pinfo->psamp.command) {
return LIBTOP_ERR_ALLOC;
}
} else {
int mib[3];
size_t procargssize;
#ifdef TOP_JAGUAR
char *arg_end, *exec_path;
int *ip;
#else
char *cp;
#endif
char *command_beg, *command, *command_end;
assert(pinfo->psamp.pid != 0);
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pinfo->psamp.pid;
procargssize = libtop_argmax;
#ifdef TOP_JAGUAR
if (procargssize > 8192) {
procargssize = 8192;
}
#endif
if (sysctl(mib, 3, libtop_arg, &procargssize, NULL, 0) == -1) {
libtop_print(libtop_user_data, "%s(): Error in sysctl(): %s",
__FUNCTION__, strerror(errno));
return LIBTOP_ERR_INVALID;
}
#ifdef TOP_JAGUAR
arg_end = &libtop_arg[procargssize];
ip = (int *)arg_end;
ip -= 3;
for (; *ip != 0; ip--) {
if (ip == (int *)libtop_arg) {
goto ERROR;
}
}
ip++;
exec_path = (char *)ip;
command_beg = exec_path + strlen(exec_path);
for (; *command_beg == '\0'; command_beg++) {
if (command_beg >= arg_end) {
goto ERROR;
}
}
command = command_end = command_beg + strlen(command_beg) + 1;
for (command--; command >= command_beg; command--) {
if (*command == '/') {
break;
}
}
command++;
len = command_end - command;
pinfo->psamp.command = (char *)malloc(len + 1);
if (pinfo->psamp.command == NULL) {
return LIBTOP_ERR_ALLOC;
}
memcpy(pinfo->psamp.command, command, len + 1);
#else
for (cp = libtop_arg; cp < &libtop_arg[procargssize]; cp++) {
if (*cp == '\0') {
break;
}
}
if (cp == &libtop_arg[procargssize]) {
goto ERROR;
}
for (; cp < &libtop_arg[procargssize]; cp++) {
if (*cp != '\0') {
break;
}
}
if (cp == &libtop_arg[procargssize]) {
goto ERROR;
}
command_beg = cp;
for (; cp < &libtop_arg[procargssize]; cp++) {
if (*cp == '\0') {
break;
}
}
if (cp == &libtop_arg[procargssize]) {
goto ERROR;
}
command_end = command = cp;
for (command--; command >= command_beg; command--) {
if (*command == '/') {
command++;
break;
}
}
len = command_end - command;
pinfo->psamp.command = (char *)malloc(len + 1);
if (pinfo->psamp.command == NULL) {
return LIBTOP_ERR_ALLOC;
}
memcpy(pinfo->psamp.command, command, len + 1);
#endif
}
return LIBTOP_NO_ERR;
ERROR:
pinfo->psamp.command = strdup("(LaunchCFMApp)");
return LIBTOP_NO_ERR;
}
static void
libtop_p_pinsert(libtop_pinfo_t *pinfo)
{
rb_node_new(&libtop_ptree, pinfo, pnode);
rb_insert(&libtop_ptree, pinfo, libtop_p_pinfo_pid_comp,
libtop_pinfo_t, pnode);
}
static void
libtop_p_premove(libtop_pinfo_t *pinfo)
{
rb_remove(&libtop_ptree, pinfo, libtop_pinfo_t, pnode);
}
static libtop_pinfo_t *
libtop_p_psearch(pid_t pid)
{
libtop_pinfo_t *retval, key;
key.psamp.pid = pid;
rb_search(&libtop_ptree, &key, libtop_p_pinfo_pid_comp, pnode, retval);
if (retval == rb_tree_nil(&libtop_ptree)) {
retval = NULL;
}
return retval;
}
static int
libtop_p_pinfo_pid_comp(libtop_pinfo_t *a, libtop_pinfo_t *b)
{
assert(a != NULL);
assert(b != NULL);
if (a->psamp.pid < b->psamp.pid) return -1;
if (a->psamp.pid > b->psamp.pid) return 1;
return 0;
}
static int
libtop_p_pinfo_comp(libtop_pinfo_t *a, libtop_pinfo_t *b)
{
return libtop_sort(libtop_sort_data, &a->psamp, &b->psamp);
}
static libtop_oinfo_t *
libtop_p_oinfo_insert(int obj_id, int share_type, int resident_page_count,
int ref_count, int size, libtop_pinfo_t *pinfo)
{
const void *k = (const void *)(intptr_t)obj_id;
libtop_oinfo_t *oinfo = (libtop_oinfo_t *)CFDictionaryGetValue(libtop_oinfo_hash, k);
int clear_rollback = 0;
if (oinfo != NULL) {
if (oinfo->pinfo != pinfo) {
oinfo->proc_ref_count = 0;
oinfo->pinfo = pinfo;
clear_rollback = 1;
}
oinfo->size += size;
oinfo->proc_ref_count++;
} else {
oinfo = (libtop_oinfo_t *)malloc(sizeof(libtop_oinfo_t));
if (oinfo == NULL)
return NULL;
oinfo->pinfo = pinfo;
oinfo->obj_id = obj_id;
oinfo->size = size;
oinfo->share_type = share_type;
oinfo->resident_page_count = resident_page_count;
oinfo->ref_count = ref_count;
oinfo->proc_ref_count = 1;
clear_rollback = 1;
CFDictionarySetValue(libtop_oinfo_hash, k, oinfo);
}
if (clear_rollback) {
oinfo->rb_share_type = SM_EMPTY;
oinfo->rb_aliased = 0;
oinfo->rb_vprvt = 0;
oinfo->rb_rshrd = 0;
}
return oinfo;
}
static void
libtop_p_destroy_pinfo(libtop_pinfo_t *pinfo) {
libtop_p_premove(pinfo);
if(pinfo->psamp.command) {
free(pinfo->psamp.command);
pinfo->psamp.command = NULL;
}
free(pinfo);
}
static int
libtop_p_wq_update(libtop_pinfo_t *pinfo) {
#ifdef PROC_PIDWORKQUEUEINFO
struct proc_workqueueinfo wqinfo;
int result;
#endif
if (tsamp.seq > 1) {
pinfo->psamp.p_wq_nthreads = pinfo->psamp.wq_nthreads;
pinfo->psamp.p_wq_run_threads = pinfo->psamp.wq_run_threads;
pinfo->psamp.p_wq_blocked_threads = pinfo->psamp.wq_blocked_threads;
}
pinfo->psamp.wq_nthreads = 0;
pinfo->psamp.wq_run_threads = 0;
pinfo->psamp.wq_blocked_threads = 0;
#ifdef PROC_PIDWORKQUEUEINFO
result = proc_pidinfo(pinfo->psamp.pid, PROC_PIDWORKQUEUEINFO,
0ULL,
&wqinfo, PROC_PIDWORKQUEUEINFO_SIZE);
if(!result)
return -1;
pinfo->psamp.wq_nthreads = wqinfo.pwq_nthreads;
pinfo->psamp.wq_run_threads = wqinfo.pwq_runthreads;
pinfo->psamp.wq_blocked_threads = wqinfo.pwq_blockedthreads;
#endif
return 0;
}
libtop_i64_t
libtop_i64_init(uint64_t acc, int last_value) {
libtop_i64_t r;
r.accumulator = acc;
r.last_value = last_value;
if(0 == acc) {
if(last_value >= 0) {
r.accumulator += last_value;
} else {
r.accumulator += INT_MAX;
r.accumulator += abs(INT_MIN - last_value) + 1;
}
}
return r;
}
void
libtop_i64_update(struct libtop_i64 *i, int value) {
int delta = 0;
if(value >= 0) {
if(i->last_value >= 0) {
delta = value - i->last_value;
} else {
delta = value + (-i->last_value);
}
} else {
if(i->last_value >= 0) {
delta = abs(INT_MIN - value) + 1;
delta += INT_MAX - i->last_value;
} else {
delta = value - i->last_value;
}
}
i->accumulator += delta;
#ifdef LIBTOP_I64_DEBUG
if(delta == 0)
assert((value - i->last_value) == 0);
fprintf(stderr, "%s: delta %u :: value %d :: i->last_value %d\n", __func__,
delta, value, i->last_value);
fprintf(stderr, "%s: accumulator > INT_MAX %s\n", __func__,
(i->accumulator > INT_MAX) ? "YES" : "NO");
fprintf(stderr, "%s: value %x value unsigned %u > UINT_MAX %s\n", __func__,
value, value, (i->accumulator > UINT_MAX) ? "YES" : "NO");
#endif
i->last_value = value;
}
uint64_t
libtop_i64_value(libtop_i64_t *i) {
return i->accumulator;
}
static void
libtop_i64_test(void) {
libtop_i64_t i64;
int i;
int value = 3;
int incr = INT_MAX / 4;
uint64_t accum = value;
i64 = libtop_i64_init(0, value);
i = 0;
while(1) {
fprintf(stderr, "test: i %d value %d i64 value %" PRIu64 " matches? %" PRIu64 "\n", i, value, libtop_i64_value(&i64), accum);
fprintf(stderr, "+ %d\n", incr);
value += incr;
accum += incr;
libtop_i64_update(&i64, value);
++i;
}
}