NetBSDProcessTable.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. htop - NetBSDProcessTable.c
  3. (C) 2014 Hisham H. Muhammad
  4. (C) 2015 Michael McConville
  5. (C) 2021 Santhosh Raju
  6. (C) 2021 htop dev team
  7. Released under the GNU GPLv2+, see the COPYING file
  8. in the source distribution for its full text.
  9. */
  10. #include "config.h" // IWYU pragma: keep
  11. #include "netbsd/NetBSDProcessTable.h"
  12. #include <kvm.h>
  13. #include <math.h>
  14. #include <limits.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <unistd.h>
  18. #include <sys/mount.h>
  19. #include <sys/param.h>
  20. #include <sys/proc.h>
  21. #include <sys/sched.h>
  22. #include <sys/sysctl.h>
  23. #include <sys/types.h>
  24. #include <uvm/uvm_extern.h>
  25. #include "CRT.h"
  26. #include "Macros.h"
  27. #include "Object.h"
  28. #include "Process.h"
  29. #include "ProcessTable.h"
  30. #include "Settings.h"
  31. #include "XUtils.h"
  32. #include "netbsd/NetBSDMachine.h"
  33. #include "netbsd/NetBSDProcess.h"
  34. ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
  35. NetBSDProcessTable* this = xCalloc(1, sizeof(NetBSDProcessTable));
  36. Object_setClass(this, Class(ProcessTable));
  37. ProcessTable* super = (ProcessTable*) this;
  38. ProcessTable_init(super, Class(NetBSDProcess), host, pidMatchList);
  39. return super;
  40. }
  41. void ProcessTable_delete(Object* cast) {
  42. NetBSDProcessTable* this = (NetBSDProcessTable*) cast;
  43. ProcessTable_done(&this->super);
  44. free(this);
  45. }
  46. static void NetBSDProcessTable_updateExe(const struct kinfo_proc2* kproc, Process* proc) {
  47. const int mib[] = { CTL_KERN, KERN_PROC_ARGS, kproc->p_pid, KERN_PROC_PATHNAME };
  48. char buffer[2048];
  49. size_t size = sizeof(buffer);
  50. if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) {
  51. Process_updateExe(proc, NULL);
  52. return;
  53. }
  54. /* Kernel threads return an empty buffer */
  55. if (buffer[0] == '\0') {
  56. Process_updateExe(proc, NULL);
  57. return;
  58. }
  59. Process_updateExe(proc, buffer);
  60. }
  61. static void NetBSDProcessTable_updateCwd(const struct kinfo_proc2* kproc, Process* proc) {
  62. const int mib[] = { CTL_KERN, KERN_PROC_ARGS, kproc->p_pid, KERN_PROC_CWD };
  63. char buffer[2048];
  64. size_t size = sizeof(buffer);
  65. if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) {
  66. free(proc->procCwd);
  67. proc->procCwd = NULL;
  68. return;
  69. }
  70. /* Kernel threads return an empty buffer */
  71. if (buffer[0] == '\0') {
  72. free(proc->procCwd);
  73. proc->procCwd = NULL;
  74. return;
  75. }
  76. free_and_xStrdup(&proc->procCwd, buffer);
  77. }
  78. static void NetBSDProcessTable_updateProcessName(kvm_t* kd, const struct kinfo_proc2* kproc, Process* proc) {
  79. Process_updateComm(proc, kproc->p_comm);
  80. /*
  81. * Like NetBSD's top(1), we try to fall back to the command name
  82. * (argv[0]) if we fail to construct the full command.
  83. */
  84. char** arg = kvm_getargv2(kd, kproc, 500);
  85. if (arg == NULL || *arg == NULL) {
  86. Process_updateCmdline(proc, kproc->p_comm, 0, strlen(kproc->p_comm));
  87. return;
  88. }
  89. size_t len = 0;
  90. for (int i = 0; arg[i] != NULL; i++) {
  91. len += strlen(arg[i]) + 1; /* room for arg and trailing space or NUL */
  92. }
  93. /* don't use xMalloc here - we want to handle huge argv's gracefully */
  94. char* s;
  95. if ((s = malloc(len)) == NULL) {
  96. Process_updateCmdline(proc, kproc->p_comm, 0, strlen(kproc->p_comm));
  97. return;
  98. }
  99. *s = '\0';
  100. int start = 0;
  101. int end = 0;
  102. for (int i = 0; arg[i] != NULL; i++) {
  103. size_t n = strlcat(s, arg[i], len);
  104. if (i == 0) {
  105. end = MINIMUM(n, len - 1);
  106. /* check if cmdline ended earlier, e.g 'kdeinit5: Running...' */
  107. for (int j = end; j > 0; j--) {
  108. if (arg[0][j] == ' ' && arg[0][j - 1] != '\\') {
  109. end = (arg[0][j - 1] == ':') ? (j - 1) : j;
  110. }
  111. }
  112. }
  113. /* the trailing space should get truncated anyway */
  114. strlcat(s, " ", len);
  115. }
  116. Process_updateCmdline(proc, s, start, end);
  117. free(s);
  118. }
  119. /*
  120. * Borrowed with modifications from NetBSD's top(1).
  121. */
  122. static double getpcpu(const NetBSDMachine* nhost, const struct kinfo_proc2* kp) {
  123. if (nhost->fscale == 0)
  124. return 0.0;
  125. return 100.0 * (double)kp->p_pctcpu / nhost->fscale;
  126. }
  127. void ProcessTable_goThroughEntries(ProcessTable* super) {
  128. const Machine* host = super->super.host;
  129. const NetBSDMachine* nhost = (const NetBSDMachine*) host;
  130. const Settings* settings = host->settings;
  131. bool hideKernelThreads = settings->hideKernelThreads;
  132. bool hideUserlandThreads = settings->hideUserlandThreads;
  133. int count = 0;
  134. const struct kinfo_proc2* kprocs = kvm_getproc2(nhost->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count);
  135. for (int i = 0; i < count; i++) {
  136. const struct kinfo_proc2* kproc = &kprocs[i];
  137. bool preExisting = false;
  138. Process* proc = ProcessTable_getProcess(super, kproc->p_pid, &preExisting, NetBSDProcess_new);
  139. proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
  140. if (!preExisting) {
  141. Process_setPid(proc, kproc->p_pid);
  142. Process_setParent(proc, kproc->p_ppid);
  143. Process_setThreadGroup(proc, kproc->p_pid);
  144. proc->tpgid = kproc->p_tpgid;
  145. proc->session = kproc->p_sid;
  146. proc->pgrp = kproc->p__pgid;
  147. proc->isKernelThread = !!(kproc->p_flag & P_SYSTEM);
  148. proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); // eh?
  149. proc->starttime_ctime = kproc->p_ustart_sec;
  150. Process_fillStarttimeBuffer(proc);
  151. ProcessTable_add(super, proc);
  152. proc->tty_nr = kproc->p_tdev;
  153. // KERN_PROC_TTY_NODEV is a negative constant but the type of
  154. // kproc->p_tdev may be unsigned.
  155. const char* name = ((dev_t)~kproc->p_tdev != (dev_t)~(KERN_PROC_TTY_NODEV)) ? devname(kproc->p_tdev, S_IFCHR) : NULL;
  156. if (!name) {
  157. free(proc->tty_name);
  158. proc->tty_name = NULL;
  159. } else {
  160. free_and_xStrdup(&proc->tty_name, name);
  161. }
  162. NetBSDProcessTable_updateExe(kproc, proc);
  163. NetBSDProcessTable_updateProcessName(nhost->kd, kproc, proc);
  164. } else {
  165. if (settings->updateProcessNames) {
  166. NetBSDProcessTable_updateProcessName(nhost->kd, kproc, proc);
  167. }
  168. }
  169. if (settings->ss->flags & PROCESS_FLAG_CWD) {
  170. NetBSDProcessTable_updateCwd(kproc, proc);
  171. }
  172. if (proc->st_uid != kproc->p_uid) {
  173. proc->st_uid = kproc->p_uid;
  174. proc->user = UsersTable_getRef(host->usersTable, proc->st_uid);
  175. }
  176. proc->m_virt = kproc->p_vm_vsize;
  177. proc->m_resident = kproc->p_vm_rssize;
  178. proc->percent_mem = (proc->m_resident * nhost->pageSizeKB) / (double)(host->totalMem) * 100.0;
  179. proc->percent_cpu = CLAMP(getpcpu(nhost, kproc), 0.0, host->activeCPUs * 100.0);
  180. Process_updateCPUFieldWidths(proc->percent_cpu);
  181. proc->nlwp = kproc->p_nlwps;
  182. proc->nice = kproc->p_nice - 20;
  183. proc->time = 100 * (kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000));
  184. proc->priority = kproc->p_priority - PZERO;
  185. proc->processor = kproc->p_cpuid;
  186. proc->minflt = kproc->p_uru_minflt;
  187. proc->majflt = kproc->p_uru_majflt;
  188. int nlwps = 0;
  189. const struct kinfo_lwp* klwps = kvm_getlwps(nhost->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps);
  190. /* TODO: According to the link below, SDYING should be a regarded state */
  191. /* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */
  192. switch (kproc->p_realstat) {
  193. case SIDL: proc->state = IDLE; break;
  194. case SACTIVE:
  195. // We only consider the first LWP with a one of the below states.
  196. for (int j = 0; j < nlwps; j++) {
  197. if (klwps) {
  198. switch (klwps[j].l_stat) {
  199. case LSONPROC: proc->state = RUNNING; break;
  200. case LSRUN: proc->state = RUNNABLE; break;
  201. case LSSLEEP: proc->state = SLEEPING; break;
  202. case LSSTOP: proc->state = STOPPED; break;
  203. default: proc->state = UNKNOWN;
  204. }
  205. if (proc->state != UNKNOWN) {
  206. break;
  207. }
  208. } else {
  209. proc->state = UNKNOWN;
  210. break;
  211. }
  212. }
  213. break;
  214. case SSTOP: proc->state = STOPPED; break;
  215. case SZOMB: proc->state = ZOMBIE; break;
  216. case SDEAD: proc->state = DEFUNCT; break;
  217. default: proc->state = UNKNOWN;
  218. }
  219. if (Process_isKernelThread(proc)) {
  220. super->kernelThreads++;
  221. } else if (Process_isUserlandThread(proc)) {
  222. super->userlandThreads++;
  223. }
  224. super->totalTasks++;
  225. if (proc->state == RUNNING) {
  226. super->runningTasks++;
  227. }
  228. proc->super.updated = true;
  229. }
  230. }