DarwinProcess.c 9.87 KB
Newer Older
1
2
3
4
5
6
7
8
9
/*
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"
David Hunt's avatar
David Hunt committed
10

11
#include <stdlib.h>
David Hunt's avatar
David Hunt committed
12
13
#include <libproc.h>
#include <string.h>
David Hunt's avatar
David Hunt committed
14
#include <stdio.h>
15

16
17
#include <mach/mach.h>

18
19
/*{
#include "Settings.h"
David Hunt's avatar
David Hunt committed
20
21
#include "DarwinProcessList.h"

David Hunt's avatar
David Hunt committed
22
#include <sys/sysctl.h>
23

David Hunt's avatar
David Hunt committed
24
25
26
27
28
typedef struct DarwinProcess_ {
   Process super;

   uint64_t utime;
   uint64_t stime;
Hisham's avatar
Hisham committed
29
   bool taskAccess;
David Hunt's avatar
David Hunt committed
30
31
} DarwinProcess;

32
33
}*/

David Hunt's avatar
David Hunt committed
34
35
36
37
38
39
40
41
42
43
44
ProcessClass DarwinProcess_class = {
   .super = {
      .extends = Class(Process),
      .display = Process_display,
      .delete = Process_delete,
      .compare = Process_compare
   },
   .writeField = Process_writeField,
};

DarwinProcess* DarwinProcess_new(Settings* settings) {
Hisham's avatar
Hisham committed
45
   DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess));
David Hunt's avatar
David Hunt committed
46
47
48
49
50
   Object_setClass(this, Class(DarwinProcess));
   Process_init(&this->super, settings);

   this->utime = 0;
   this->stime = 0;
Hisham's avatar
Hisham committed
51
   this->taskAccess = true;
David Hunt's avatar
David Hunt committed
52

53
54
55
   return this;
}

David Hunt's avatar
David Hunt committed
56
void Process_delete(Object* cast) {
David Hunt's avatar
David Hunt committed
57
58
   DarwinProcess* this = (DarwinProcess*) cast;
   Process_done(&this->super);
59
60
61
62
   // free platform-specific fields here
   free(this);
}

David Hunt's avatar
David Hunt committed
63
bool Process_isThread(Process* this) {
David Hunt's avatar
David Hunt committed
64
   (void) this;
David Hunt's avatar
David Hunt committed
65
66
67
68
   return false;
}

void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now) {
Hisham Muhammad's avatar
Hisham Muhammad committed
69
   struct tm date;
David Hunt's avatar
David Hunt committed
70

Hisham Muhammad's avatar
Hisham Muhammad committed
71
72
73
   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);
David Hunt's avatar
David Hunt committed
74
75
}

76
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
David Hunt's avatar
David Hunt committed
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
   /* 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. */
Hisham's avatar
Hisham committed
92
   procargs = ( char * ) xMalloc( argmax );
David Hunt's avatar
David Hunt committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
   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;

174
   *basenameOffset = 0;
David Hunt's avatar
David Hunt committed
175
176
177
178
179
180
181
182
183
   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;
184
185
        if (*basenameOffset == 0) {
           *basenameOffset = cp - sp;
David Hunt's avatar
David Hunt committed
186
187
        }
     }
188
   }
David Hunt's avatar
David Hunt committed
189
190
191
192
193
194
195
196
197

   /*
    * 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;
   }
198
199
200
   if (*basenameOffset == 0) {
      *basenameOffset = np - sp;
   }
David Hunt's avatar
David Hunt committed
201
202

   /* Make a copy of the string. */
Hisham's avatar
Hisham committed
203
   retval = xStrdup(sp);
David Hunt's avatar
David Hunt committed
204
205
206
207
208
209
210
211
212

   /* Clean up. */
   free( procargs );

   return retval;

ERROR_B:
   free( procargs );
ERROR_A:
Hisham's avatar
Hisham committed
213
   retval = xStrdup(k->kp_proc.p_comm);
214
215
   *basenameOffset = strlen(retval);
   
David Hunt's avatar
David Hunt committed
216
217
218
219
   return retval;
}

void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists) {
Hisham Muhammad's avatar
Hisham Muhammad committed
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
   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 */
242
243
      proc->tpgid = ps->kp_eproc.e_tpgid;
      proc->tgid = proc->pid;
Hisham Muhammad's avatar
Hisham Muhammad committed
244
245
246
247
248
249
      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);
250
      proc->comm = DarwinProcess_getCmdLine(ps, &(proc->basenameOffset));
Hisham Muhammad's avatar
Hisham Muhammad committed
251
252
253
254
255
256
   }

   /* Mutable information */
   proc->nice = ep->p_nice;
   proc->priority = ep->p_priority;

257
   proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
Hisham Muhammad's avatar
Hisham Muhammad committed
258
259
260

   /* Make sure the updated flag is set */
   proc->updated = true;
David Hunt's avatar
David Hunt committed
261
262
}

David Hunt's avatar
David Hunt committed
263
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl) {
Hisham Muhammad's avatar
Hisham Muhammad committed
264
   struct proc_taskinfo pti;
David Hunt's avatar
David Hunt committed
265

Hisham Muhammad's avatar
Hisham Muhammad committed
266
267
268
269
   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);
David Hunt's avatar
David Hunt committed
270

Hisham Muhammad's avatar
Hisham Muhammad committed
271
272
         proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount
                  / ((double)dpl->global_diff * 100000.0);
David Hunt's avatar
David Hunt committed
273

Hisham Muhammad's avatar
Hisham Muhammad committed
274
275
276
277
//       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);
      }
David Hunt's avatar
David Hunt committed
278

Hisham Muhammad's avatar
Hisham Muhammad committed
279
280
      proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
      proc->super.nlwp = pti.pti_threadnum;
Hisham's avatar
Hisham committed
281
282
      proc->super.m_size = pti.pti_virtual_size / 1024 / PAGE_SIZE_KB;
      proc->super.m_resident = pti.pti_resident_size / 1024 / PAGE_SIZE_KB;
Hisham Muhammad's avatar
Hisham Muhammad committed
283
284
285
286
287
288
289
290
291
292
293
294
      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;
   }
David Hunt's avatar
David Hunt committed
295
}
296
297
298
299
300
301
302
303
304
305

/*
 * 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;
   
Hisham's avatar
Hisham committed
306
307
308
309
   if (!dp->taskAccess) {
      return;
   }
   
310
311
312
313
314
315
316
   if (proc->state == 'Z') {
      return;
   }

   task_t port;
   ret = task_for_pid(mach_task_self(), proc->pid, &port);
   if (ret != KERN_SUCCESS) {
Hisham's avatar
Hisham committed
317
      dp->taskAccess = false;
318
319
320
321
322
323
324
      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) {
Hisham's avatar
Hisham committed
325
      dp->taskAccess = false;
326
327
328
329
330
331
332
      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) {
Hisham's avatar
Hisham committed
333
      dp->taskAccess = false;
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
      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;
}