2015-07-12 18:47:43 +00:00
|
|
|
/*
|
|
|
|
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"
|
2015-07-13 06:17:14 +00:00
|
|
|
|
2015-07-12 18:47:43 +00:00
|
|
|
#include <stdlib.h>
|
2015-07-13 06:17:14 +00:00
|
|
|
#include <libproc.h>
|
|
|
|
#include <string.h>
|
2015-07-14 16:46:16 +00:00
|
|
|
#include <stdio.h>
|
2015-07-12 18:47:43 +00:00
|
|
|
|
2016-02-18 16:57:09 +00:00
|
|
|
#include <mach/mach.h>
|
|
|
|
|
2015-07-12 18:47:43 +00:00
|
|
|
/*{
|
|
|
|
#include "Settings.h"
|
2015-07-14 02:02:40 +00:00
|
|
|
#include "DarwinProcessList.h"
|
|
|
|
|
2015-07-13 06:17:14 +00:00
|
|
|
#include <sys/sysctl.h>
|
2015-07-12 18:47:43 +00:00
|
|
|
|
2015-07-14 16:46:16 +00:00
|
|
|
typedef struct DarwinProcess_ {
|
|
|
|
Process super;
|
|
|
|
|
|
|
|
uint64_t utime;
|
|
|
|
uint64_t stime;
|
|
|
|
} DarwinProcess;
|
|
|
|
|
2015-07-12 18:47:43 +00:00
|
|
|
}*/
|
|
|
|
|
2015-07-14 16:46:16 +00:00
|
|
|
ProcessClass DarwinProcess_class = {
|
|
|
|
.super = {
|
|
|
|
.extends = Class(Process),
|
|
|
|
.display = Process_display,
|
|
|
|
.delete = Process_delete,
|
|
|
|
.compare = Process_compare
|
|
|
|
},
|
|
|
|
.writeField = Process_writeField,
|
|
|
|
};
|
|
|
|
|
|
|
|
DarwinProcess* DarwinProcess_new(Settings* settings) {
|
2016-02-02 14:53:02 +00:00
|
|
|
DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess));
|
2015-07-14 16:46:16 +00:00
|
|
|
Object_setClass(this, Class(DarwinProcess));
|
|
|
|
Process_init(&this->super, settings);
|
|
|
|
|
|
|
|
this->utime = 0;
|
|
|
|
this->stime = 0;
|
|
|
|
|
2015-07-12 18:47:43 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-07-13 06:17:14 +00:00
|
|
|
void Process_delete(Object* cast) {
|
2015-07-14 16:46:16 +00:00
|
|
|
DarwinProcess* this = (DarwinProcess*) cast;
|
|
|
|
Process_done(&this->super);
|
2015-07-12 18:47:43 +00:00
|
|
|
// free platform-specific fields here
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
2015-07-13 06:17:14 +00:00
|
|
|
bool Process_isThread(Process* this) {
|
2015-07-14 16:46:16 +00:00
|
|
|
(void) this;
|
2015-07-13 06:17:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now) {
|
2015-08-19 16:56:46 +00:00
|
|
|
struct tm date;
|
2015-07-13 06:17:14 +00:00
|
|
|
|
2015-08-19 16:56:46 +00:00
|
|
|
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);
|
2015-07-13 06:17:14 +00:00
|
|
|
}
|
|
|
|
|
2015-07-14 16:46:16 +00:00
|
|
|
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
|
2015-07-13 06:17:14 +00:00
|
|
|
/* 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. */
|
2016-02-02 14:53:02 +00:00
|
|
|
procargs = ( char * ) xMalloc( argmax );
|
2015-07-13 06:17:14 +00:00
|
|
|
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 = ' ';
|
|
|
|
}
|
2015-10-23 15:46:21 +00:00
|
|
|
/* Note location of current '\0'. */
|
|
|
|
np = cp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-13 06:17:14 +00:00
|
|
|
#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. */
|
2016-02-02 14:53:02 +00:00
|
|
|
retval = xStrdup(sp);
|
2015-07-13 06:17:14 +00:00
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
free( procargs );
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
ERROR_B:
|
|
|
|
free( procargs );
|
|
|
|
ERROR_A:
|
2016-02-02 14:53:02 +00:00
|
|
|
retval = xStrdup(k->kp_proc.p_comm);
|
2015-07-13 06:17:14 +00:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists) {
|
2015-08-19 16:56:46 +00:00
|
|
|
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 */
|
2016-02-18 16:14:45 +00:00
|
|
|
proc->tpgid = ps->kp_eproc.e_tpgid;
|
|
|
|
proc->tgid = proc->pid;
|
2015-08-19 16:56:46 +00:00
|
|
|
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;
|
|
|
|
|
2016-02-18 16:57:09 +00:00
|
|
|
proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
|
2015-08-19 16:56:46 +00:00
|
|
|
|
|
|
|
/* Make sure the updated flag is set */
|
|
|
|
proc->updated = true;
|
2015-07-13 06:17:14 +00:00
|
|
|
}
|
|
|
|
|
2015-07-14 16:46:16 +00:00
|
|
|
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl) {
|
2015-08-19 16:56:46 +00:00
|
|
|
struct proc_taskinfo pti;
|
2015-07-13 06:17:14 +00:00
|
|
|
|
2015-08-19 16:56:46 +00:00
|
|
|
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);
|
2015-07-14 16:46:16 +00:00
|
|
|
|
2015-08-19 16:56:46 +00:00
|
|
|
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount
|
|
|
|
/ ((double)dpl->global_diff * 100000.0);
|
2015-07-14 16:46:16 +00:00
|
|
|
|
2015-08-19 16:56:46 +00:00
|
|
|
// 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);
|
|
|
|
}
|
2015-07-14 16:46:16 +00:00
|
|
|
|
2015-08-19 16:56:46 +00:00
|
|
|
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
|
|
|
|
proc->super.nlwp = pti.pti_threadnum;
|
2016-02-13 04:13:57 +00:00
|
|
|
proc->super.m_size = pti.pti_virtual_size / 1024 / PAGE_SIZE_KB;
|
|
|
|
proc->super.m_resident = pti.pti_resident_size / 1024 / PAGE_SIZE_KB;
|
2015-08-19 16:56:46 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-07-13 06:17:14 +00:00
|
|
|
}
|
2016-02-18 16:57:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan threads for process state information.
|
|
|
|
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
|
|
|
|
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
|
|
|
|
*/
|
|
|
|
void DarwinProcess_scanThreads(DarwinProcess *dp) {
|
|
|
|
Process* proc = (Process*) dp;
|
|
|
|
kern_return_t ret;
|
|
|
|
|
|
|
|
if (proc->state == 'Z') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
task_t port;
|
|
|
|
ret = task_for_pid(mach_task_self(), proc->pid, &port);
|
|
|
|
if (ret != KERN_SUCCESS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
task_info_data_t tinfo;
|
|
|
|
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
|
|
|
|
ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count);
|
|
|
|
if (ret != KERN_SUCCESS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_array_t thread_list;
|
|
|
|
mach_msg_type_number_t thread_count;
|
|
|
|
ret = task_threads(port, &thread_list, &thread_count);
|
|
|
|
if (ret != KERN_SUCCESS) {
|
|
|
|
mach_port_deallocate(mach_task_self(), port);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
integer_t run_state = 999;
|
|
|
|
for (unsigned int i = 0; i < thread_count; i++) {
|
|
|
|
thread_info_data_t thinfo;
|
|
|
|
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
|
|
|
|
ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
|
|
|
|
if (ret == KERN_SUCCESS) {
|
|
|
|
thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo;
|
|
|
|
if (basic_info_th->run_state < run_state) {
|
|
|
|
run_state = basic_info_th->run_state;
|
|
|
|
}
|
|
|
|
mach_port_deallocate(mach_task_self(), thread_list[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
|
|
|
|
mach_port_deallocate(mach_task_self(), port);
|
|
|
|
|
|
|
|
char state = '?';
|
|
|
|
switch (run_state) {
|
|
|
|
case TH_STATE_RUNNING: state = 'R'; break;
|
|
|
|
case TH_STATE_STOPPED: state = 'S'; break;
|
|
|
|
case TH_STATE_WAITING: state = 'W'; break;
|
|
|
|
case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break;
|
|
|
|
case TH_STATE_HALTED: state = 'H'; break;
|
|
|
|
}
|
|
|
|
proc->state = state;
|
|
|
|
}
|