From ff9409b1737627857eb47f64f536a3f66b6a09a4 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Mon, 11 Apr 2016 13:01:07 +0200 Subject: Imported Upstream version 2.0.0 --- darwin/Battery.c | 75 ++++++++++ darwin/Battery.h | 9 ++ darwin/DarwinCRT.c | 35 +++++ darwin/DarwinCRT.h | 15 ++ darwin/DarwinProcess.c | 344 +++++++++++++++++++++++++++++++++++++++++++++ darwin/DarwinProcess.h | 41 ++++++ darwin/DarwinProcessList.c | 184 ++++++++++++++++++++++++ darwin/DarwinProcessList.h | 45 ++++++ darwin/Platform.c | 295 ++++++++++++++++++++++++++++++++++++++ darwin/Platform.h | 53 +++++++ 10 files changed, 1096 insertions(+) create mode 100644 darwin/Battery.c create mode 100644 darwin/Battery.h create mode 100644 darwin/DarwinCRT.c create mode 100644 darwin/DarwinCRT.h create mode 100644 darwin/DarwinProcess.c create mode 100644 darwin/DarwinProcess.h create mode 100644 darwin/DarwinProcessList.c create mode 100644 darwin/DarwinProcessList.h create mode 100644 darwin/Platform.c create mode 100644 darwin/Platform.h (limited to 'darwin') diff --git a/darwin/Battery.c b/darwin/Battery.c new file mode 100644 index 0000000..d197c04 --- /dev/null +++ b/darwin/Battery.c @@ -0,0 +1,75 @@ + +#include "BatteryMeter.h" + +#include +#include +#include +#include + +void Battery_getData(double* level, ACPresence* isOnAC) { + CFTypeRef power_sources = IOPSCopyPowerSourcesInfo(); + + *level = -1; + *isOnAC = AC_ERROR; + + if(NULL == power_sources) { + return; + } + + if(power_sources != NULL) { + CFArrayRef list = IOPSCopyPowerSourcesList(power_sources); + CFDictionaryRef battery = NULL; + int len; + + if(NULL == list) { + CFRelease(power_sources); + + return; + } + + len = CFArrayGetCount(list); + + /* Get the battery */ + for(int i = 0; i < len && battery == NULL; ++i) { + CFDictionaryRef candidate = IOPSGetPowerSourceDescription(power_sources, + CFArrayGetValueAtIndex(list, i)); /* GET rule */ + CFStringRef type; + + if(NULL != candidate) { + type = (CFStringRef) CFDictionaryGetValue(candidate, + CFSTR(kIOPSTransportTypeKey)); /* GET rule */ + + if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0)) { + CFRetain(candidate); + battery = candidate; + } + } + } + + if(NULL != battery) { + /* Determine the AC state */ + CFStringRef power_state = CFDictionaryGetValue(battery, CFSTR(kIOPSPowerSourceStateKey)); + + *isOnAC = (kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0)) + ? AC_PRESENT + : AC_ABSENT; + + /* Get the percentage remaining */ + double current; + double max; + + CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey)), + kCFNumberDoubleType, ¤t); + CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey)), + kCFNumberDoubleType, &max); + + *level = (current * 100.0) / max; + + CFRelease(battery); + } + + CFRelease(list); + CFRelease(power_sources); + } +} + diff --git a/darwin/Battery.h b/darwin/Battery.h new file mode 100644 index 0000000..8dc0cef --- /dev/null +++ b/darwin/Battery.h @@ -0,0 +1,9 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_Battery +#define HEADER_Battery + +void Battery_getData(double* level, ACPresence* isOnAC); + + +#endif diff --git a/darwin/DarwinCRT.c b/darwin/DarwinCRT.c new file mode 100644 index 0000000..5172598 --- /dev/null +++ b/darwin/DarwinCRT.c @@ -0,0 +1,35 @@ +/* +htop - DarwinCRT.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" +#include "CRT.h" +#include +#include +#include + +void CRT_handleSIGSEGV(int sgn) { + (void) sgn; + CRT_done(); + #ifdef __APPLE__ + fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n"); + #ifdef HAVE_EXECINFO_H + size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *)); + fprintf(stderr, "\n Please include in your report the following backtrace: \n"); + backtrace_symbols_fd(backtraceArray, size, 2); + fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,"); + fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:"); + fprintf(stderr, "\n\n otool -tvV `which htop` > ~/htop.otool"); + fprintf(stderr, "\n\nand then attach the file ~/htop.otool to your bug report."); + fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n"); + #endif + #else + fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!"); + fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n"); + #endif + abort(); +} + diff --git a/darwin/DarwinCRT.h b/darwin/DarwinCRT.h new file mode 100644 index 0000000..e1c22bb --- /dev/null +++ b/darwin/DarwinCRT.h @@ -0,0 +1,15 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_DarwinCRT +#define HEADER_DarwinCRT +/* +htop - DarwinCRT.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +void CRT_handleSIGSEGV(int sgn); + + +#endif diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c new file mode 100644 index 0000000..9c46046 --- /dev/null +++ b/darwin/DarwinProcess.c @@ -0,0 +1,344 @@ +/* +htop - DarwinProcess.c +(C) 2015 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Process.h" +#include "DarwinProcess.h" + +#include +#include +#include +#include + +/*{ +#include "Settings.h" +#include "DarwinProcessList.h" + +#include + +typedef struct DarwinProcess_ { + Process super; + + uint64_t utime; + uint64_t stime; +} DarwinProcess; + +}*/ + +ProcessClass DarwinProcess_class = { + .super = { + .extends = Class(Process), + .display = Process_display, + .delete = Process_delete, + .compare = Process_compare + }, + .writeField = Process_writeField, +}; + +DarwinProcess* DarwinProcess_new(Settings* settings) { + DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess)); + Object_setClass(this, Class(DarwinProcess)); + Process_init(&this->super, settings); + + this->utime = 0; + this->stime = 0; + + return this; +} + +void Process_delete(Object* cast) { + DarwinProcess* this = (DarwinProcess*) cast; + Process_done(&this->super); + // free platform-specific fields here + free(this); +} + +bool Process_isThread(Process* this) { + (void) 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 *DarwinProcess_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 * ) xMalloc( 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 = xStrdup(sp); + + /* Clean up. */ + free( procargs ); + + return retval; + +ERROR_B: + free( procargs ); +ERROR_A: + retval = xStrdup(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 = DarwinProcess_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(DarwinProcess *proc, DarwinProcessList *dpl) { + struct proc_taskinfo pti; + + if(sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + if(0 != proc->utime || 0 != proc->stime) { + uint64_t diff = (pti.pti_total_system - proc->stime) + + (pti.pti_total_user - proc->utime); + + proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount + / ((double)dpl->global_diff * 100000.0); + +// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu, +// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff); +// exit(7); + } + + proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000; + proc->super.nlwp = pti.pti_threadnum; + proc->super.m_size = pti.pti_virtual_size / 1024; + proc->super.m_resident = pti.pti_resident_size / 1024; + proc->super.majflt = pti.pti_faults; + proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 + / (double)dpl->host_info.max_mem; + + proc->stime = pti.pti_total_system; + proc->utime = pti.pti_total_user; + + dpl->super.kernelThreads += 0; /*pti.pti_threads_system;*/ + dpl->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/ + dpl->super.totalTasks += pti.pti_threadnum; + dpl->super.runningTasks += pti.pti_numrunning; + } +} diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h new file mode 100644 index 0000000..c479acf --- /dev/null +++ b/darwin/DarwinProcess.h @@ -0,0 +1,41 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_DarwinProcess +#define HEADER_DarwinProcess +/* +htop - DarwinProcess.h +(C) 2015 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Settings.h" +#include "DarwinProcessList.h" + +#include + +typedef struct DarwinProcess_ { + Process super; + + uint64_t utime; + uint64_t stime; +} DarwinProcess; + + +extern ProcessClass DarwinProcess_class; + +DarwinProcess* DarwinProcess_new(Settings* settings); + +void Process_delete(Object* cast); + +bool Process_isThread(Process* this); + +void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now); + +char *DarwinProcess_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(DarwinProcess *proc, DarwinProcessList *dpl); + +#endif diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c new file mode 100644 index 0000000..7ea89d9 --- /dev/null +++ b/darwin/DarwinProcessList.c @@ -0,0 +1,184 @@ +/* +htop - DarwinProcessList.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ProcessList.h" +#include "DarwinProcess.h" +#include "DarwinProcessList.h" +#include "CRT.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/*{ +#include "ProcessList.h" +#include +#include + +typedef struct DarwinProcessList_ { + ProcessList super; + + host_basic_info_data_t host_info; + vm_statistics_data_t vm_stats; + processor_cpu_load_info_t prev_load; + processor_cpu_load_info_t curr_load; + uint64_t kernel_threads; + uint64_t user_threads; + uint64_t global_diff; +} 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)) { + CRT_fatalError("Unable to retrieve host info\n"); + } +} + +void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) { + if(NULL != p && NULL != *p) { + if(0 != munmap(*p, vm_page_size)) { + CRT_fatalError("Unable to free old CPU load information\n"); + } + } + + *p = NULL; +} + +unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) { + mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t); + unsigned cpu_count; + + // TODO Improving the accuracy of the load counts woule help a lot. + if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) { + CRT_fatalError("Unable to retrieve CPU info\n"); + } + + return cpu_count; +} + +void ProcessList_getVMStats(vm_statistics_t p) { + mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; + + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) + CRT_fatalError("Unable to retrieve VM statistics\n"); +} + +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 (sysctl(mib, 4, NULL, count, NULL, 0) < 0) + CRT_fatalError("Unable to get size of kproc_infos"); + + processes = xMalloc(*count); + if (processes == NULL) + CRT_fatalError("Out of memory for kproc_infos"); + + if (sysctl(mib, 4, processes, count, NULL, 0) < 0) + CRT_fatalError("Unable to get kinfo_procs"); + + *count = *count / sizeof(struct kinfo_proc); + + return processes; +} + + +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { + DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); + + ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId); + + /* Initialize the CPU information */ + this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load); + ProcessList_getHostInfo(&this->host_info); + ProcessList_allocateCPULoadInfo(&this->curr_load); + + /* Initialize the VM statistics */ + ProcessList_getVMStats(&this->vm_stats); + + this->super.kernelThreads = 0; + this->super.userlandThreads = 0; + this->super.totalTasks = 0; + this->super.runningTasks = 0; + + return &this->super; +} + +void ProcessList_delete(ProcessList* this) { + ProcessList_done(this); + free(this); +} + +void ProcessList_goThroughEntries(ProcessList* super) { + DarwinProcessList *dpl = (DarwinProcessList *)super; + bool preExisting = true; + struct kinfo_proc *ps; + size_t count; + DarwinProcess *proc; + struct timeval tv; + + gettimeofday(&tv, NULL); /* Start processing time */ + + /* Update the global data (CPU times and VM stats) */ + ProcessList_freeCPULoadInfo(&dpl->prev_load); + dpl->prev_load = dpl->curr_load; + ProcessList_allocateCPULoadInfo(&dpl->curr_load); + ProcessList_getVMStats(&dpl->vm_stats); + + /* Get the time difference */ + dpl->global_diff = 0; + for(int i = 0; i < dpl->super.cpuCount; ++i) { + for(size_t j = 0; j < CPU_STATE_MAX; ++j) { + dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; + } + } + + /* Clear the thread counts */ + super->kernelThreads = 0; + super->userlandThreads = 0; + super->totalTasks = 0; + super->runningTasks = 0; + + /* 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); + + for(size_t i = 0; i < count; ++i) { + proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new); + + DarwinProcess_setFromKInfoProc(&proc->super, ps + i, tv.tv_sec, preExisting); + DarwinProcess_setFromLibprocPidinfo(proc, dpl); + + super->totalTasks += 1; + + if(!preExisting) { + proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid); + + ProcessList_add(super, &proc->super); + } + } + + free(ps); +} diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h new file mode 100644 index 0000000..c216a80 --- /dev/null +++ b/darwin/DarwinProcessList.h @@ -0,0 +1,45 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_DarwinProcessList +#define HEADER_DarwinProcessList +/* +htop - DarwinProcessList.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ProcessList.h" +#include +#include + +typedef struct DarwinProcessList_ { + ProcessList super; + + host_basic_info_data_t host_info; + vm_statistics_data_t vm_stats; + processor_cpu_load_info_t prev_load; + processor_cpu_load_info_t curr_load; + uint64_t kernel_threads; + uint64_t user_threads; + uint64_t global_diff; +} DarwinProcessList; + + +void ProcessList_getHostInfo(host_basic_info_data_t *p); + +void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p); + +unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p); + +void ProcessList_getVMStats(vm_statistics_t p); + +struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count); + +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); + +void ProcessList_delete(ProcessList* this); + +void ProcessList_goThroughEntries(ProcessList* super); + +#endif diff --git a/darwin/Platform.c b/darwin/Platform.c new file mode 100644 index 0000000..303b6c1 --- /dev/null +++ b/darwin/Platform.c @@ -0,0 +1,295 @@ +/* +htop - darwin/Platform.c +(C) 2014 Hisham H. Muhammad +(C) 2015 David C. Hunt +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Platform.h" +#include "CPUMeter.h" +#include "MemoryMeter.h" +#include "SwapMeter.h" +#include "TasksMeter.h" +#include "LoadAverageMeter.h" +#include "ClockMeter.h" +#include "HostnameMeter.h" +#include "UptimeMeter.h" +#include "DarwinProcessList.h" + +#include + +/*{ +#include "Action.h" +#include "SignalsPanel.h" +#include "CPUMeter.h" +#include "BatteryMeter.h" +#include "DarwinProcess.h" +}*/ + +#ifndef CLAMP +#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) +#endif + +ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; + +SignalItem Platform_signals[] = { + { .name = " 0 Cancel", .number = 0 }, + { .name = " 1 SIGHUP", .number = 1 }, + { .name = " 2 SIGINT", .number = 2 }, + { .name = " 3 SIGQUIT", .number = 3 }, + { .name = " 4 SIGILL", .number = 4 }, + { .name = " 5 SIGTRAP", .number = 5 }, + { .name = " 6 SIGABRT", .number = 6 }, + { .name = " 6 SIGIOT", .number = 6 }, + { .name = " 7 SIGEMT", .number = 7 }, + { .name = " 8 SIGFPE", .number = 8 }, + { .name = " 9 SIGKILL", .number = 9 }, + { .name = "10 SIGBUS", .number = 10 }, + { .name = "11 SIGSEGV", .number = 11 }, + { .name = "12 SIGSYS", .number = 12 }, + { .name = "13 SIGPIPE", .number = 13 }, + { .name = "14 SIGALRM", .number = 14 }, + { .name = "15 SIGTERM", .number = 15 }, + { .name = "16 SIGURG", .number = 16 }, + { .name = "17 SIGSTOP", .number = 17 }, + { .name = "18 SIGTSTP", .number = 18 }, + { .name = "19 SIGCONT", .number = 19 }, + { .name = "20 SIGCHLD", .number = 20 }, + { .name = "21 SIGTTIN", .number = 21 }, + { .name = "22 SIGTTOU", .number = 22 }, + { .name = "23 SIGIO", .number = 23 }, + { .name = "24 SIGXCPU", .number = 24 }, + { .name = "25 SIGXFSZ", .number = 25 }, + { .name = "26 SIGVTALRM", .number = 26 }, + { .name = "27 SIGPROF", .number = 27 }, + { .name = "28 SIGWINCH", .number = 28 }, + { .name = "29 SIGINFO", .number = 29 }, + { .name = "30 SIGUSR1", .number = 30 }, + { .name = "31 SIGUSR2", .number = 31 }, +}; + +unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem); + +ProcessFieldData Process_fields[] = { + [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, + [PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, + [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, + [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, + [PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, + [PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, + [SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, }, + [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, + [TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, + [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, + [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, + [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, + [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, + [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, + + [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, + [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, + [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, + [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, + [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, + [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, + [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, + [TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, + [100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, +}; + +MeterClass* Platform_meterTypes[] = { + &CPUMeter_class, + &ClockMeter_class, + &LoadAverageMeter_class, + &LoadMeter_class, + &MemoryMeter_class, + &SwapMeter_class, + &TasksMeter_class, + &BatteryMeter_class, + &HostnameMeter_class, + &UptimeMeter_class, + &AllCPUsMeter_class, + &AllCPUs2Meter_class, + &LeftCPUsMeter_class, + &RightCPUsMeter_class, + &LeftCPUs2Meter_class, + &RightCPUs2Meter_class, + &BlankMeter_class, + NULL +}; + +void Platform_setBindings(Htop_Action* keys) { + (void) keys; +} + +int Platform_numberOfFields = 100; + +int Platform_getUptime() { + struct timeval bootTime, currTime; + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t size = sizeof(bootTime); + + int err = sysctl(mib, 2, &bootTime, &size, NULL, 0); + if (err) { + return -1; + } + gettimeofday(&currTime, NULL); + + return (int) difftime(currTime.tv_sec, bootTime.tv_sec); +} + +void Platform_getLoadAverage(double* one, double* five, double* fifteen) { + 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() { + /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */ + return 99999; +} + +ProcessPidColumn Process_pidColumns[] = { + { .id = PID, .label = "PID" }, + { .id = PPID, .label = "PPID" }, + { .id = TPGID, .label = "TPGID" }, + { .id = TGID, .label = "TGID" }, + { .id = PGRP, .label = "PGRP" }, + { .id = SESSION, .label = "SESN" }, + { .id = 0, .label = NULL }, +}; + +static double Platform_setCPUAverageValues(Meter* mtr) { + DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl; + int cpus = dpl->super.cpuCount; + double sumNice = 0.0; + double sumNormal = 0.0; + double sumKernel = 0.0; + double sumPercent = 0.0; + for (int i = 1; i <= cpus; i++) { + sumPercent += Platform_setCPUValues(mtr, i); + sumNice += mtr->values[CPU_METER_NICE]; + sumNormal += mtr->values[CPU_METER_NORMAL]; + sumKernel += mtr->values[CPU_METER_KERNEL]; + } + mtr->values[CPU_METER_NICE] = sumNice / cpus; + mtr->values[CPU_METER_NORMAL] = sumNormal / cpus; + mtr->values[CPU_METER_KERNEL] = sumKernel / cpus; + return sumPercent / cpus; +} + +double Platform_setCPUValues(Meter* mtr, int cpu) { + + if (cpu == 0) { + return Platform_setCPUAverageValues(mtr); + } + + DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl; + processor_cpu_load_info_t prev = &dpl->prev_load[cpu-1]; + processor_cpu_load_info_t curr = &dpl->curr_load[cpu-1]; + double total = 0; + + /* Take the sums */ + for(size_t i = 0; i < CPU_STATE_MAX; ++i) { + total += (double)curr->cpu_ticks[i] - (double)prev->cpu_ticks[i]; + } + + mtr->values[CPU_METER_NICE] + = ((double)curr->cpu_ticks[CPU_STATE_NICE] - (double)prev->cpu_ticks[CPU_STATE_NICE])* 100.0 / total; + mtr->values[CPU_METER_NORMAL] + = ((double)curr->cpu_ticks[CPU_STATE_USER] - (double)prev->cpu_ticks[CPU_STATE_USER])* 100.0 / total; + mtr->values[CPU_METER_KERNEL] + = ((double)curr->cpu_ticks[CPU_STATE_SYSTEM] - (double)prev->cpu_ticks[CPU_STATE_SYSTEM])* 100.0 / total; + + Meter_setItems(mtr, 3); + + /* Convert to percent and return */ + total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL]; + + return CLAMP(total, 0.0, 100.0); +} + +void Platform_setMemoryValues(Meter* mtr) { + DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl; + vm_statistics_t vm = &dpl->vm_stats; + double page_K = (double)vm_page_size / (double)1024; + + mtr->total = dpl->host_info.max_mem / 1024; + mtr->values[0] = (double)(vm->active_count + vm->wire_count) * page_K; + mtr->values[1] = (double)vm->purgeable_count * page_K; + mtr->values[2] = (double)vm->inactive_count * page_K; +} + +void Platform_setSwapValues(Meter* mtr) { + int mib[2] = {CTL_VM, VM_SWAPUSAGE}; + struct xsw_usage swapused; + size_t swlen = sizeof(swapused); + sysctl(mib, 2, &swapused, &swlen, NULL, 0); + + mtr->total = swapused.xsu_total / 1024; + mtr->values[0] = swapused.xsu_used / 1024; +} + +char* Platform_getProcessEnv(pid_t pid) { + char* env = NULL; + + int argmax; + size_t bufsz = sizeof(argmax); + + int mib[3]; + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + if (sysctl(mib, 2, &argmax, &bufsz, 0, 0) == 0) { + char* buf = xMalloc(argmax); + if (buf) { + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = pid; + size_t bufsz = argmax; + if (sysctl(mib, 3, buf, &bufsz, 0, 0) == 0) { + if (bufsz > sizeof(int)) { + char *p = buf, *endp = buf + bufsz; + int argc = *(int*)p; + p += sizeof(int); + + // skip exe + p = strchr(p, 0)+1; + + // skip padding + while(!*p && p < endp) + ++p; + + // skip argv + for (; argc-- && p < endp; p = strrchr(p, 0)+1) + ; + + // skip padding + while(!*p && p < endp) + ++p; + + size_t size = endp - p; + env = xMalloc(size+2); + if (env) { + memcpy(env, p, size); + env[size] = 0; + env[size+1] = 0; + } + } + } + free(buf); + } + } + + return env; +} diff --git a/darwin/Platform.h b/darwin/Platform.h new file mode 100644 index 0000000..29ef289 --- /dev/null +++ b/darwin/Platform.h @@ -0,0 +1,53 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_Platform +#define HEADER_Platform +/* +htop - darwin/Platform.h +(C) 2014 Hisham H. Muhammad +(C) 2015 David C. Hunt +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Action.h" +#include "SignalsPanel.h" +#include "CPUMeter.h" +#include "BatteryMeter.h" +#include "DarwinProcess.h" + +#ifndef CLAMP +#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) +#endif + +extern ProcessField Platform_defaultFields[]; + +extern SignalItem Platform_signals[]; + +extern unsigned int Platform_numberOfSignals; + +extern ProcessFieldData Process_fields[]; + +extern MeterClass* Platform_meterTypes[]; + +void Platform_setBindings(Htop_Action* keys); + +extern int Platform_numberOfFields; + +int Platform_getUptime(); + +void Platform_getLoadAverage(double* one, double* five, double* fifteen); + +int Platform_getMaxPid(); + +extern ProcessPidColumn Process_pidColumns[]; + +double Platform_setCPUValues(Meter* mtr, int cpu); + +void Platform_setMemoryValues(Meter* mtr); + +void Platform_setSwapValues(Meter* mtr); + +char* Platform_getProcessEnv(pid_t pid); + +#endif -- cgit v1.2.3