OpenBSDMachine.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. htop - OpenBSDMachine.c
  3. (C) 2014 Hisham H. Muhammad
  4. (C) 2015 Michael McConville
  5. Released under the GNU GPLv2+, see the COPYING file
  6. in the source distribution for its full text.
  7. */
  8. #include "config.h" // IWYU pragma: keep
  9. #include "openbsd/OpenBSDMachine.h"
  10. #include <kvm.h>
  11. #include <limits.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <sys/mount.h>
  16. #include <sys/param.h>
  17. #include <sys/proc.h>
  18. #include <sys/sched.h>
  19. #include <sys/swap.h>
  20. #include <sys/sysctl.h>
  21. #include <sys/types.h>
  22. #include <uvm/uvmexp.h>
  23. #include "CRT.h"
  24. #include "Macros.h"
  25. #include "Object.h"
  26. #include "Settings.h"
  27. #include "XUtils.h"
  28. static void OpenBSDMachine_updateCPUcount(OpenBSDMachine* this) {
  29. Machine* super = &this->super;
  30. const int nmib[] = { CTL_HW, HW_NCPU };
  31. const int mib[] = { CTL_HW, HW_NCPUONLINE };
  32. int r;
  33. unsigned int value;
  34. size_t size;
  35. bool change = false;
  36. size = sizeof(value);
  37. r = sysctl(mib, 2, &value, &size, NULL, 0);
  38. if (r < 0 || value < 1) {
  39. value = 1;
  40. }
  41. if (value != super->activeCPUs) {
  42. super->activeCPUs = value;
  43. change = true;
  44. }
  45. size = sizeof(value);
  46. r = sysctl(nmib, 2, &value, &size, NULL, 0);
  47. if (r < 0 || value < 1) {
  48. value = super->activeCPUs;
  49. }
  50. if (value != super->existingCPUs) {
  51. this->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData));
  52. super->existingCPUs = value;
  53. change = true;
  54. }
  55. if (change) {
  56. CPUData* dAvg = &this->cpuData[0];
  57. memset(dAvg, '\0', sizeof(CPUData));
  58. dAvg->totalTime = 1;
  59. dAvg->totalPeriod = 1;
  60. dAvg->online = true;
  61. for (unsigned int i = 0; i < super->existingCPUs; i++) {
  62. CPUData* d = &this->cpuData[i + 1];
  63. memset(d, '\0', sizeof(CPUData));
  64. d->totalTime = 1;
  65. d->totalPeriod = 1;
  66. const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i };
  67. struct cpustats cpu_stats;
  68. size = sizeof(cpu_stats);
  69. if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) {
  70. CRT_fatalError("ncmib sysctl call failed");
  71. }
  72. d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE);
  73. }
  74. }
  75. }
  76. Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
  77. const int fmib[] = { CTL_KERN, KERN_FSCALE };
  78. size_t size;
  79. char errbuf[_POSIX2_LINE_MAX];
  80. OpenBSDMachine* this = xCalloc(1, sizeof(OpenBSDMachine));
  81. Machine* super = &this->super;
  82. Machine_init(super, usersTable, userId);
  83. OpenBSDMachine_updateCPUcount(this);
  84. size = sizeof(this->fscale);
  85. if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0 || this->fscale <= 0) {
  86. CRT_fatalError("fscale sysctl call failed");
  87. }
  88. if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1)
  89. CRT_fatalError("pagesize sysconf call failed");
  90. this->pageSizeKB = this->pageSize / ONE_K;
  91. this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
  92. if (this->kd == NULL) {
  93. CRT_fatalError("kvm_openfiles() failed");
  94. }
  95. this->cpuSpeed = -1;
  96. return super;
  97. }
  98. void Machine_delete(Machine* super) {
  99. OpenBSDMachine* this = (OpenBSDMachine*) super;
  100. if (this->kd) {
  101. kvm_close(this->kd);
  102. }
  103. free(this->cpuData);
  104. Machine_done(super);
  105. free(this);
  106. }
  107. static void OpenBSDMachine_scanMemoryInfo(OpenBSDMachine* this) {
  108. Machine* super = &this->super;
  109. const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP };
  110. struct uvmexp uvmexp;
  111. size_t size_uvmexp = sizeof(uvmexp);
  112. if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) {
  113. CRT_fatalError("uvmexp sysctl call failed");
  114. }
  115. super->totalMem = uvmexp.npages * this->pageSizeKB;
  116. super->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * this->pageSizeKB;
  117. // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
  118. const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
  119. struct bcachestats bcstats;
  120. size_t size_bcstats = sizeof(bcstats);
  121. if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) {
  122. CRT_fatalError("cannot get vfs.bcachestat");
  123. }
  124. super->cachedMem = bcstats.numbufpages * this->pageSizeKB;
  125. /*
  126. * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
  127. * All rights reserved.
  128. *
  129. * Taken almost directly from OpenBSD's top(1)
  130. *
  131. * Originally released under a BSD-3 license
  132. * Modified through htop developers applying GPL-2
  133. */
  134. int nswap = swapctl(SWAP_NSWAP, 0, 0);
  135. if (nswap > 0) {
  136. struct swapent* swdev = xMallocArray(nswap, sizeof(struct swapent));
  137. int rnswap = swapctl(SWAP_STATS, swdev, nswap);
  138. /* Total things up */
  139. unsigned long long int total = 0, used = 0;
  140. for (int i = 0; i < rnswap; i++) {
  141. if (swdev[i].se_flags & SWF_ENABLE) {
  142. used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
  143. total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
  144. }
  145. }
  146. super->totalSwap = total;
  147. super->usedSwap = used;
  148. free(swdev);
  149. } else {
  150. super->totalSwap = super->usedSwap = 0;
  151. }
  152. }
  153. static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) {
  154. const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId };
  155. size_t length = sizeof(*times) * CPUSTATES;
  156. if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) {
  157. CRT_fatalError("sysctl kern.cp_time2 failed");
  158. }
  159. }
  160. static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) {
  161. unsigned long long totalTime = 0;
  162. for (int i = 0; i < CPUSTATES; i++) {
  163. totalTime += times[i];
  164. }
  165. unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS];
  166. // XXX Not sure if CP_SPIN should be added to sysAllTime.
  167. // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c
  168. #ifdef CP_SPIN
  169. sysAllTime += times[CP_SPIN];
  170. #endif
  171. cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime);
  172. cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime);
  173. cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime);
  174. cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime);
  175. cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime);
  176. #ifdef CP_SPIN
  177. cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime);
  178. #endif
  179. cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime);
  180. cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime);
  181. cpu->totalTime = totalTime;
  182. cpu->userTime = times[CP_USER];
  183. cpu->niceTime = times[CP_NICE];
  184. cpu->sysTime = times[CP_SYS];
  185. cpu->sysAllTime = sysAllTime;
  186. #ifdef CP_SPIN
  187. cpu->spinTime = times[CP_SPIN];
  188. #endif
  189. cpu->intrTime = times[CP_INTR];
  190. cpu->idleTime = times[CP_IDLE];
  191. }
  192. static void OpenBSDMachine_scanCPUTime(OpenBSDMachine* this) {
  193. Machine* super = &this->super;
  194. u_int64_t kernelTimes[CPUSTATES] = {0};
  195. u_int64_t avg[CPUSTATES] = {0};
  196. for (unsigned int i = 0; i < super->existingCPUs; i++) {
  197. CPUData* cpu = &this->cpuData[i + 1];
  198. if (!cpu->online) {
  199. continue;
  200. }
  201. getKernelCPUTimes(i, kernelTimes);
  202. kernelCPUTimesToHtop(kernelTimes, cpu);
  203. avg[CP_USER] += cpu->userTime;
  204. avg[CP_NICE] += cpu->niceTime;
  205. avg[CP_SYS] += cpu->sysTime;
  206. #ifdef CP_SPIN
  207. avg[CP_SPIN] += cpu->spinTime;
  208. #endif
  209. avg[CP_INTR] += cpu->intrTime;
  210. avg[CP_IDLE] += cpu->idleTime;
  211. }
  212. for (int i = 0; i < CPUSTATES; i++) {
  213. avg[i] /= super->activeCPUs;
  214. }
  215. kernelCPUTimesToHtop(avg, &this->cpuData[0]);
  216. {
  217. const int mib[] = { CTL_HW, HW_CPUSPEED };
  218. int cpuSpeed;
  219. size_t size = sizeof(cpuSpeed);
  220. if (sysctl(mib, 2, &cpuSpeed, &size, NULL, 0) == -1) {
  221. this->cpuSpeed = -1;
  222. } else {
  223. this->cpuSpeed = cpuSpeed;
  224. }
  225. }
  226. }
  227. void Machine_scan(Machine* super) {
  228. OpenBSDMachine* this = (OpenBSDMachine*) super;
  229. OpenBSDMachine_updateCPUcount(this);
  230. OpenBSDMachine_scanMemoryInfo(this);
  231. OpenBSDMachine_scanCPUTime(this);
  232. }
  233. bool Machine_isCPUonline(const Machine* super, unsigned int id) {
  234. assert(id < super->existingCPUs);
  235. const OpenBSDMachine* this = (const OpenBSDMachine*) super;
  236. return this->cpuData[id + 1].online;
  237. }