DarwinProcess.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. htop - DarwinProcess.c
  3. (C) 2015 Hisham H. Muhammad
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "darwin/DarwinProcess.h"
  8. #include <libproc.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <mach/mach.h>
  13. #include <sys/dirent.h>
  14. #include "CRT.h"
  15. #include "Process.h"
  16. #include "darwin/DarwinMachine.h"
  17. #include "darwin/Platform.h"
  18. const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
  19. [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
  20. [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
  21. [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
  22. [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
  23. [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
  24. [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
  25. [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
  26. [TTY] = { .name = "TTY", .title = "TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_TTY, },
  27. [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
  28. [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, .defaultSortDesc = true, },
  29. [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, .defaultSortDesc = true, },
  30. [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
  31. [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
  32. [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
  33. [ELAPSED] = { .name = "ELAPSED", .title = "ELAPSED ", .description = "Time since the process was started", .flags = 0, },
  34. [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
  35. [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
  36. [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, },
  37. [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
  38. [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, .autoTitleRightAlign = true, },
  39. [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, .autoWidth = true, },
  40. [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, },
  41. [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
  42. [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
  43. [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
  44. [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
  45. [PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
  46. [CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
  47. [TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, },
  48. };
  49. Process* DarwinProcess_new(const Machine* host) {
  50. DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess));
  51. Object_setClass(this, Class(DarwinProcess));
  52. Process_init(&this->super, host);
  53. this->utime = 0;
  54. this->stime = 0;
  55. this->taskAccess = true;
  56. this->translated = false;
  57. return (Process*)this;
  58. }
  59. void Process_delete(Object* cast) {
  60. DarwinProcess* this = (DarwinProcess*) cast;
  61. Process_done(&this->super);
  62. // free platform-specific fields here
  63. free(this);
  64. }
  65. static void DarwinProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
  66. const DarwinProcess* dp = (const DarwinProcess*) super;
  67. char buffer[256]; buffer[255] = '\0';
  68. int attr = CRT_colors[DEFAULT_COLOR];
  69. size_t n = sizeof(buffer) - 1;
  70. switch (field) {
  71. // add Platform-specific fields here
  72. case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break;
  73. default:
  74. Process_writeField(&dp->super, str, field);
  75. return;
  76. }
  77. RichString_appendWide(str, attr, buffer);
  78. }
  79. static int DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
  80. const DarwinProcess* p1 = (const DarwinProcess*)v1;
  81. const DarwinProcess* p2 = (const DarwinProcess*)v2;
  82. switch (key) {
  83. // add Platform-specific fields here
  84. case TRANSLATED:
  85. return SPACESHIP_NUMBER(p1->translated, p2->translated);
  86. default:
  87. return Process_compareByKey_Base(v1, v2, key);
  88. }
  89. }
  90. static void DarwinProcess_updateExe(pid_t pid, Process* proc) {
  91. char path[PROC_PIDPATHINFO_MAXSIZE];
  92. int r = proc_pidpath(pid, path, sizeof(path));
  93. if (r <= 0)
  94. return;
  95. Process_updateExe(proc, path);
  96. }
  97. static void DarwinProcess_updateCwd(pid_t pid, Process* proc) {
  98. struct proc_vnodepathinfo vpi;
  99. int r = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
  100. if (r <= 0) {
  101. free(proc->procCwd);
  102. proc->procCwd = NULL;
  103. return;
  104. }
  105. if (!vpi.pvi_cdir.vip_path[0]) {
  106. free(proc->procCwd);
  107. proc->procCwd = NULL;
  108. return;
  109. }
  110. free_and_xStrdup(&proc->procCwd, vpi.pvi_cdir.vip_path);
  111. }
  112. static void DarwinProcess_updateCmdLine(const struct kinfo_proc* k, Process* proc) {
  113. Process_updateComm(proc, k->kp_proc.p_comm);
  114. /* This function is from the old Mac version of htop. Originally from ps? */
  115. int mib[3], argmax, nargs, c = 0;
  116. size_t size;
  117. char *procargs, *sp, *np, *cp;
  118. /* Get the maximum process arguments size. */
  119. mib[0] = CTL_KERN;
  120. mib[1] = KERN_ARGMAX;
  121. size = sizeof( argmax );
  122. if ( sysctl( mib, 2, &argmax, &size, NULL, 0 ) == -1 ) {
  123. goto ERROR_A;
  124. }
  125. /* Allocate space for the arguments. */
  126. procargs = (char*)malloc(argmax);
  127. if ( procargs == NULL ) {
  128. goto ERROR_A;
  129. }
  130. /*
  131. * Make a sysctl() call to get the raw argument space of the process.
  132. * The layout is documented in start.s, which is part of the Csu
  133. * project. In summary, it looks like:
  134. *
  135. * /---------------\ 0x00000000
  136. * : :
  137. * : :
  138. * |---------------|
  139. * | argc |
  140. * |---------------|
  141. * | arg[0] |
  142. * |---------------|
  143. * : :
  144. * : :
  145. * |---------------|
  146. * | arg[argc - 1] |
  147. * |---------------|
  148. * | 0 |
  149. * |---------------|
  150. * | env[0] |
  151. * |---------------|
  152. * : :
  153. * : :
  154. * |---------------|
  155. * | env[n] |
  156. * |---------------|
  157. * | 0 |
  158. * |---------------| <-- Beginning of data returned by sysctl() is here.
  159. * | argc |
  160. * |---------------|
  161. * | exec_path |
  162. * |:::::::::::::::|
  163. * | |
  164. * | String area. |
  165. * | |
  166. * |---------------| <-- Top of stack.
  167. * : :
  168. * : :
  169. * \---------------/ 0xffffffff
  170. */
  171. mib[0] = CTL_KERN;
  172. mib[1] = KERN_PROCARGS2;
  173. mib[2] = k->kp_proc.p_pid;
  174. size = ( size_t ) argmax;
  175. if ( sysctl( mib, 3, procargs, &size, NULL, 0 ) == -1 ) {
  176. goto ERROR_B;
  177. }
  178. memcpy( &nargs, procargs, sizeof( nargs ) );
  179. cp = procargs + sizeof( nargs );
  180. /* Skip the saved exec_path. */
  181. for ( ; cp < &procargs[size]; cp++ ) {
  182. if ( *cp == '\0' ) {
  183. /* End of exec_path reached. */
  184. break;
  185. }
  186. }
  187. if ( cp == &procargs[size] ) {
  188. goto ERROR_B;
  189. }
  190. /* Skip trailing '\0' characters. */
  191. for ( ; cp < &procargs[size]; cp++ ) {
  192. if ( *cp != '\0' ) {
  193. /* Beginning of first argument reached. */
  194. break;
  195. }
  196. }
  197. if ( cp == &procargs[size] ) {
  198. goto ERROR_B;
  199. }
  200. /* Save where the argv[0] string starts. */
  201. sp = cp;
  202. int end = 0;
  203. for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) {
  204. if ( *cp == '\0' ) {
  205. c++;
  206. if ( np != NULL ) {
  207. /* Convert previous '\0'. */
  208. *np = ' ';
  209. }
  210. /* Note location of current '\0'. */
  211. np = cp;
  212. if (end == 0) {
  213. end = cp - sp;
  214. }
  215. }
  216. }
  217. /*
  218. * sp points to the beginning of the arguments/environment string, and
  219. * np should point to the '\0' terminator for the string.
  220. */
  221. if ( np == NULL || np == sp ) {
  222. /* Empty or unterminated string. */
  223. goto ERROR_B;
  224. }
  225. if (end == 0) {
  226. end = np - sp;
  227. }
  228. Process_updateCmdline(proc, sp, 0, end);
  229. /* Clean up. */
  230. free( procargs );
  231. return;
  232. ERROR_B:
  233. free( procargs );
  234. ERROR_A:
  235. Process_updateCmdline(proc, k->kp_proc.p_comm, 0, strlen(k->kp_proc.p_comm));
  236. }
  237. // Converts nanoseconds to hundredths of a second (centiseconds) as needed by the "time" field of the Process struct.
  238. static long long int nanosecondsToCentiseconds(uint64_t nanoseconds) {
  239. const uint64_t centiseconds_per_second = 100;
  240. const uint64_t nanoseconds_per_second = 1e9;
  241. return nanoseconds / nanoseconds_per_second * centiseconds_per_second;
  242. }
  243. static char* DarwinProcess_getDevname(dev_t dev) {
  244. if (dev == NODEV) {
  245. return NULL;
  246. }
  247. char buf[sizeof("/dev/") + MAXNAMLEN];
  248. char* name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN);
  249. if (name) {
  250. return xStrdup(name);
  251. }
  252. return NULL;
  253. }
  254. void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
  255. DarwinProcess* dp = (DarwinProcess*)proc;
  256. const Settings* settings = proc->super.host->settings;
  257. const struct extern_proc* ep = &ps->kp_proc;
  258. /* UNSET HERE :
  259. *
  260. * processor
  261. * user (set at ProcessTable level)
  262. * nlwp
  263. * percent_cpu
  264. * percent_mem
  265. * m_virt
  266. * m_resident
  267. * minflt
  268. * majflt
  269. */
  270. /* First, the "immutable" parts */
  271. if (!exists) {
  272. /* Set the PID/PGID/etc. */
  273. Process_setPid(proc, ep->p_pid);
  274. Process_setThreadGroup(proc, ep->p_pid);
  275. Process_setParent(proc, ps->kp_eproc.e_ppid);
  276. proc->pgrp = ps->kp_eproc.e_pgid;
  277. proc->session = 0; /* TODO Get the session id */
  278. proc->tpgid = ps->kp_eproc.e_tpgid;
  279. proc->isKernelThread = false;
  280. proc->isUserlandThread = false;
  281. dp->translated = ps->kp_proc.p_flag & P_TRANSLATED;
  282. proc->tty_nr = ps->kp_eproc.e_tdev;
  283. proc->tty_name = NULL;
  284. proc->starttime_ctime = ep->p_starttime.tv_sec;
  285. Process_fillStarttimeBuffer(proc);
  286. DarwinProcess_updateExe(ep->p_pid, proc);
  287. DarwinProcess_updateCmdLine(ps, proc);
  288. if (settings->ss->flags & PROCESS_FLAG_CWD) {
  289. DarwinProcess_updateCwd(ep->p_pid, proc);
  290. }
  291. }
  292. if (proc->tty_name == NULL && (dev_t)proc->tty_nr != NODEV) {
  293. /* The call to devname() is extremely expensive (due to lstat)
  294. * and represents ~95% of htop's CPU usage when there is high
  295. * process turnover.
  296. *
  297. * To mitigate this we only fetch TTY information if the TTY
  298. * field is enabled in the settings.
  299. */
  300. if (settings->ss->flags & PROCESS_FLAG_TTY) {
  301. proc->tty_name = DarwinProcess_getDevname(proc->tty_nr);
  302. if (!proc->tty_name) {
  303. /* devname failed: prevent us from calling it again */
  304. proc->tty_nr = NODEV;
  305. }
  306. }
  307. }
  308. /* Mutable information */
  309. proc->nice = ep->p_nice;
  310. proc->priority = ep->p_priority;
  311. proc->state = (ep->p_stat == SZOMB) ? ZOMBIE : UNKNOWN;
  312. /* Make sure the updated flag is set */
  313. proc->super.updated = true;
  314. }
  315. void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable* dpt, double timeIntervalNS) {
  316. struct proc_taskinfo pti;
  317. if (sizeof(pti) == proc_pidinfo(Process_getPid(&proc->super), PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
  318. const DarwinMachine* dhost = (const DarwinMachine*) proc->super.super.host;
  319. uint64_t total_existing_time_ns = proc->stime + proc->utime;
  320. uint64_t user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user);
  321. uint64_t system_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_system);
  322. uint64_t total_current_time_ns = user_time_ns + system_time_ns;
  323. if (total_existing_time_ns && 1E-6 < timeIntervalNS) {
  324. uint64_t total_time_diff_ns = total_current_time_ns - total_existing_time_ns;
  325. proc->super.percent_cpu = ((double)total_time_diff_ns / timeIntervalNS) * 100.0;
  326. } else {
  327. proc->super.percent_cpu = 0.0;
  328. }
  329. Process_updateCPUFieldWidths(proc->super.percent_cpu);
  330. proc->super.time = nanosecondsToCentiseconds(total_current_time_ns);
  331. proc->super.nlwp = pti.pti_threadnum;
  332. proc->super.m_virt = pti.pti_virtual_size / ONE_K;
  333. proc->super.m_resident = pti.pti_resident_size / ONE_K;
  334. proc->super.majflt = pti.pti_faults;
  335. proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
  336. / (double)dhost->host_info.max_mem;
  337. proc->stime = system_time_ns;
  338. proc->utime = user_time_ns;
  339. dpt->super.kernelThreads += 0; /*pti.pti_threads_system;*/
  340. dpt->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/
  341. dpt->super.totalTasks += pti.pti_threadnum;
  342. dpt->super.runningTasks += pti.pti_numrunning;
  343. }
  344. }
  345. static ProcessState stateToChar(int run_state) {
  346. switch (run_state) {
  347. case TH_STATE_RUNNING:
  348. return RUNNING;
  349. case TH_STATE_STOPPED:
  350. return STOPPED;
  351. case TH_STATE_WAITING:
  352. return WAITING;
  353. case TH_STATE_UNINTERRUPTIBLE:
  354. return UNINTERRUPTIBLE_WAIT;
  355. case TH_STATE_HALTED:
  356. return BLOCKED;
  357. }
  358. return UNKNOWN;
  359. }
  360. /*
  361. * Scan threads for process state information.
  362. * Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
  363. * and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
  364. */
  365. void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt) {
  366. Process* proc = (Process*) dp;
  367. kern_return_t ret;
  368. if (!dp->taskAccess) {
  369. return;
  370. }
  371. if (proc->state == ZOMBIE) {
  372. return;
  373. }
  374. pid_t pid = Process_getPid(proc);
  375. task_t task;
  376. ret = task_for_pid(mach_task_self(), pid, &task);
  377. if (ret != KERN_SUCCESS) {
  378. // TODO: workaround for modern MacOS limits on task_for_pid()
  379. if (ret != KERN_FAILURE)
  380. CRT_debug("task_for_pid(%d) failed: %s", pid, mach_error_string(ret));
  381. dp->taskAccess = false;
  382. return;
  383. }
  384. {
  385. task_info_data_t tinfo;
  386. mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
  387. ret = task_info(task, TASK_BASIC_INFO, (task_info_t) &tinfo, &task_info_count);
  388. if (ret != KERN_SUCCESS) {
  389. CRT_debug("task_info(%d) failed: %s", pid, mach_error_string(ret));
  390. dp->taskAccess = false;
  391. mach_port_deallocate(mach_task_self(), task);
  392. return;
  393. }
  394. }
  395. thread_array_t thread_list;
  396. mach_msg_type_number_t thread_count;
  397. ret = task_threads(task, &thread_list, &thread_count);
  398. if (ret != KERN_SUCCESS) {
  399. CRT_debug("task_threads(%d) failed: %s", pid, mach_error_string(ret));
  400. dp->taskAccess = false;
  401. mach_port_deallocate(mach_task_self(), task);
  402. return;
  403. }
  404. const bool hideUserlandThreads = dpt->super.super.host->settings->hideUserlandThreads;
  405. integer_t run_state = 999;
  406. for (mach_msg_type_number_t i = 0; i < thread_count; i++) {
  407. thread_identifier_info_data_t identifer_info;
  408. mach_msg_type_number_t identifer_info_count = THREAD_IDENTIFIER_INFO_COUNT;
  409. ret = thread_info(thread_list[i], THREAD_IDENTIFIER_INFO, (thread_info_t) &identifer_info, &identifer_info_count);
  410. if (ret != KERN_SUCCESS) {
  411. CRT_debug("thread_info(%d:%d) for identifier failed: %s", pid, i, mach_error_string(ret));
  412. continue;
  413. }
  414. uint64_t tid = identifer_info.thread_id;
  415. bool preExisting;
  416. Process *tprocess = ProcessTable_getProcess(&dpt->super, tid, &preExisting, DarwinProcess_new);
  417. tprocess->super.updated = true;
  418. dpt->super.totalTasks++;
  419. if (hideUserlandThreads) {
  420. tprocess->super.show = false;
  421. continue;
  422. }
  423. assert(Process_getPid(tprocess) == tid);
  424. Process_setParent(tprocess, pid);
  425. Process_setThreadGroup(tprocess, pid);
  426. tprocess->super.show = true;
  427. tprocess->isUserlandThread = true;
  428. tprocess->st_uid = proc->st_uid;
  429. tprocess->user = proc->user;
  430. thread_extended_info_data_t extended_info;
  431. mach_msg_type_number_t extended_info_count = THREAD_EXTENDED_INFO_COUNT;
  432. ret = thread_info(thread_list[i], THREAD_EXTENDED_INFO, (thread_info_t) &extended_info, &extended_info_count);
  433. if (ret != KERN_SUCCESS) {
  434. CRT_debug("thread_info(%d:%d) for extended failed: %s", pid, i, mach_error_string(ret));
  435. continue;
  436. }
  437. DarwinProcess* tdproc = (DarwinProcess*)tprocess;
  438. tdproc->super.state = stateToChar(extended_info.pth_run_state);
  439. tdproc->super.percent_cpu = extended_info.pth_cpu_usage / 10.0;
  440. tdproc->stime = extended_info.pth_system_time;
  441. tdproc->utime = extended_info.pth_user_time;
  442. tdproc->super.time = (extended_info.pth_system_time + extended_info.pth_user_time) / 10000000;
  443. tdproc->super.priority = extended_info.pth_curpri;
  444. if (extended_info.pth_run_state < run_state)
  445. run_state = extended_info.pth_run_state;
  446. // TODO: depend on setting
  447. const char* name = extended_info.pth_name[0] != '\0' ? extended_info.pth_name : proc->procComm;
  448. Process_updateCmdline(tprocess, name, 0, strlen(name));
  449. if (!preExisting)
  450. ProcessTable_add(&dpt->super, tprocess);
  451. }
  452. vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
  453. mach_port_deallocate(mach_task_self(), task);
  454. if (run_state != 999)
  455. proc->state = stateToChar(run_state);
  456. }
  457. const ProcessClass DarwinProcess_class = {
  458. .super = {
  459. .super = {
  460. .extends = Class(Process),
  461. .display = Row_display,
  462. .delete = Process_delete,
  463. .compare = Process_compare
  464. },
  465. .isHighlighted = Process_rowIsHighlighted,
  466. .isVisible = Process_rowIsVisible,
  467. .matchesFilter = Process_rowMatchesFilter,
  468. .compareByParent = Process_compareByParent,
  469. .sortKeyString = Process_rowGetSortKey,
  470. .writeField = DarwinProcess_rowWriteField
  471. },
  472. .compareByKey = DarwinProcess_compareByKey
  473. };