1 /*
2 htop - darwin/Platform.c
3 (C) 2014 Hisham H. Muhammad
4 (C) 2015 David C. Hunt
5 Released under the GNU GPLv2+, see the COPYING file
6 in the source distribution for its full text.
7 */
8 
9 #include "config.h" // IWYU pragma: keep
10 
11 #include "darwin/Platform.h"
12 
13 #include <errno.h>
14 #include <math.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <CoreFoundation/CFString.h>
18 #include <CoreFoundation/CoreFoundation.h>
19 #include <IOKit/ps/IOPowerSources.h>
20 #include <IOKit/ps/IOPSKeys.h>
21 
22 #include "ClockMeter.h"
23 #include "CPUMeter.h"
24 #include "CRT.h"
25 #include "DateMeter.h"
26 #include "DateTimeMeter.h"
27 #include "HostnameMeter.h"
28 #include "LoadAverageMeter.h"
29 #include "Macros.h"
30 #include "MemoryMeter.h"
31 #include "MemorySwapMeter.h"
32 #include "ProcessLocksScreen.h"
33 #include "SwapMeter.h"
34 #include "SysArchMeter.h"
35 #include "TasksMeter.h"
36 #include "UptimeMeter.h"
37 #include "darwin/DarwinProcessList.h"
38 #include "darwin/PlatformHelpers.h"
39 #include "zfs/ZfsArcMeter.h"
40 #include "zfs/ZfsCompressedArcMeter.h"
41 
42 #ifdef HAVE_HOST_GET_CLOCK_SERVICE
43 #include <mach/clock.h>
44 #include <mach/mach.h>
45 #endif
46 
47 #ifdef HAVE_MACH_MACH_TIME_H
48 #include <mach/mach_time.h>
49 #endif
50 
51 
52 const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
53 
54 const SignalItem Platform_signals[] = {
55    { .name = " 0 Cancel",    .number =  0 },
56    { .name = " 1 SIGHUP",    .number =  1 },
57    { .name = " 2 SIGINT",    .number =  2 },
58    { .name = " 3 SIGQUIT",   .number =  3 },
59    { .name = " 4 SIGILL",    .number =  4 },
60    { .name = " 5 SIGTRAP",   .number =  5 },
61    { .name = " 6 SIGABRT",   .number =  6 },
62    { .name = " 6 SIGIOT",    .number =  6 },
63    { .name = " 7 SIGEMT",    .number =  7 },
64    { .name = " 8 SIGFPE",    .number =  8 },
65    { .name = " 9 SIGKILL",   .number =  9 },
66    { .name = "10 SIGBUS",    .number = 10 },
67    { .name = "11 SIGSEGV",   .number = 11 },
68    { .name = "12 SIGSYS",    .number = 12 },
69    { .name = "13 SIGPIPE",   .number = 13 },
70    { .name = "14 SIGALRM",   .number = 14 },
71    { .name = "15 SIGTERM",   .number = 15 },
72    { .name = "16 SIGURG",    .number = 16 },
73    { .name = "17 SIGSTOP",   .number = 17 },
74    { .name = "18 SIGTSTP",   .number = 18 },
75    { .name = "19 SIGCONT",   .number = 19 },
76    { .name = "20 SIGCHLD",   .number = 20 },
77    { .name = "21 SIGTTIN",   .number = 21 },
78    { .name = "22 SIGTTOU",   .number = 22 },
79    { .name = "23 SIGIO",     .number = 23 },
80    { .name = "24 SIGXCPU",   .number = 24 },
81    { .name = "25 SIGXFSZ",   .number = 25 },
82    { .name = "26 SIGVTALRM", .number = 26 },
83    { .name = "27 SIGPROF",   .number = 27 },
84    { .name = "28 SIGWINCH",  .number = 28 },
85    { .name = "29 SIGINFO",   .number = 29 },
86    { .name = "30 SIGUSR1",   .number = 30 },
87    { .name = "31 SIGUSR2",   .number = 31 },
88 };
89 
90 const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
91 
92 const MeterClass* const Platform_meterTypes[] = {
93    &CPUMeter_class,
94    &ClockMeter_class,
95    &DateMeter_class,
96    &DateTimeMeter_class,
97    &LoadAverageMeter_class,
98    &LoadMeter_class,
99    &MemoryMeter_class,
100    &SwapMeter_class,
101    &MemorySwapMeter_class,
102    &TasksMeter_class,
103    &BatteryMeter_class,
104    &HostnameMeter_class,
105    &SysArchMeter_class,
106    &UptimeMeter_class,
107    &AllCPUsMeter_class,
108    &AllCPUs2Meter_class,
109    &AllCPUs4Meter_class,
110    &AllCPUs8Meter_class,
111    &LeftCPUsMeter_class,
112    &RightCPUsMeter_class,
113    &LeftCPUs2Meter_class,
114    &RightCPUs2Meter_class,
115    &LeftCPUs4Meter_class,
116    &RightCPUs4Meter_class,
117    &LeftCPUs8Meter_class,
118    &RightCPUs8Meter_class,
119    &ZfsArcMeter_class,
120    &ZfsCompressedArcMeter_class,
121    &BlankMeter_class,
122    NULL
123 };
124 
125 static double Platform_nanosecondsPerMachTick = 1.0;
126 
127 static double Platform_nanosecondsPerSchedulerTick = -1;
128 
Platform_init(void)129 bool Platform_init(void) {
130    Platform_nanosecondsPerMachTick = Platform_calculateNanosecondsPerMachTick();
131 
132    // Determine the number of scheduler clock ticks per second
133    errno = 0;
134    long scheduler_ticks_per_sec = sysconf(_SC_CLK_TCK);
135 
136    if (errno || scheduler_ticks_per_sec < 1) {
137       CRT_fatalError("Unable to retrieve clock tick rate");
138    }
139 
140    const double nanos_per_sec = 1e9;
141    Platform_nanosecondsPerSchedulerTick = nanos_per_sec / scheduler_ticks_per_sec;
142 
143    return true;
144 }
145 
146 // Converts ticks in the Mach "timebase" to nanoseconds.
147 // See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
Platform_machTicksToNanoseconds(uint64_t mach_ticks)148 uint64_t Platform_machTicksToNanoseconds(uint64_t mach_ticks) {
149    return (uint64_t) ((double) mach_ticks * Platform_nanosecondsPerMachTick);
150 }
151 
152 // Converts "scheduler ticks" to nanoseconds.
153 // See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_nanosecondsPerSchedulerTick` constant.
Platform_schedulerTicksToNanoseconds(const double scheduler_ticks)154 double Platform_schedulerTicksToNanoseconds(const double scheduler_ticks) {
155    return scheduler_ticks * Platform_nanosecondsPerSchedulerTick;
156 }
157 
Platform_done(void)158 void Platform_done(void) {
159    /* no platform-specific cleanup needed */
160 }
161 
Platform_setBindings(Htop_Action * keys)162 void Platform_setBindings(Htop_Action* keys) {
163    /* no platform-specific key bindings */
164    (void) keys;
165 }
166 
Platform_getUptime()167 int Platform_getUptime() {
168    struct timeval bootTime, currTime;
169    int mib[2] = { CTL_KERN, KERN_BOOTTIME };
170    size_t size = sizeof(bootTime);
171 
172    int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
173    if (err) {
174       return -1;
175    }
176    gettimeofday(&currTime, NULL);
177 
178    return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
179 }
180 
Platform_getLoadAverage(double * one,double * five,double * fifteen)181 void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
182    double results[3];
183 
184    if (3 == getloadavg(results, 3)) {
185       *one = results[0];
186       *five = results[1];
187       *fifteen = results[2];
188    } else {
189       *one = 0;
190       *five = 0;
191       *fifteen = 0;
192    }
193 }
194 
Platform_getMaxPid()195 int Platform_getMaxPid() {
196    /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */
197    return 99999;
198 }
199 
Platform_setCPUAverageValues(Meter * mtr)200 static double Platform_setCPUAverageValues(Meter* mtr) {
201    const ProcessList* dpl = mtr->pl;
202    unsigned int activeCPUs = dpl->activeCPUs;
203    double sumNice = 0.0;
204    double sumNormal = 0.0;
205    double sumKernel = 0.0;
206    double sumPercent = 0.0;
207    for (unsigned int i = 1; i <= dpl->existingCPUs; i++) {
208       sumPercent += Platform_setCPUValues(mtr, i);
209       sumNice    += mtr->values[CPU_METER_NICE];
210       sumNormal  += mtr->values[CPU_METER_NORMAL];
211       sumKernel  += mtr->values[CPU_METER_KERNEL];
212    }
213    mtr->values[CPU_METER_NICE]   = sumNice   / activeCPUs;
214    mtr->values[CPU_METER_NORMAL] = sumNormal / activeCPUs;
215    mtr->values[CPU_METER_KERNEL] = sumKernel / activeCPUs;
216    return sumPercent / activeCPUs;
217 }
218 
Platform_setCPUValues(Meter * mtr,unsigned int cpu)219 double Platform_setCPUValues(Meter* mtr, unsigned int cpu) {
220 
221    if (cpu == 0) {
222       return Platform_setCPUAverageValues(mtr);
223    }
224 
225    const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl;
226    const processor_cpu_load_info_t prev = &dpl->prev_load[cpu - 1];
227    const processor_cpu_load_info_t curr = &dpl->curr_load[cpu - 1];
228    double total = 0;
229 
230    /* Take the sums */
231    for (size_t i = 0; i < CPU_STATE_MAX; ++i) {
232       total += (double)curr->cpu_ticks[i] - (double)prev->cpu_ticks[i];
233    }
234 
235    mtr->values[CPU_METER_NICE]
236       = ((double)curr->cpu_ticks[CPU_STATE_NICE] - (double)prev->cpu_ticks[CPU_STATE_NICE]) * 100.0 / total;
237    mtr->values[CPU_METER_NORMAL]
238       = ((double)curr->cpu_ticks[CPU_STATE_USER] - (double)prev->cpu_ticks[CPU_STATE_USER]) * 100.0 / total;
239    mtr->values[CPU_METER_KERNEL]
240       = ((double)curr->cpu_ticks[CPU_STATE_SYSTEM] - (double)prev->cpu_ticks[CPU_STATE_SYSTEM]) * 100.0 / total;
241 
242    mtr->curItems = 3;
243 
244    /* Convert to percent and return */
245    total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL];
246 
247    mtr->values[CPU_METER_FREQUENCY] = NAN;
248    mtr->values[CPU_METER_TEMPERATURE] = NAN;
249 
250    return CLAMP(total, 0.0, 100.0);
251 }
252 
Platform_setMemoryValues(Meter * mtr)253 void Platform_setMemoryValues(Meter* mtr) {
254    const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl;
255    const struct vm_statistics* vm = &dpl->vm_stats;
256    double page_K = (double)vm_page_size / (double)1024;
257 
258    mtr->total = dpl->host_info.max_mem / 1024;
259    mtr->values[0] = (double)(vm->active_count + vm->wire_count) * page_K;
260    mtr->values[1] = (double)vm->purgeable_count * page_K;
261    // mtr->values[2] = "shared memory, like tmpfs and shm"
262    mtr->values[3] = (double)vm->inactive_count * page_K;
263    // mtr->values[4] = "available memory"
264 }
265 
Platform_setSwapValues(Meter * mtr)266 void Platform_setSwapValues(Meter* mtr) {
267    int mib[2] = {CTL_VM, VM_SWAPUSAGE};
268    struct xsw_usage swapused;
269    size_t swlen = sizeof(swapused);
270    sysctl(mib, 2, &swapused, &swlen, NULL, 0);
271 
272    mtr->total = swapused.xsu_total / 1024;
273    mtr->values[0] = swapused.xsu_used / 1024;
274 }
275 
Platform_setZfsArcValues(Meter * this)276 void Platform_setZfsArcValues(Meter* this) {
277    const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl;
278 
279    ZfsArcMeter_readStats(this, &(dpl->zfs));
280 }
281 
Platform_setZfsCompressedArcValues(Meter * this)282 void Platform_setZfsCompressedArcValues(Meter* this) {
283    const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl;
284 
285    ZfsCompressedArcMeter_readStats(this, &(dpl->zfs));
286 }
287 
Platform_getProcessEnv(pid_t pid)288 char* Platform_getProcessEnv(pid_t pid) {
289    char* env = NULL;
290 
291    int argmax;
292    size_t bufsz = sizeof(argmax);
293 
294    int mib[3];
295    mib[0] = CTL_KERN;
296    mib[1] = KERN_ARGMAX;
297    if (sysctl(mib, 2, &argmax, &bufsz, 0, 0) == 0) {
298       char* buf = xMalloc(argmax);
299       if (buf) {
300          mib[0] = CTL_KERN;
301          mib[1] = KERN_PROCARGS2;
302          mib[2] = pid;
303          bufsz = argmax;
304          if (sysctl(mib, 3, buf, &bufsz, 0, 0) == 0) {
305             if (bufsz > sizeof(int)) {
306                char *p = buf, *endp = buf + bufsz;
307                int argc = *(int*)(void*)p;
308                p += sizeof(int);
309 
310                // skip exe
311                p = strchr(p, 0) + 1;
312 
313                // skip padding
314                while (!*p && p < endp)
315                   ++p;
316 
317                // skip argv
318                for (; argc-- && p < endp; p = strrchr(p, 0) + 1)
319                   ;
320 
321                // skip padding
322                while (!*p && p < endp)
323                   ++p;
324 
325                size_t size = endp - p;
326                env = xMalloc(size + 2);
327                memcpy(env, p, size);
328                env[size] = 0;
329                env[size + 1] = 0;
330             }
331          }
332          free(buf);
333       }
334    }
335 
336    return env;
337 }
338 
Platform_getInodeFilename(pid_t pid,ino_t inode)339 char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
340    (void)pid;
341    (void)inode;
342    return NULL;
343 }
344 
Platform_getProcessLocks(pid_t pid)345 FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
346    (void)pid;
347    return NULL;
348 }
349 
Platform_getDiskIO(DiskIOData * data)350 bool Platform_getDiskIO(DiskIOData* data) {
351    // TODO
352    (void)data;
353    return false;
354 }
355 
Platform_getNetworkIO(NetworkIOData * data)356 bool Platform_getNetworkIO(NetworkIOData* data) {
357    // TODO
358    (void)data;
359    return false;
360 }
361 
Platform_getBattery(double * percent,ACPresence * isOnAC)362 void Platform_getBattery(double* percent, ACPresence* isOnAC) {
363    *percent = NAN;
364    *isOnAC = AC_ERROR;
365 
366    CFArrayRef list = NULL;
367 
368    CFTypeRef power_sources = IOPSCopyPowerSourcesInfo();
369    if (!power_sources)
370       goto cleanup;
371 
372    list = IOPSCopyPowerSourcesList(power_sources);
373    if (!list)
374       goto cleanup;
375 
376    double cap_current = 0.0;
377    double cap_max = 0.0;
378 
379    /* Get the battery */
380    for (int i = 0, len = CFArrayGetCount(list); i < len; ++i) {
381       CFDictionaryRef power_source = IOPSGetPowerSourceDescription(power_sources, CFArrayGetValueAtIndex(list, i)); /* GET rule */
382 
383       if (!power_source)
384          continue;
385 
386       CFStringRef power_type = CFDictionaryGetValue(power_source, CFSTR(kIOPSTransportTypeKey)); /* GET rule */
387 
388       if (kCFCompareEqualTo != CFStringCompare(power_type, CFSTR(kIOPSInternalType), 0))
389          continue;
390 
391       /* Determine the AC state */
392       CFStringRef power_state = CFDictionaryGetValue(power_source, CFSTR(kIOPSPowerSourceStateKey));
393 
394       if (*isOnAC != AC_PRESENT)
395          *isOnAC = (kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0)) ? AC_PRESENT : AC_ABSENT;
396 
397       /* Get the percentage remaining */
398       double tmp;
399       CFNumberGetValue(CFDictionaryGetValue(power_source, CFSTR(kIOPSCurrentCapacityKey)), kCFNumberDoubleType, &tmp);
400       cap_current += tmp;
401       CFNumberGetValue(CFDictionaryGetValue(power_source, CFSTR(kIOPSMaxCapacityKey)), kCFNumberDoubleType, &tmp);
402       cap_max += tmp;
403    }
404 
405    if (cap_max > 0.0)
406       *percent = 100.0 * cap_current / cap_max;
407 
408 cleanup:
409    if (list)
410       CFRelease(list);
411 
412    if (power_sources)
413       CFRelease(power_sources);
414 }
415 
Platform_gettime_monotonic(uint64_t * msec)416 void Platform_gettime_monotonic(uint64_t* msec) {
417 
418 #ifdef HAVE_HOST_GET_CLOCK_SERVICE
419 
420    clock_serv_t cclock;
421    mach_timespec_t mts;
422 
423    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
424    clock_get_time(cclock, &mts);
425    mach_port_deallocate(mach_task_self(), cclock);
426 
427    *msec = ((uint64_t)mts.tv_sec * 1000) + ((uint64_t)mts.tv_nsec / 1000000);
428 
429 #else
430 
431    Generic_gettime_monotonic(msec);
432 
433 #endif
434 
435 }
436