From 43ef703f034be4e347ab94250101cd8f23409952 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Mon, 13 Jul 2015 01:17:14 -0500 Subject: [PATCH] Start supporting actual data --- darwin/DarwinCRT.c | 1 + darwin/DarwinProcess.c | 286 ++++++++++++++++++++++++++++++++++++- darwin/DarwinProcess.h | 16 ++- darwin/DarwinProcessList.c | 128 ++++++++++++----- darwin/DarwinProcessList.h | 18 +++ darwin/Platform.c | 25 ++-- darwin/Platform.h | 2 - 7 files changed, 427 insertions(+), 49 deletions(-) diff --git a/darwin/DarwinCRT.c b/darwin/DarwinCRT.c index 49f98cdc..e998f000 100644 --- a/darwin/DarwinCRT.c +++ b/darwin/DarwinCRT.c @@ -9,6 +9,7 @@ in the source distribution for its full text. #include "CRT.h" #include #include +#include void CRT_handleSIGSEGV(int sgn) { (void) sgn; diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 7054cf46..64388490 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -7,12 +7,14 @@ in the source distribution for its full text. #include "Process.h" #include "DarwinProcess.h" + #include +#include +#include /*{ #include "Settings.h" - -#define Process_delete UnsupportedProcess_delete +#include }*/ @@ -23,7 +25,7 @@ Process* DarwinProcess_new(Settings* settings) { return this; } -void DarwinProcess_delete(Object* cast) { +void Process_delete(Object* cast) { Process* this = (Process*) cast; Object_setClass(this, Class(Process)); Process_done((Process*)cast); @@ -31,3 +33,281 @@ void DarwinProcess_delete(Object* cast) { free(this); } +bool Process_isThread(Process* this) { + return false; +} + +void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now) { + struct tm date; + + proc->starttime_ctime = ep->p_starttime.tv_sec; + (void) localtime_r(&proc->starttime_ctime, &date); + strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date); +} + +char *DarwinProcessList_getCmdLine(struct kinfo_proc* k, int show_args ) { + /* This function is from the old Mac version of htop. Originally from ps? */ + int mib[3], argmax, nargs, c = 0; + size_t size; + char *procargs, *sp, *np, *cp, *retval; + + /* Get the maximum process arguments size. */ + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof( argmax ); + if ( sysctl( mib, 2, &argmax, &size, NULL, 0 ) == -1 ) { + goto ERROR_A; + } + + /* Allocate space for the arguments. */ + procargs = ( char * ) malloc( argmax ); + if ( procargs == NULL ) { + goto ERROR_A; + } + + /* + * Make a sysctl() call to get the raw argument space of the process. + * The layout is documented in start.s, which is part of the Csu + * project. In summary, it looks like: + * + * /---------------\ 0x00000000 + * : : + * : : + * |---------------| + * | argc | + * |---------------| + * | arg[0] | + * |---------------| + * : : + * : : + * |---------------| + * | arg[argc - 1] | + * |---------------| + * | 0 | + * |---------------| + * | env[0] | + * |---------------| + * : : + * : : + * |---------------| + * | env[n] | + * |---------------| + * | 0 | + * |---------------| <-- Beginning of data returned by sysctl() is here. + * | argc | + * |---------------| + * | exec_path | + * |:::::::::::::::| + * | | + * | String area. | + * | | + * |---------------| <-- Top of stack. + * : : + * : : + * \---------------/ 0xffffffff + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = k->kp_proc.p_pid; + + size = ( size_t ) argmax; + if ( sysctl( mib, 3, procargs, &size, NULL, 0 ) == -1 ) { + goto ERROR_B; + } + + memcpy( &nargs, procargs, sizeof( nargs ) ); + cp = procargs + sizeof( nargs ); + + /* Skip the saved exec_path. */ + for ( ; cp < &procargs[size]; cp++ ) { + if ( *cp == '\0' ) { + /* End of exec_path reached. */ + break; + } + } + if ( cp == &procargs[size] ) { + goto ERROR_B; + } + + /* Skip trailing '\0' characters. */ + for ( ; cp < &procargs[size]; cp++ ) { + if ( *cp != '\0' ) { + /* Beginning of first argument reached. */ + break; + } + } + if ( cp == &procargs[size] ) { + goto ERROR_B; + } + /* Save where the argv[0] string starts. */ + sp = cp; + + /* + * Iterate through the '\0'-terminated strings and convert '\0' to ' ' + * until a string is found that has a '=' character in it (or there are + * no more strings in procargs). There is no way to deterministically + * know where the command arguments end and the environment strings + * start, which is why the '=' character is searched for as a heuristic. + */ + for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) { + if ( *cp == '\0' ) { + c++; + if ( np != NULL ) { + /* Convert previous '\0'. */ + *np = ' '; + } + /* Note location of current '\0'. */ + np = cp; + + if ( !show_args ) { + /* + * Don't convert '\0' characters to ' '. + * However, we needed to know that the + * command name was terminated, which we + * now know. + */ + break; + } + } + } +#if 0 + /* + * If eflg is non-zero, continue converting '\0' characters to ' ' + * characters until no more strings that look like environment settings + * follow. + */ + if ( ( eflg != 0 ) + && ( ( getuid( ) == 0 ) + || ( k->kp_eproc.e_pcred.p_ruid == getuid( ) ) ) ) { + for ( ; cp < &procargs[size]; cp++ ) { + if ( *cp == '\0' ) { + if ( np != NULL ) { + if ( &np[1] == cp ) { + /* + * Two '\0' characters in a row. + * This should normally only + * happen after all the strings + * have been seen, but in any + * case, stop parsing. + */ + break; + } + /* Convert previous '\0'. */ + *np = ' '; + } + /* Note location of current '\0'. */ + np = cp; + } + } + } +#endif + + /* + * sp points to the beginning of the arguments/environment string, and + * np should point to the '\0' terminator for the string. + */ + if ( np == NULL || np == sp ) { + /* Empty or unterminated string. */ + goto ERROR_B; + } + + /* Make a copy of the string. */ + retval = strdup(sp); + + /* Clean up. */ + free( procargs ); + + return retval; + +ERROR_B: + free( procargs ); +ERROR_A: + retval = strdup(k->kp_proc.p_comm); + + return retval; +} + +void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists) { + struct extern_proc *ep = &ps->kp_proc; + + /* UNSET HERE : + * + * processor + * user (set at ProcessList level) + * nlwp + * percent_cpu + * percent_mem + * m_size + * m_resident + * minflt + * majflt + */ + + /* First, the "immutable" parts */ + if(!exists) { + /* Set the PID/PGID/etc. */ + proc->pid = ep->p_pid; + proc->ppid = ps->kp_eproc.e_ppid; + proc->pgrp = ps->kp_eproc.e_pgid; + proc->session = 0; /* TODO Get the session id */ + proc->tgid = ps->kp_eproc.e_tpgid; + proc->st_uid = ps->kp_eproc.e_ucred.cr_uid; + /* e_tdev = (major << 24) | (minor & 0xffffff) */ + /* e_tdev == -1 for "no device" */ + proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */ + + DarwinProcess_setStartTime(proc, ep, now); + + /* The command is from the old Mac htop */ + char *slash; + + proc->comm = DarwinProcessList_getCmdLine(ps, false); + slash = strrchr(proc->comm, '/'); + proc->basenameOffset = (NULL != slash) ? (slash - proc->comm) : 0; + } + + /* Mutable information */ + proc->nice = ep->p_nice; + proc->priority = ep->p_priority; + + /* Set the state */ + switch(ep->p_stat) { + case SIDL: proc->state = 'I'; break; + case SRUN: proc->state = 'R'; break; + case SSLEEP: proc->state = 'S'; break; + case SSTOP: proc->state = 'T'; break; + case SZOMB: proc->state = 'Z'; break; + default: proc->state = '?'; break; + } + + /* Make sure the updated flag is set */ + proc->updated = true; +} + +void DarwinProcess_setFromLibprocPidinfo(Process *proc, uint64_t total_memory, bool preExisting) { + struct proc_taskinfo pti; + + if(sizeof(pti) == proc_pidinfo(proc->pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + proc->nlwp = pti.pti_threadnum; + proc->m_size = pti.pti_virtual_size / 1024; + proc->m_resident = pti.pti_resident_size / 1024; + proc->majflt = pti.pti_faults; + proc->percent_mem = (double)pti.pti_resident_size * 100.0 / (double)total_memory; + } +} + +void DarwinProcess_parseThreads(Process *proc, time_t now, bool preExisting) { + static const size_t IDS_SZ = sizeof(uint64_t) * 2048; + + uint64_t *thread_ids = (uint64_t *)malloc(IDS_SZ); + size_t bytes = proc_pidinfo(proc->pid, PROC_PIDLISTTHREADS, 0, thread_ids, IDS_SZ); + + if(0 < bytes) { + size_t count = bytes / sizeof(uint64_t); + + proc->nlwp = count; + } + + free(thread_ids); /* TODO Keep reusing this block */ +} diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h index 201650d5..65fff6b7 100644 --- a/darwin/DarwinProcess.h +++ b/darwin/DarwinProcess.h @@ -10,13 +10,23 @@ in the source distribution for its full text. */ #include "Settings.h" - -#define Process_delete DarwinProcess_delete +#include Process* DarwinProcess_new(Settings* settings); -void DarwinProcess_delete(Object* cast); +void Process_delete(Object* cast); +bool Process_isThread(Process* this); + +void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now); + +char *DarwinProcessList_getCmdLine(struct kinfo_proc* k, int show_args ); + +void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists); + +void DarwinProcess_setFromLibprocPidinfo(Process *proc, uint64_t total_memory, bool preExisting); + +void DarwinProcess_parseThreads(Process *proc, time_t now, bool preExisting); #endif diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 3fb36365..6187d346 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -11,16 +11,85 @@ in the source distribution for its full text. #include #include +#include +#include +#include /*{ +#include +#include + +typedef struct DarwinProcessList_ { + ProcessList super; + + host_basic_info_data_t prev_hinfo; + processor_info_data_t prev_cpus; +} DarwinProcessList; }*/ +void ProcessList_getHostInfo(host_basic_info_data_t *p) { + mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; + + if(0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { + fprintf(stderr, "Unable to retrieve host info\n"); + exit(2); + } +} + +unsigned ProcessList_getCPUInfo(processor_info_data_t *p) { + mach_msg_type_number_t info_size = PROCESSOR_CPU_LOAD_INFO_COUNT; + unsigned cpu_count; + + if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) { + fprintf(stderr, "Unable to retrieve CPU info\n"); + exit(4); + } + + return cpu_count; +} + +struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + struct kinfo_proc *processes = NULL; + + /* Note the two calls to sysctl(). One to get length and one to get the + * data. This -does- mean that the second call could end up with a missing + * process entry or two. + */ + *count = 0; + if(0 > sysctl(mib, 4, NULL, count, NULL, 0)) { + fprintf(stderr, "Unable to get size of kproc_infos"); + exit(5); + } + + processes = (struct kinfo_proc *)malloc(*count); + if(NULL == processes) { + fprintf(stderr, "Out of memory for kproc_infos\n"); + exit(6); + } + + if(0 > sysctl(mib, 4, processes, count, NULL, 0)) { + fprintf(stderr, "Unable to get kinfo_procs\n"); + exit(7); + } + + *count = *count / sizeof(struct kinfo_proc); + + return processes; +} + + ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { - ProcessList* this = calloc(1, sizeof(ProcessList)); - ProcessList_init(this, Class(Process), usersTable, pidWhiteList, userId); + DarwinProcessList* this = calloc(1, sizeof(DarwinProcessList)); + + ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId); - return this; + /* Initialize the previous information */ + this->super.cpuCount = ProcessList_getCPUInfo(&this->prev_cpus); + ProcessList_getHostInfo(&this->prev_hinfo); + + return &this->super; } void ProcessList_delete(ProcessList* this) { @@ -29,43 +98,36 @@ void ProcessList_delete(ProcessList* this) { } void ProcessList_goThroughEntries(ProcessList* super) { + DarwinProcessList *dpl = (DarwinProcessList *)super; bool preExisting = true; + struct kinfo_proc *ps; + size_t count; Process *proc; + struct timeval tv; - proc = ProcessList_getProcess(super, 1, &preExisting, DarwinProcess_new); + gettimeofday(&tv, NULL); /* Start processing time */ - /* Empty values */ - proc->time = proc->time + 10; - proc->pid = 1; - proc->ppid = 1; - proc->tgid = 0; - proc->comm = ""; - proc->basenameOffset = 0; - proc->updated = true; + /* We use kinfo_procs for initial data since : + * + * 1) They always succeed. + * 2) The contain the basic information. + * + * We attempt to fill-in additional information with libproc. + */ + ps = ProcessList_getKInfoProcs(&count); - proc->state = 'R'; - proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */ - proc->pgrp = 0; - proc->session = 0; - proc->tty_nr = 0; - proc->tpgid = 0; - proc->st_uid = 0; - proc->flags = 0; - proc->processor = 0; + for(size_t i = 0; i < count; ++i) { + proc = ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new); - proc->percent_cpu = 2.5; - proc->percent_mem = 2.5; - proc->user = "nobody"; + DarwinProcess_setFromKInfoProc(proc, ps + i, tv.tv_sec, preExisting); + DarwinProcess_setFromLibprocPidinfo(proc, dpl->prev_hinfo.max_mem, preExisting); - proc->priority = 0; - proc->nice = 0; - proc->nlwp = 1; - strncpy(proc->starttime_show, "Jun 01 ", sizeof(proc->starttime_show)); - proc->starttime_ctime = 1433116800; // Jun 01, 2015 + if(!preExisting) { + proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); - proc->m_size = 100; - proc->m_resident = 100; + ProcessList_add(super, proc); + } + } - proc->minflt = 20; - proc->majflt = 20; + free(ps); } diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index ccdf718f..6a4bb094 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -9,7 +9,25 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ +#include +#include +typedef struct DarwinProcessList_ { + ProcessList super; + + host_basic_info_data_t prev_hinfo; + vm_statistics64_data_t prev_vminfo; + processor_info_data_t prev_cpus; +} DarwinProcessList; + + +void ProcessList_getHostInfo(host_basic_info_data_t *p); + +void ProcessList_getVMInfo(vm_statistics64_data_t *p); + +unsigned ProcessList_getCPUInfo(processor_info_data_t *p); + +struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count); ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); diff --git a/darwin/Platform.c b/darwin/Platform.c index 99e90801..a1a3471e 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -16,6 +16,8 @@ in the source distribution for its full text. #include "HostnameMeter.h" #include "UptimeMeter.h" +#include + /*{ #include "Action.h" #include "BatteryMeter.h" @@ -87,13 +89,22 @@ int Platform_getUptime() { } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { - *one = 0; - *five = 0; - *fifteen = 0; + double results[3]; + + if(3 == getloadavg(results, 3)) { + *one = results[0]; + *five = results[1]; + *fifteen = results[2]; + } else { + *one = 0; + *five = 0; + *fifteen = 0; + } } int Platform_getMaxPid() { - return 1; + /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */ + return 99999; } void Process_setupColumnWidths() { @@ -121,6 +132,8 @@ void Process_setupColumnWidths() { } double Platform_setCPUValues(Meter* this, int cpu) { + DarwinProcessList *dpl = (DarwinProcessList *)this->pl; + return 0.0; } @@ -130,7 +143,3 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { } -bool Process_isThread(Process* this) { - return false; -} - diff --git a/darwin/Platform.h b/darwin/Platform.h index 4fc06f71..7da13321 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -40,7 +40,5 @@ void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); -bool Process_isThread(Process* this); - #endif