htop/darwin/DarwinProcess.c

314 lines
8.4 KiB
C
Raw Normal View History

/*
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
#include <stdlib.h>
2015-07-13 06:17:14 +00:00
#include <libproc.h>
#include <string.h>
/*{
#include "Settings.h"
2015-07-13 06:17:14 +00:00
#include <sys/sysctl.h>
}*/
Process* DarwinProcess_new(Settings* settings) {
Process* this = calloc(sizeof(Process), 1);
Object_setClass(this, Class(Process));
Process_init(this, settings);
return this;
}
2015-07-13 06:17:14 +00:00
void Process_delete(Object* cast) {
Process* this = (Process*) cast;
Object_setClass(this, Class(Process));
Process_done((Process*)cast);
// free platform-specific fields here
free(this);
}
2015-07-13 06:17:14 +00:00
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 */
}