DarwinProcessList.c 6.58 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
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"
Hisham Muhammad's avatar
Hisham Muhammad committed
11
#include "CRT.h"
12
13
14

#include <stdlib.h>
#include <string.h>
David Hunt's avatar
David Hunt committed
15
16
17
#include <unistd.h>
#include <stdio.h>
#include <libproc.h>
David Hunt's avatar
David Hunt committed
18
#include <sys/mman.h>
David Hunt's avatar
David Hunt committed
19
#include <utmpx.h>
20
#include <err.h>
21
22
23
#include <sys/sysctl.h>
#include <stdbool.h>

24
/*{
David Hunt's avatar
David Hunt committed
25
#include "ProcessList.h"
David Hunt's avatar
David Hunt committed
26
27
28
29
30
31
#include <mach/mach_host.h>
#include <sys/sysctl.h>

typedef struct DarwinProcessList_ {
   ProcessList super;

David Hunt's avatar
David Hunt committed
32
   host_basic_info_data_t host_info;
33
   vm_statistics_data_t vm_stats;
David Hunt's avatar
David Hunt committed
34
35
   processor_cpu_load_info_t prev_load;
   processor_cpu_load_info_t curr_load;
David Hunt's avatar
David Hunt committed
36
37
   uint64_t kernel_threads;
   uint64_t user_threads;
David Hunt's avatar
David Hunt committed
38
   uint64_t global_diff;
David Hunt's avatar
David Hunt committed
39
} DarwinProcessList;
40
41
42

}*/

Hisham Muhammad's avatar
Hisham Muhammad committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
typedef struct kern {
   short int version[3];
} kern_;

static void getKernelVersion(struct kern *k) {
   static short int version_[3] = {0};
   if (!version_[0]) {
      // just in case it fails someday
      version_[0] = version_[1] = version_[2] = -1;
      char str[256] = {0};
      size_t size = sizeof(str);
      int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
      if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
   }
   memcpy(k->version, version_, sizeof(version_));
}

static int compareKernelVersion(short int major, short int minor, short int component) {
   /*
   compare the given os version with the one installed returns:
   0 if equals the installed version
   positive value if less than the installed version
   negative value if more than the installed version
   */
   struct kern k;
   getKernelVersion(&k);
   if ( k.version[0] != major) return k.version[0] - major;
   if ( k.version[1] != minor) return k.version[1] - minor;
   if ( k.version[2] != component) return k.version[2] - component;
   return 0;
}

David Hunt's avatar
David Hunt committed
75
76
77
void ProcessList_getHostInfo(host_basic_info_data_t *p) {
   mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;

Hisham Muhammad's avatar
Hisham Muhammad committed
78
79
   if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
      CRT_fatalError("Unable to retrieve host info\n");
David Hunt's avatar
David Hunt committed
80
81
82
   }
}

David Hunt's avatar
David Hunt committed
83
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
Hisham Muhammad's avatar
Hisham Muhammad committed
84
85
86
87
   if (NULL != p && NULL != *p) {
      if (0 != munmap(*p, vm_page_size)) {
         CRT_fatalError("Unable to free old CPU load information\n");
      }
David Hunt's avatar
David Hunt committed
88
89
   }

David Hunt's avatar
David Hunt committed
90
91
92
93
94
95
96
   *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;

David Hunt's avatar
David Hunt committed
97
   // TODO Improving the accuracy of the load counts woule help a lot.
Hisham Muhammad's avatar
Hisham Muhammad committed
98
   if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
99
       CRT_fatalError("Unable to retrieve CPU info\n");
David Hunt's avatar
David Hunt committed
100
101
102
103
104
   }

   return cpu_count;
}

105
void ProcessList_getVMStats(vm_statistics_t p) {
Hisham Muhammad's avatar
Hisham Muhammad committed
106
   mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
David Hunt's avatar
David Hunt committed
107

Hisham Muhammad's avatar
Hisham Muhammad committed
108
109
   if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
      CRT_fatalError("Unable to retrieve VM statistics\n");
David Hunt's avatar
David Hunt committed
110
111
}

David Hunt's avatar
David Hunt committed
112
113
114
115
116
117
118
119
120
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;
121
   if (sysctl(mib, 4, NULL, count, NULL, 0) < 0)
122
      CRT_fatalError("Unable to get size of kproc_infos");
David Hunt's avatar
David Hunt committed
123

Hisham's avatar
Hisham committed
124
   processes = xMalloc(*count);
125
   if (processes == NULL)
126
      CRT_fatalError("Out of memory for kproc_infos");
David Hunt's avatar
David Hunt committed
127

128
   if (sysctl(mib, 4, processes, count, NULL, 0) < 0)
129
      CRT_fatalError("Unable to get kinfo_procs");
David Hunt's avatar
David Hunt committed
130
131
132
133
134
135
136

   *count = *count / sizeof(struct kinfo_proc);

   return processes;
}


137
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
Hisham's avatar
Hisham committed
138
   DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
David Hunt's avatar
David Hunt committed
139
140

   ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId);
David Hunt's avatar
David Hunt committed
141
142

   /* Initialize the CPU information */
David Hunt's avatar
David Hunt committed
143
   this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load);
David Hunt's avatar
David Hunt committed
144
   ProcessList_getHostInfo(&this->host_info);
David Hunt's avatar
David Hunt committed
145
   ProcessList_allocateCPULoadInfo(&this->curr_load);
David Hunt's avatar
David Hunt committed
146

David Hunt's avatar
David Hunt committed
147
148
149
150
151
152
153
154
   /* 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;

David Hunt's avatar
David Hunt committed
155
   return &this->super;
156
157
158
159
160
161
162
163
}

void ProcessList_delete(ProcessList* this) {
   ProcessList_done(this);
   free(this);
}

void ProcessList_goThroughEntries(ProcessList* super) {
Hisham Muhammad's avatar
Hisham Muhammad committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
   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);

      // Disabled for High Sierra due to bug in macOS High Sierra
      bool isScanThreadSupported  = ! ( compareKernelVersion(17, 0, 0) >= 0 && compareKernelVersion(17, 5, 0) < 0);

      if (isScanThreadSupported){
         DarwinProcess_scanThreads(proc);
      }

      super->totalTasks += 1;

      if (!preExisting) {
         proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);

         ProcessList_add(super, &proc->super);
      }
   }

   free(ps);
225
}