#include <mach/bootstrap.h>
#include <mach/host_priv.h>
#include <mach/mach_error.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/processor_set.h>
#include <mach/task.h>
#include <mach/thread_act.h>
#include <mach/vm_region.h>
#include <mach/vm_map.h>
#include <mach/vm_prot.h>
#include <mach/shared_memory_server.h>
#define IOKIT 1
#include <device/device_types.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <fcntl.h>
#include <kvm.h>
#include <nlist.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#define LIBTOP_DBG
#ifndef LIBTOP_DBG
#ifndef NDEBUG
#define NDEBUG
#endif
#endif
#include <assert.h>
#include "libtop.h"
#include "qr.h"
#include "ql.h"
#include "rb.h"
#include "ch.h"
#include "dch.h"
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_chi_t chi;
ql_elm(libtop_oinfo_t) link;
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;
vm_size_t rb_aliased;
vm_size_t rb_vprvt;
vm_size_t rb_rshrd;
};
typedef struct libtop_user_s libtop_user_t;
struct libtop_user_s {
libtop_chi_t chi;
uid_t uid;
char username[9];
};
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 kvm_t *libtop_kvmd;
static struct nlist libtop_nlist_net[2];
static mach_port_t libtop_master_port;
static libtop_dch_t libtop_oinfo_hash;
static ql_head(libtop_oinfo_t) libtop_oinfo_list;
static unsigned libtop_oinfo_nspares;
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 libtop_dch_t libtop_uhash;
#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
(r)->tv_sec = (a)->seconds; \
(r)->tv_usec = (a)->microseconds; \
} while (0)
static boolean_t
libtop_p_print(void *a_user_data, const char *a_format, ...);
static int
libtop_p_mach_state_order(int a_state, long a_sleep_time);
static boolean_t
libtop_p_kread(u_long a_addr, void *r_buf, size_t a_nbytes);
static boolean_t
libtop_p_load_get(host_cpu_load_info_t r_load);
static boolean_t
libtop_p_loadavg_update(void);
static void
libtop_p_fw_sample(boolean_t a_fw);
static boolean_t
libtop_p_vm_sample(void);
static void
libtop_p_networks_sample(void);
static boolean_t
libtop_p_disks_sample(void);
static boolean_t
libtop_p_proc_table_read(boolean_t a_reg);
static boolean_t
libtop_p_task_update(task_t a_task, boolean_t a_reg);
static boolean_t
libtop_p_proc_command(libtop_pinfo_t *a_pinfo, struct kinfo_proc *a_kinfo);
static void
libtop_p_pinsert(libtop_pinfo_t *a_pinfo);
static void
libtop_p_premove(libtop_pinfo_t *a_pinfo);
static libtop_pinfo_t *
libtop_p_psearch(pid_t a_pid);
static int
libtop_p_pinfo_pid_comp(libtop_pinfo_t *a_a, libtop_pinfo_t *a_b);
static int
libtop_p_pinfo_comp(libtop_pinfo_t *a_a, libtop_pinfo_t *a_b);
static libtop_user_t *
libtop_p_usearch(uid_t a_uid);
static void
libtop_p_oinfo_init(void);
static void
libtop_p_oinfo_fini(void);
static libtop_oinfo_t *
libtop_p_oinfo_insert(int a_obj_id, int a_share_type, int a_resident_page_count,
int a_ref_count, int a_size, libtop_pinfo_t *a_pinfo);
static void
libtop_p_oinfo_reset(void);
boolean_t
libtop_init(libtop_print_t *a_print, void *a_user_data)
{
boolean_t retval;
char errbuf[_POSIX2_LINE_MAX];
if (a_print != NULL) {
libtop_print = a_print;
libtop_user_data = a_user_data;
} else {
libtop_print = libtop_p_print;
libtop_user_data = NULL;
}
tsamp.seq = 0;
libtop_port = mach_host_self();
host_page_size(libtop_port, &tsamp.pagesize);
rb_tree_new(&libtop_ptree, pnode);
dch_new(&libtop_uhash, 32, 24, 0, ch_direct_hash, ch_direct_key_comp);
libtop_p_oinfo_init();
{
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));
retval = TRUE;
goto RETURN;
}
libtop_arg = (char *)malloc(libtop_argmax);
if (libtop_arg == NULL) {
retval = TRUE;
goto RETURN;
}
}
libtop_kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
if (libtop_kvmd == NULL) {
libtop_print(libtop_user_data,
"Error in kvm_openfiles(): %s", errbuf);
retval = TRUE;
goto RETURN;
}
libtop_nlist_net[0].n_name = "_ifnet";
libtop_nlist_net[1].n_name = NULL;
if (kvm_nlist(libtop_kvmd, libtop_nlist_net) < 0) {
libtop_print(libtop_user_data,
"Error in kvm_nlist(): %s", kvm_geterr(libtop_kvmd));
retval = TRUE;
goto RETURN;
}
if (libtop_nlist_net[0].n_type == N_UNDF) {
libtop_print(libtop_user_data, "No nlist for _ifnet");
retval = TRUE;
goto RETURN;
}
if (IOMasterPort(bootstrap_port, &libtop_master_port)) {
libtop_print(libtop_user_data, "Error in IOMasterPort()");
retval = TRUE;
goto RETURN;
}
if (libtop_p_load_get(&tsamp.b_cpu)) {
retval = TRUE;
goto RETURN;
}
tsamp.p_cpu = tsamp.b_cpu;
tsamp.cpu = tsamp.b_cpu;
gettimeofday(&tsamp.b_time, NULL);
tsamp.p_time = tsamp.b_time;
tsamp.time = tsamp.b_time;
retval = FALSE;
RETURN:
return retval;
}
void
libtop_fini(void)
{
libtop_pinfo_t *pinfo, *ppinfo;
libtop_user_t *user;
free(libtop_arg);
libtop_p_oinfo_fini();
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_premove(pinfo);
if (pinfo->psamp.command != NULL) {
free(pinfo->psamp.command);
}
free(pinfo);
}
for (; dch_remove_iterate(&libtop_uhash, NULL, (void **)&user, NULL)
== FALSE;) {
free(user);
}
dch_delete(&libtop_uhash);
}
boolean_t
libtop_sample(boolean_t a_reg, boolean_t a_fw)
{
boolean_t retval;
tsamp.seq++;
libtop_sorted = FALSE;
libtop_piter = NULL;
memset(tsamp.state_breakdown, 0, sizeof(tsamp.state_breakdown));
if (tsamp.seq != 1) {
tsamp.p_time = tsamp.time;
gettimeofday(&tsamp.time, NULL);
}
if (libtop_p_proc_table_read(a_reg)
|| libtop_p_loadavg_update()) {
retval = TRUE;
goto RETURN;
}
tsamp.p_cpu = tsamp.cpu;
if (libtop_p_load_get(&tsamp.cpu)) {
retval = TRUE;
goto RETURN;
}
libtop_p_fw_sample(a_fw);
if (libtop_p_vm_sample()) {
retval = TRUE;
goto RETURN;
}
libtop_p_networks_sample();
if (libtop_p_disks_sample()) {
retval = TRUE;
goto RETURN;
}
retval = FALSE;
RETURN:
return retval;
}
const libtop_tsamp_t *
libtop_tsamp(void)
{
return &tsamp;
}
void
libtop_psort(libtop_sort_t *a_sort, void *a_data)
{
libtop_pinfo_t *pinfo, *ppinfo;
assert(tsamp.seq != 0);
libtop_piter = NULL;
rb_tree_new(&libtop_stree, snode);
libtop_sorted = TRUE;
libtop_sort = a_sort;
libtop_sort_data = a_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_premove(pinfo);
if (pinfo->psamp.command != NULL) {
free(pinfo->psamp.command);
}
free(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_premove(pinfo);
if (pinfo->psamp.command != NULL) {
free(pinfo->psamp.command);
}
free(pinfo);
dead = TRUE;
}
} while (dead);
}
return &libtop_piter->psamp;
}
boolean_t
libtop_preg(pid_t a_pid, libtop_preg_t a_preg)
{
boolean_t retval;
libtop_pinfo_t *pinfo;
pinfo = libtop_p_psearch(a_pid);
if (pinfo == NULL) {
retval = TRUE;
goto RETURN;
}
pinfo->preg = a_preg;
retval = FALSE;
RETURN:
return retval;
}
const char *
libtop_username(uid_t a_uid)
{
const char *retval;
libtop_user_t *user;
user = libtop_p_usearch(a_uid);
if (user == NULL) {
retval = NULL;
goto RETURN;
}
retval = user->username;
RETURN:
return retval;
}
const char *
libtop_state_str(unsigned a_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(a_state <= LIBTOP_STATE_MAX);
return strings[a_state];
}
static boolean_t
libtop_p_print(void *a_user_data, const char *a_format, ...)
{
return FALSE;
}
static int
libtop_p_mach_state_order(int a_state, long a_sleep_time)
{
int retval;
switch (a_state) {
case TH_STATE_RUNNING:
retval = LIBTOP_STATE_RUN;
break;
case TH_STATE_UNINTERRUPTIBLE:
retval = LIBTOP_STATE_STUCK;
break;
case TH_STATE_WAITING:
if (a_sleep_time > 0) {
retval = LIBTOP_STATE_IDLE;
} else {
retval = LIBTOP_STATE_SLEEP;
}
break;
case TH_STATE_STOPPED:
retval = LIBTOP_STATE_STOP;
break;
case TH_STATE_HALTED:
retval = LIBTOP_STATE_HALT;
break;
default:
retval = LIBTOP_STATE_UNKNOWN;
break;
}
return retval;
}
static boolean_t
libtop_p_kread(u_long a_addr, void *r_buf, size_t a_nbytes)
{
boolean_t retval;
assert(r_buf != NULL);
if (kvm_read(libtop_kvmd, a_addr, r_buf, a_nbytes) != a_nbytes) {
libtop_print(libtop_user_data, "Error in kvm_read(): %s",
kvm_geterr(libtop_kvmd));
retval = TRUE;
goto RETURN;
}
retval = FALSE;
RETURN:
return retval;
}
static boolean_t
libtop_p_load_get(host_cpu_load_info_t r_load)
{
boolean_t retval;
kern_return_t error;
mach_msg_type_number_t count;
count = HOST_CPU_LOAD_INFO_COUNT;
error = host_statistics(libtop_port, HOST_CPU_LOAD_INFO,
(host_info_t)r_load, &count);
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data, "Error in host_statistics(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
retval = FALSE;
RETURN:
return retval;
}
static boolean_t
libtop_p_loadavg_update(void)
{
boolean_t retval;
int mib[2];
size_t size;
struct loadavg loadavg;
mib[0] = CTL_VM;
mib[1] = VM_LOADAVG;
size = sizeof(loadavg);
if (sysctl(mib, 2, &loadavg, &size, NULL, 0) == -1) {
libtop_print(libtop_user_data,
"%s(): Error in sysctl(): %s",
__FUNCTION__, strerror(errno));
retval = TRUE;
goto RETURN;
}
tsamp.loadavg[0] = (float)loadavg.ldavg[0] / (float)loadavg.fscale;
tsamp.loadavg[1] = (float)loadavg.ldavg[1] / (float)loadavg.fscale;
tsamp.loadavg[2] = (float)loadavg.ldavg[2] / (float)loadavg.fscale;
retval = FALSE;
RETURN:
return retval;
}
static void
libtop_p_fw_sample(boolean_t a_fw)
{
boolean_t fw_seen;
vm_region_submap_info_data_64_t sinfo;
mach_msg_type_number_t count;
vm_size_t size;
int depth;
vm_address_t addr;
tsamp.fw_count = 0;
tsamp.fw_code = 0;
tsamp.fw_data = 0;
tsamp.fw_linkedit = 0;
if (a_fw) {
for (fw_seen = FALSE, addr = GLOBAL_SHARED_TEXT_SEGMENT;
addr < (GLOBAL_SHARED_DATA_SEGMENT
+ SHARED_DATA_REGION_SIZE);
addr += size
) {
depth = 1;
count = VM_REGION_SUBMAP_INFO_COUNT_64;
if (vm_region_recurse_64(mach_task_self(), &addr, &size,
&depth, (vm_region_info_t)&sinfo, &count)
!= KERN_SUCCESS) {
break;
}
if (addr >= (GLOBAL_SHARED_DATA_SEGMENT
+ SHARED_DATA_REGION_SIZE)) {
break;
}
if (addr < GLOBAL_SHARED_DATA_SEGMENT) {
if (sinfo.share_mode == SM_SHARED
|| sinfo.share_mode == SM_COW) {
if (sinfo.max_protection
& VM_PROT_EXECUTE) {
tsamp.fw_code
+= sinfo.pages_resident
* tsamp.pagesize;
if (fw_seen == FALSE) {
tsamp.fw_count++;
fw_seen = TRUE;
}
} else {
tsamp.fw_linkedit
+= sinfo.pages_resident
* tsamp.pagesize;
fw_seen = FALSE;
}
}
} else {
if (sinfo.share_mode == SM_SHARED
|| sinfo.share_mode == SM_COW
|| sinfo.share_mode == SM_TRUESHARED) {
tsamp.fw_data += sinfo.pages_resident
* tsamp.pagesize;
}
}
tsamp.fw_vsize += size;
}
}
}
static boolean_t
libtop_p_vm_sample(void)
{
boolean_t retval;
mach_msg_type_number_t count;
kern_return_t error;
unsigned i, ocount;
libtop_oinfo_t *oinfo;
tsamp.p_vm_stat = tsamp.vm_stat;
count = sizeof(tsamp.vm_stat) / sizeof(integer_t);
error = host_statistics(libtop_port, HOST_VM_INFO,
(host_info_t)&tsamp.vm_stat, &count);
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data, "Error in host_statistics(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
if (tsamp.seq == 1) {
tsamp.p_vm_stat = tsamp.vm_stat;
tsamp.b_vm_stat = tsamp.vm_stat;
}
tsamp.rshrd = 0;
for (i = 0, ocount = dch_count(&libtop_oinfo_hash);
i < ocount;
i++) {
dch_get_iterate(&libtop_oinfo_hash, NULL, (void **)&oinfo);
if (oinfo->share_type == SM_SHARED
|| oinfo->share_type == SM_COW) {
tsamp.rshrd += oinfo->resident_page_count;
}
}
tsamp.rshrd *= tsamp.pagesize;
retval = FALSE;
RETURN:
return retval;
}
static void
libtop_p_networks_sample(void)
{
struct ifnet ifnet;
struct ifnethead ifnethead;
u_long off;
char tname[16];
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;
if (libtop_nlist_net[0].n_value != 0
&& libtop_p_kread(libtop_nlist_net[0].n_value, &ifnethead,
sizeof(ifnethead)) == FALSE) {
for (off = (u_long)ifnethead.tqh_first;
off != 0;
off = (u_long)ifnet.if_link.tqe_next) {
if (libtop_p_kread(off, &ifnet, sizeof(ifnet))) {
break;
}
if (libtop_p_kread((u_long)ifnet.if_name, tname,
sizeof(tname))) {
break;
}
if (strncmp(tname, "lo", 2)) {
tsamp.net_ipackets += ifnet.if_ipackets;
tsamp.net_opackets += ifnet.if_opackets;
tsamp.net_ibytes += ifnet.if_ibytes;
tsamp.net_obytes += ifnet.if_obytes;
}
}
}
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;
}
}
static boolean_t
libtop_p_disks_sample(void)
{
boolean_t 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()");
retval = TRUE;
goto ERROR_NOLIST;
}
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,
kNilOptions)) {
libtop_print(libtop_user_data,
"Error in IORegistryEntryCreateCFProperties()");
retval = TRUE;
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 = FALSE;
RETURN:
IOObjectRelease(drive_list);
ERROR_NOLIST:
return retval;
}
static boolean_t
libtop_p_proc_table_read(boolean_t a_reg)
{
boolean_t retval;
kern_return_t error;
processor_set_t *psets, pset;
task_t *tasks;
unsigned i, j, pcnt, tcnt;
error = host_processor_sets(libtop_port, &psets, &pcnt);
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in host_processor_sets(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
for (i = 0; i < pcnt; i++) {
error = host_processor_set_priv(libtop_port, psets[i], &pset);
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in host_processor_set_priv(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
error = processor_set_tasks(pset, &tasks, &tcnt);
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in processor_set_tasks(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
tsamp.reg = 0;
tsamp.fw_private = 0;
tsamp.fw_vsize = 0;
tsamp.rprvt = 0;
tsamp.vsize = 0;
tsamp.threads = 0;
libtop_p_oinfo_reset();
for (j = 0; j < tcnt; j++) {
if (libtop_p_task_update(tasks[j], a_reg)) {
retval = TRUE;
goto RETURN;
}
if (tasks[j] != mach_task_self()) {
mach_port_deallocate(mach_task_self(),
tasks[j]);
}
}
error = vm_deallocate((vm_map_t)mach_task_self(),
(vm_address_t)tasks, tcnt * sizeof(task_t));
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in vm_deallocate(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
if ((error = mach_port_deallocate(mach_task_self(),
pset)) != KERN_SUCCESS
|| (error = mach_port_deallocate(mach_task_self(),
psets[i])) != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in mach_port_deallocate(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
}
error = vm_deallocate((vm_map_t)mach_task_self(),
(vm_address_t)psets, pcnt * sizeof(processor_set_t));
if (error != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in vm_deallocate(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
retval = FALSE;
RETURN:
return retval;
}
static boolean_t
libtop_p_task_update(task_t a_task, boolean_t a_reg)
{
boolean_t retval;
kern_return_t error;
struct kinfo_proc kinfo;
size_t kinfosize;
int pid, mib[4];
mach_msg_type_number_t count;
vm_size_t aliased;
libtop_pinfo_t *pinfo;
libtop_oinfo_t *oinfo;
task_basic_info_data_t ti;
struct timeval tv;
vm_address_t address;
mach_port_t object_name;
vm_region_top_info_data_t info;
vm_size_t size;
int state, tstate;
thread_array_t thread_table;
unsigned int table_size;
thread_basic_info_t thi;
thread_basic_info_data_t thi_data;
unsigned i;
mach_port_array_t names, types;
unsigned ncnt, tcnt;
state = LIBTOP_STATE_ZOMBIE;
error = pid_for_task(a_task, &pid);
if (error != KERN_SUCCESS) {
retval = FALSE;
goto GONE;
}
kinfosize = sizeof(struct kinfo_proc);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
if (sysctl(mib, 4, &kinfo, &kinfosize, NULL, 0) == -1) {
libtop_print(libtop_user_data,
"%s(): Error in sysctl(): %s", __FUNCTION__,
strerror(errno));
retval = TRUE;
goto RETURN;
}
if (kinfo.kp_proc.p_stat == 0) {
retval = FALSE;
goto RETURN;
}
pinfo = libtop_p_psearch((pid_t)pid);
if (pinfo == NULL) {
pinfo = (libtop_pinfo_t *)calloc(1, sizeof(libtop_pinfo_t));
if (pinfo == NULL) {
retval = TRUE;
goto RETURN;
}
pinfo->psamp.pid = (pid_t)pid;
libtop_p_pinsert(pinfo);
}
if (libtop_p_proc_command(pinfo, &kinfo)) {
retval = TRUE;
goto RETURN;
}
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.p_seq = pinfo->psamp.seq;
pinfo->psamp.seq = tsamp.seq;
count = TASK_BASIC_INFO_COUNT;
error = task_info(a_task, TASK_BASIC_INFO, (task_info_t)&ti, &count);
if (error != KERN_SUCCESS) {
state = LIBTOP_STATE_ZOMBIE;
retval = FALSE;
goto GONE;
}
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;
aliased = 0;
pinfo->psamp.rprvt = 0;
pinfo->psamp.vprvt = 0;
pinfo->psamp.rshrd = 0;
pinfo->psamp.reg = 0;
pinfo->psamp.rsize = ti.resident_size;
pinfo->psamp.vsize = ti.virtual_size;
if ((a_reg && pinfo->preg != LIBTOP_PREG_off)
|| pinfo->preg == LIBTOP_PREG_on
|| pinfo->psamp.p_seq == 0
|| (pinfo->split && pinfo->psamp.vsize
< (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE))
) {
for (address = 0, pinfo->split = FALSE;
;
address += size) {
count = VM_REGION_TOP_INFO_COUNT;
if (vm_region(a_task, &address, &size,
VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count,
&object_name) != KERN_SUCCESS) {
break;
}
if (address >= GLOBAL_SHARED_TEXT_SEGMENT
&& address < (GLOBAL_SHARED_DATA_SEGMENT
+ SHARED_DATA_REGION_SIZE)) {
tsamp.fw_private += info.private_pages_resident
* tsamp.pagesize;
if (pinfo->split == FALSE && info.share_mode
== SM_EMPTY) {
vm_region_basic_info_data_64_t b_info;
count = VM_REGION_BASIC_INFO_COUNT_64;
if (vm_region_64(a_task, &address,
&size, VM_REGION_BASIC_INFO,
(vm_region_info_t)&b_info, &count,
&object_name) != KERN_SUCCESS) {
break;
}
if (b_info.reserved) {
pinfo->split = TRUE;
}
}
if (info.share_mode != SM_PRIVATE) {
continue;
}
}
pinfo->psamp.reg++;
switch (info.share_mode) {
case SM_COW: {
if (info.ref_count == 1) {
pinfo->psamp.vprvt += size;
pinfo->psamp.rprvt
+= info.shared_pages_resident
* tsamp.pagesize;
} else {
if (pinfo->psamp.pid == 0) {
pinfo->psamp.rprvt
+= info.private_pages_resident
* tsamp.pagesize;
pinfo->psamp.vprvt
+= info.private_pages_resident
* tsamp.pagesize;
break;
}
oinfo
= libtop_p_oinfo_insert(info.obj_id,
SM_COW, info.shared_pages_resident,
info.ref_count, size, pinfo);
if (oinfo == NULL) {
retval = TRUE;
goto RETURN;
}
if (oinfo->proc_ref_count > 1) {
if (oinfo->rb_share_type
!= SM_EMPTY) {
oinfo->share_type
= oinfo->rb_share_type;
}
aliased -= oinfo->rb_aliased;
pinfo->psamp.vprvt
-= oinfo->rb_vprvt;
pinfo->psamp.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
* tsamp.pagesize;
aliased
+= oinfo->resident_page_count
* tsamp.pagesize;
oinfo->rb_vprvt += oinfo->size;
pinfo->psamp.vprvt
+= oinfo->size;
}
if (oinfo->share_type
!= SM_PRIVATE_ALIASED) {
oinfo->rb_rshrd
+= oinfo->resident_page_count
* tsamp.pagesize;
pinfo->psamp.rshrd
+= oinfo->resident_page_count
* tsamp.pagesize;
}
pinfo->psamp.vprvt
+= info.private_pages_resident
* tsamp.pagesize;
}
pinfo->psamp.rprvt
+= info.private_pages_resident
* tsamp.pagesize;
break;
}
case SM_PRIVATE: {
pinfo->psamp.rprvt
+= info.private_pages_resident
* tsamp.pagesize;
pinfo->psamp.vprvt += size;
break;
}
case SM_EMPTY:
break;
case SM_SHARED: {
if (pinfo->psamp.pid == 0) {
break;
}
oinfo = libtop_p_oinfo_insert(info.obj_id,
SM_SHARED, info.shared_pages_resident,
info.ref_count, size, pinfo);
if (oinfo == NULL) {
retval = TRUE;
goto RETURN;
}
if (oinfo->proc_ref_count > 1) {
if (oinfo->rb_share_type != SM_EMPTY) {
oinfo->share_type
= oinfo->rb_share_type;
}
aliased -= oinfo->rb_aliased;
pinfo->psamp.vprvt -= oinfo->rb_vprvt;
pinfo->psamp.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
* tsamp.pagesize;
aliased
+= oinfo->resident_page_count
* tsamp.pagesize;
oinfo->rb_vprvt += oinfo->size;
pinfo->psamp.vprvt += oinfo->size;
}
if (oinfo->share_type != SM_PRIVATE_ALIASED) {
oinfo->rb_rshrd
+= oinfo->resident_page_count
* tsamp.pagesize;
pinfo->psamp.rshrd
+= oinfo->resident_page_count
* tsamp.pagesize;
}
break;
}
default:
assert(0);
break;
}
}
pinfo->psamp.rprvt += aliased;
tsamp.reg += pinfo->psamp.reg;
tsamp.rprvt += pinfo->psamp.rprvt;
}
if (pinfo->split) {
pinfo->psamp.vsize -= (SHARED_TEXT_REGION_SIZE
+ SHARED_DATA_REGION_SIZE);
}
tsamp.vsize += pinfo->psamp.vsize;
pinfo->psamp.p_total_time = pinfo->psamp.total_time;
TIME_VALUE_TO_TIMEVAL(&ti.user_time, &pinfo->psamp.total_time);
TIME_VALUE_TO_TIMEVAL(&ti.system_time, &tv);
timeradd(&pinfo->psamp.total_time, &tv, &pinfo->psamp.total_time);
state = LIBTOP_STATE_MAX;
pinfo->psamp.state = LIBTOP_STATE_MAX;
error = task_threads(a_task, &thread_table, &table_size);
if (error != KERN_SUCCESS) {
state = LIBTOP_STATE_ZOMBIE;
retval = FALSE;
goto RETURN;
}
pinfo->psamp.th = table_size;
tsamp.threads += table_size;
thi = &thi_data;
for (i = 0; i < table_size; i++) {
count = THREAD_BASIC_INFO_COUNT;
if (thread_info(thread_table[i], THREAD_BASIC_INFO,
(thread_info_t)thi, &count) == KERN_SUCCESS) {
if ((thi->flags & TH_FLAGS_IDLE) == 0) {
TIME_VALUE_TO_TIMEVAL(&thi->user_time, &tv);
timeradd(&pinfo->psamp.total_time, &tv,
&pinfo->psamp.total_time);
TIME_VALUE_TO_TIMEVAL(&thi->system_time, &tv);
timeradd(&pinfo->psamp.total_time, &tv,
&pinfo->psamp.total_time);
}
tstate = libtop_p_mach_state_order(thi->run_state,
thi->sleep_time);
if (tstate < state) {
state = tstate;
pinfo->psamp.state = tstate;
}
}
if (a_task != mach_task_self()) {
if ((error = mach_port_deallocate(mach_task_self(),
thread_table[i])) != KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in mach_port_deallocate(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
}
}
if ((error = vm_deallocate(mach_task_self(), (vm_offset_t)thread_table,
table_size * sizeof(thread_array_t)) != KERN_SUCCESS)) {
libtop_print(libtop_user_data,
"Error in vm_deallocate(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_total_time = pinfo->psamp.total_time;
pinfo->psamp.p_total_time = pinfo->psamp.total_time;
}
pinfo->psamp.p_prt = pinfo->psamp.prt;
if (mach_port_names(a_task, &names, &ncnt, &types, &tcnt)
!= KERN_SUCCESS) {
pinfo->psamp.prt = 0;
state = LIBTOP_STATE_ZOMBIE;
retval = FALSE;
goto RETURN;
} else {
pinfo->psamp.prt = ncnt;
if ((error = vm_deallocate(mach_task_self(), (vm_offset_t)names,
ncnt * sizeof(mach_port_array_t))) != KERN_SUCCESS
|| (error = vm_deallocate(mach_task_self(),
(vm_offset_t)types, tcnt * sizeof(mach_port_array_t)))
!= KERN_SUCCESS) {
libtop_print(libtop_user_data,
"Error in vm_deallocate(): %s",
mach_error_string(error));
retval = TRUE;
goto RETURN;
}
}
pinfo->psamp.p_events = pinfo->psamp.events;
count = TASK_EVENTS_INFO_COUNT;
if (task_info(a_task, TASK_EVENTS_INFO,
(task_info_t)&pinfo->psamp.events, &count) != KERN_SUCCESS) {
state = LIBTOP_STATE_ZOMBIE;
retval = FALSE;
goto RETURN;
} else {
if (pinfo->psamp.p_seq == 0) {
pinfo->psamp.b_events = pinfo->psamp.events;
pinfo->psamp.p_events = pinfo->psamp.events;
}
}
retval = FALSE;
RETURN:
tsamp.state_breakdown[state]++;
GONE:
return retval;
}
static boolean_t
libtop_p_proc_command(libtop_pinfo_t *a_pinfo, struct kinfo_proc *a_kinfo)
{
boolean_t retval;
unsigned len;
if (a_pinfo->psamp.command != NULL) {
free(a_pinfo->psamp.command);
a_pinfo->psamp.command = NULL;
}
len = strlen(a_kinfo->kp_proc.p_comm);
if (strncmp(a_kinfo->kp_proc.p_comm, "LaunchCFMApp",len)) {
a_pinfo->psamp.command = (char *)malloc(len + 1);
if (a_pinfo->psamp.command == NULL) {
retval = TRUE;
goto RETURN;
}
memcpy(a_pinfo->psamp.command, a_kinfo->kp_proc.p_comm,
len + 1);
} 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(a_pinfo->psamp.pid != 0);
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS;
mib[2] = a_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));
retval = TRUE;
goto RETURN;
}
#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;
a_pinfo->psamp.command = (char *)malloc(len + 1);
if (a_pinfo->psamp.command == NULL) {
retval = TRUE;
goto RETURN;
}
memcpy(a_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;
a_pinfo->psamp.command = (char *)malloc(len + 1);
if (a_pinfo->psamp.command == NULL) {
retval = TRUE;
goto RETURN;
}
memcpy(a_pinfo->psamp.command, command, len + 1);
#endif
}
retval = FALSE;
RETURN:
return retval;
ERROR:
{
static const char s[] = "(LaunchCFMApp)";
a_pinfo->psamp.command = malloc(sizeof(s));
if (a_pinfo->psamp.command == NULL) {
retval = TRUE;
goto RETURN;
}
memcpy(a_pinfo->psamp.command, s, sizeof(s));
retval = FALSE;
goto RETURN;
}
}
static void
libtop_p_pinsert(libtop_pinfo_t *a_pinfo)
{
rb_node_new(&libtop_ptree, a_pinfo, pnode);
rb_insert(&libtop_ptree, a_pinfo, libtop_p_pinfo_pid_comp,
libtop_pinfo_t, pnode);
}
static void
libtop_p_premove(libtop_pinfo_t *a_pinfo)
{
rb_remove(&libtop_ptree, a_pinfo, libtop_pinfo_t, pnode);
}
static libtop_pinfo_t *
libtop_p_psearch(pid_t a_pid)
{
libtop_pinfo_t *retval, key;
key.psamp.pid = a_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_a, libtop_pinfo_t *a_b)
{
int retval;
if (a_a->psamp.pid < a_b->psamp.pid) {
retval = -1;
} else if (a_a->psamp.pid > a_b->psamp.pid) {
retval = 1;
} else {
retval = 0;
}
return retval;
}
static int
libtop_p_pinfo_comp(libtop_pinfo_t *a_a, libtop_pinfo_t *a_b)
{
int retval;
retval = libtop_sort(libtop_sort_data, &a_a->psamp, &a_b->psamp);
return retval;
}
static libtop_user_t *
libtop_p_usearch(uid_t a_uid)
{
libtop_user_t *retval;
if (dch_search(&libtop_uhash, (void *)a_uid, (void **)&retval)) {
struct passwd *pwd;
retval = (libtop_user_t *)malloc(sizeof(libtop_user_t));
if (retval == NULL) {
goto RETURN;
}
retval->uid = a_uid;
setpwent();
pwd = getpwuid(a_uid);
if (pwd == NULL) {
retval = NULL;
goto RETURN;
} else {
snprintf(retval->username, sizeof(retval->username),
"%s", pwd->pw_name);
}
endpwent();
dch_insert(&libtop_uhash, (void *)retval->uid, (void *)retval,
&retval->chi);
}
RETURN:
return retval;
}
static void
libtop_p_oinfo_init(void)
{
dch_new(&libtop_oinfo_hash, 4096, 3072, 0,
ch_direct_hash, ch_direct_key_comp);
ql_new(&libtop_oinfo_list);
libtop_oinfo_nspares = 0;
}
static void
libtop_p_oinfo_fini(void)
{
libtop_oinfo_t *oinfo;
for (oinfo = ql_last(&libtop_oinfo_list, link);
oinfo != NULL;
oinfo = ql_last(&libtop_oinfo_list, link)) {
ql_tail_remove(&libtop_oinfo_list, libtop_oinfo_t, link);
free(oinfo);
}
}
static libtop_oinfo_t *
libtop_p_oinfo_insert(int a_obj_id, int a_share_type, int a_resident_page_count,
int a_ref_count, int a_size, libtop_pinfo_t *a_pinfo)
{
libtop_oinfo_t *oinfo;
if (dch_search(&libtop_oinfo_hash, (void *)a_obj_id, (void **)&oinfo)
== FALSE) {
if (oinfo->pinfo != a_pinfo) {
oinfo->proc_ref_count = 0;
oinfo->pinfo = a_pinfo;
}
oinfo->size += a_size;
oinfo->proc_ref_count++;
} else {
if (libtop_oinfo_nspares > 0) {
oinfo = ql_first(&libtop_oinfo_list);
ql_first(&libtop_oinfo_list) = qr_next(oinfo, link);
libtop_oinfo_nspares--;
} else {
assert(libtop_oinfo_nspares == 0);
oinfo
= (libtop_oinfo_t *)malloc(sizeof(libtop_oinfo_t));
if (oinfo == NULL) {
goto RETURN;
}
ql_elm_new(oinfo, link);
ql_tail_insert(&libtop_oinfo_list, oinfo, link);
}
oinfo->pinfo = a_pinfo;
oinfo->obj_id = a_obj_id;
oinfo->size = a_size;
oinfo->share_type = a_share_type;
oinfo->resident_page_count = a_resident_page_count;
oinfo->ref_count = a_ref_count;
oinfo->proc_ref_count = 1;
dch_insert(&libtop_oinfo_hash, (void *)a_obj_id, (void *)oinfo,
&oinfo->chi);
}
RETURN:
return oinfo;
}
static void
libtop_p_oinfo_reset(void)
{
libtop_oinfo_nspares += dch_count(&libtop_oinfo_hash);
dch_clear(&libtop_oinfo_hash);
}