2015-07-12 18:47:43 +00:00
/*
htop - DarwinProcess . c
( C ) 2015 Hisham H . Muhammad
2020-10-05 07:51:32 +00:00
Released under the GNU GPLv2 , see the COPYING file
2015-07-12 18:47:43 +00:00
in the source distribution for its full text .
*/
# include "DarwinProcess.h"
2015-07-13 06:17:14 +00:00
# include <libproc.h>
2015-07-14 16:46:16 +00:00
# include <stdio.h>
2020-11-17 18:41:22 +00:00
# include <stdlib.h>
# include <string.h>
2016-02-18 16:57:09 +00:00
# include <mach/mach.h>
2020-10-15 20:37:02 +00:00
# include "CRT.h"
2020-12-12 19:43:08 +00:00
# include "Platform.h"
2020-11-17 18:41:22 +00:00
# include "Process.h"
2020-10-15 20:37:02 +00:00
2015-07-12 18:47:43 +00:00
2020-12-15 18:44:52 +00:00
const ProcessFieldData Process_fields [ LAST_PROCESSFIELD ] = {
2020-12-13 14:52:58 +00:00
[ 0 ] = { . name = " " , . title = NULL , . description = NULL , . flags = 0 , } ,
2020-12-15 18:44:52 +00:00
[ PID ] = { . name = " PID " , . title = " PID " , . description = " Process/thread ID " , . flags = 0 , . pidColumn = true , } ,
2020-12-13 14:52:58 +00:00
[ 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 , } ,
2020-12-15 18:44:52 +00:00
[ PPID ] = { . name = " PPID " , . title = " PPID " , . description = " Parent process ID " , . flags = 0 , . pidColumn = true , } ,
[ PGRP ] = { . name = " PGRP " , . title = " PGRP " , . description = " Process group ID " , . flags = 0 , . pidColumn = true , } ,
[ SESSION ] = { . name = " SESSION " , . title = " SID " , . description = " Process's session ID " , . flags = 0 , . pidColumn = true , } ,
2020-12-13 14:52:58 +00:00
[ TTY_NR ] = { . name = " TTY_NR " , . title = " TTY " , . description = " Controlling terminal " , . flags = 0 , } ,
2020-12-15 18:44:52 +00:00
[ TPGID ] = { . name = " TPGID " , . title = " TPGID " , . description = " Process ID of the fg process group of the controlling terminal " , . flags = 0 , . pidColumn = true , } ,
2021-01-21 13:27:23 +00:00
[ MINFLT ] = { . name = " MINFLT " , . title = " MINFLT " , . description = " Number of minor faults which have not required loading a memory page from disk " , . flags = 0 , . defaultSortDesc = true , } ,
[ MAJFLT ] = { . name = " MAJFLT " , . title = " MAJFLT " , . description = " Number of major faults which have required loading a memory page from disk " , . flags = 0 , . defaultSortDesc = true , } ,
2020-12-13 14:52:58 +00:00
[ 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 , } ,
2021-01-21 13:27:23 +00:00
[ M_VIRT ] = { . name = " M_VIRT " , . title = " VIRT " , . description = " Total program size in virtual memory " , . flags = 0 , . defaultSortDesc = true , } ,
[ M_RESIDENT ] = { . name = " M_RESIDENT " , . title = " RES " , . description = " Resident set size, size of the text and data sections, plus stack usage " , . flags = 0 , . defaultSortDesc = true , } ,
2020-12-13 14:52:58 +00:00
[ ST_UID ] = { . name = " ST_UID " , . title = " UID " , . description = " User ID of the process owner " , . flags = 0 , } ,
2021-01-21 13:27:23 +00:00
[ PERCENT_CPU ] = { . name = " PERCENT_CPU " , . title = " CPU% " , . description = " Percentage of the CPU time the process used in the last sampling " , . flags = 0 , . defaultSortDesc = true , } ,
2021-03-02 20:07:20 +00:00
[ PERCENT_NORM_CPU ] = { . name = " PERCENT_NORM_CPU " , . title = " NCPU% " , . description = " Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count) " , . flags = 0 , . defaultSortDesc = true , } ,
2021-01-21 13:27:23 +00:00
[ PERCENT_MEM ] = { . name = " PERCENT_MEM " , . title = " MEM% " , . description = " Percentage of the memory the process is using, based on resident memory size " , . flags = 0 , . defaultSortDesc = true , } ,
2020-12-13 14:52:58 +00:00
[ USER ] = { . name = " USER " , . title = " USER " , . description = " Username of the process owner (or user ID if name cannot be determined) " , . flags = 0 , } ,
2021-01-21 13:27:23 +00:00
[ TIME ] = { . name = " TIME " , . title = " TIME+ " , . description = " Total time the process has spent in user and system time " , . flags = 0 , . defaultSortDesc = true , } ,
2020-12-13 14:52:58 +00:00
[ NLWP ] = { . name = " NLWP " , . title = " NLWP " , . description = " Number of threads in the process " , . flags = 0 , } ,
2020-12-15 18:44:52 +00:00
[ TGID ] = { . name = " TGID " , . title = " TGID " , . description = " Thread group ID (i.e. process ID) " , . flags = 0 , . pidColumn = true , } ,
2020-12-13 14:54:13 +00:00
[ TRANSLATED ] = { . name = " TRANSLATED " , . title = " T " , . description = " Translation info (T translated, N native) " , . flags = 0 , } ,
2020-12-13 14:52:58 +00:00
} ;
2020-10-21 19:26:05 +00:00
Process * DarwinProcess_new ( const 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 ;
2016-03-31 03:18:42 +00:00
this - > taskAccess = true ;
2020-12-13 14:54:13 +00:00
this - > translated = false ;
2015-07-14 16:46:16 +00:00
2020-10-21 19:26:05 +00:00
return & this - > super ;
2015-07-12 18:47:43 +00:00
}
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 ) ;
}
2020-12-13 14:54:13 +00:00
static void DarwinProcess_writeField ( const Process * this , RichString * str , ProcessField field ) {
const DarwinProcess * dp = ( const DarwinProcess * ) this ;
char buffer [ 256 ] ; buffer [ 255 ] = ' \0 ' ;
int attr = CRT_colors [ DEFAULT_COLOR ] ;
int n = sizeof ( buffer ) - 1 ;
2020-12-15 18:44:48 +00:00
switch ( field ) {
2020-12-13 14:54:13 +00:00
// add Platform-specific fields here
case TRANSLATED : xSnprintf ( buffer , n , " %c " , dp - > translated ? ' T ' : ' N ' ) ; break ;
default :
Process_writeField ( this , str , field ) ;
return ;
}
RichString_appendWide ( str , attr , buffer ) ;
}
2020-12-23 12:02:32 +00:00
static int DarwinProcess_compareByKey ( const Process * v1 , const Process * v2 , ProcessField key ) {
2020-12-17 23:09:55 +00:00
const DarwinProcess * p1 = ( const DarwinProcess * ) v1 ;
const DarwinProcess * p2 = ( const DarwinProcess * ) v2 ;
2020-12-13 14:54:13 +00:00
2020-12-15 18:44:48 +00:00
switch ( key ) {
2020-12-13 14:54:13 +00:00
// add Platform-specific fields here
case TRANSLATED :
return SPACESHIP_NUMBER ( p1 - > translated , p2 - > translated ) ;
default :
2020-12-18 21:12:26 +00:00
return Process_compareByKey_Base ( v1 , v2 , key ) ;
2020-12-13 14:54:13 +00:00
}
}
2020-10-07 17:02:15 +00:00
bool Process_isThread ( const Process * this ) {
2015-07-14 16:46:16 +00:00
( void ) this ;
2015-07-13 06:17:14 +00:00
return false ;
}
2020-11-17 18:41:22 +00:00
static char * DarwinProcess_getCmdLine ( const struct kinfo_proc * k , int * basenameOffset ) {
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. */
2020-10-31 22:28: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 ;
2016-02-19 01:45:17 +00:00
* basenameOffset = 0 ;
2015-07-13 06:17:14 +00:00
for ( np = NULL ; c < nargs & & cp < & procargs [ size ] ; cp + + ) {
if ( * cp = = ' \0 ' ) {
c + + ;
if ( np ! = NULL ) {
/* Convert previous '\0'. */
* np = ' ' ;
}
2020-10-31 21:14:27 +00:00
/* Note location of current '\0'. */
np = cp ;
if ( * basenameOffset = = 0 ) {
* basenameOffset = cp - sp ;
}
}
2015-10-23 15:46:21 +00:00
}
2015-07-13 06:17:14 +00:00
/*
* 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 ;
}
2016-02-19 01:45:17 +00:00
if ( * basenameOffset = = 0 ) {
* basenameOffset = np - sp ;
}
2015-07-13 06:17:14 +00:00
/* 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 ) ;
2016-02-19 01:45:17 +00:00
* basenameOffset = strlen ( retval ) ;
2019-10-31 16:39:12 +00:00
2015-07-13 06:17:14 +00:00
return retval ;
}
2020-11-17 18:41:22 +00:00
void DarwinProcess_setFromKInfoProc ( Process * proc , const struct kinfo_proc * ps , bool exists ) {
2020-12-13 14:54:13 +00:00
DarwinProcess * dp = ( DarwinProcess * ) proc ;
2020-11-17 18:41:22 +00:00
const struct extern_proc * ep = & ps - > kp_proc ;
2015-08-19 16:56:46 +00:00
/* UNSET HERE :
*
* processor
* user ( set at ProcessList level )
* nlwp
* percent_cpu
* percent_mem
2020-11-20 16:09:34 +00:00
* m_virt
2015-08-19 16:56:46 +00:00
* m_resident
* minflt
* majflt
*/
/* First, the "immutable" parts */
2020-10-31 19:52:20 +00:00
if ( ! exists ) {
2015-08-19 16:56:46 +00:00
/* 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 */
2020-12-13 14:54:13 +00:00
dp - > translated = ps - > kp_proc . p_flag & P_TRANSLATED ;
2015-08-19 16:56:46 +00:00
2020-10-13 12:26:40 +00:00
proc - > starttime_ctime = ep - > p_starttime . tv_sec ;
Process_fillStarttimeBuffer ( proc ) ;
2016-02-19 01:45:17 +00:00
proc - > comm = DarwinProcess_getCmdLine ( ps , & ( proc - > basenameOffset ) ) ;
2015-08-19 16:56:46 +00:00
}
/* 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
}
2020-12-09 04:12:44 +00:00
void DarwinProcess_setFromLibprocPidinfo ( DarwinProcess * proc , DarwinProcessList * dpl , double time_interval ) {
2015-08-19 16:56:46 +00:00
struct proc_taskinfo pti ;
2015-07-13 06:17:14 +00:00
2020-10-31 19:52:20 +00:00
if ( sizeof ( pti ) = = proc_pidinfo ( proc - > super . pid , PROC_PIDTASKINFO , 0 , & pti , sizeof ( pti ) ) ) {
2020-12-09 04:12:44 +00:00
uint64_t total_existing_time = proc - > stime + proc - > utime ;
uint64_t total_current_time = pti . pti_total_system + pti . pti_total_user ;
2020-12-20 16:17:51 +00:00
if ( total_existing_time & & 1E-6 < time_interval ) {
2020-12-09 04:12:44 +00:00
uint64_t total_time_diff = total_current_time - total_existing_time ;
proc - > super . percent_cpu = ( ( double ) total_time_diff / time_interval ) * 100.0 ;
} else {
proc - > super . percent_cpu = 0.0 ;
2015-08-19 16:56:46 +00:00
}
2015-07-14 16:46:16 +00:00
2020-12-09 04:12:44 +00:00
proc - > super . time = total_current_time / 10000000 ;
2015-08-19 16:56:46 +00:00
proc - > super . nlwp = pti . pti_threadnum ;
2020-12-10 00:57:48 +00:00
proc - > super . m_virt = pti . pti_virtual_size / ONE_K ;
proc - > super . m_resident = pti . pti_resident_size / ONE_K ;
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
2020-10-31 21:14:27 +00:00
/ ( double ) dpl - > host_info . max_mem ;
2015-08-19 16:56:46 +00:00
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
*/
2020-10-31 22:28:02 +00:00
void DarwinProcess_scanThreads ( DarwinProcess * dp ) {
2016-02-18 16:57:09 +00:00
Process * proc = ( Process * ) dp ;
kern_return_t ret ;
2019-10-31 16:39:12 +00:00
2016-03-31 03:18:42 +00:00
if ( ! dp - > taskAccess ) {
return ;
}
2019-10-31 16:39:12 +00:00
2016-02-18 16:57:09 +00:00
if ( proc - > state = = ' Z ' ) {
return ;
}
task_t port ;
ret = task_for_pid ( mach_task_self ( ) , proc - > pid , & port ) ;
if ( ret ! = KERN_SUCCESS ) {
2016-03-31 03:18:42 +00:00
dp - > taskAccess = false ;
2016-02-18 16:57:09 +00:00
return ;
}
2019-10-31 16:39:12 +00:00
2016-02-18 16:57:09 +00:00
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 ) {
2016-03-31 03:18:42 +00:00
dp - > taskAccess = false ;
2016-02-18 16:57:09 +00:00
return ;
}
2019-10-31 16:39:12 +00:00
2016-02-18 16:57:09 +00:00
thread_array_t thread_list ;
mach_msg_type_number_t thread_count ;
ret = task_threads ( port , & thread_list , & thread_count ) ;
if ( ret ! = KERN_SUCCESS ) {
2016-03-31 03:18:42 +00:00
dp - > taskAccess = false ;
2016-02-18 16:57:09 +00:00
mach_port_deallocate ( mach_task_self ( ) , port ) ;
return ;
}
2019-10-31 16:39:12 +00:00
2016-02-18 16:57:09 +00:00
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 ;
}
2020-12-13 14:54:13 +00:00
const ProcessClass DarwinProcess_class = {
. super = {
. extends = Class ( Process ) ,
. display = Process_display ,
. delete = Process_delete ,
2020-12-17 23:09:55 +00:00
. compare = Process_compare
2020-12-13 14:54:13 +00:00
} ,
. writeField = DarwinProcess_writeField ,
2020-12-17 23:09:55 +00:00
. compareByKey = DarwinProcess_compareByKey ,
2020-12-13 14:54:13 +00:00
} ;