PCPMachine.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. htop - PCPProcessTable.c
  3. (C) 2014 Hisham H. Muhammad
  4. (C) 2020-2023 htop dev team
  5. (C) 2020-2023 Red Hat, Inc.
  6. Released under the GNU GPLv2+, see the COPYING file
  7. in the source distribution for its full text.
  8. */
  9. #include "config.h" // IWYU pragma: keep
  10. #include "pcp/PCPMachine.h"
  11. #include <assert.h>
  12. #include <limits.h>
  13. #include <math.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <sys/time.h>
  17. #include "Machine.h"
  18. #include "Macros.h"
  19. #include "Object.h"
  20. #include "Platform.h"
  21. #include "Settings.h"
  22. #include "XUtils.h"
  23. #include "pcp/Metric.h"
  24. #include "pcp/PCPProcess.h"
  25. static void PCPMachine_updateCPUcount(PCPMachine* this) {
  26. Machine* super = &this->super;
  27. super->activeCPUs = Metric_instanceCount(PCP_PERCPU_SYSTEM);
  28. unsigned int cpus = Platform_getMaxCPU();
  29. if (cpus == super->existingCPUs)
  30. return;
  31. if (cpus == 0)
  32. cpus = super->activeCPUs;
  33. if (cpus <= 1)
  34. cpus = super->activeCPUs = 1;
  35. super->existingCPUs = cpus;
  36. free(this->percpu);
  37. free(this->values);
  38. this->percpu = xCalloc(cpus, sizeof(pmAtomValue*));
  39. for (unsigned int i = 0; i < cpus; i++)
  40. this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
  41. this->values = xCalloc(cpus, sizeof(pmAtomValue));
  42. }
  43. static void PCPMachine_updateMemoryInfo(Machine* host) {
  44. unsigned long long int freeMem = 0;
  45. unsigned long long int swapFreeMem = 0;
  46. unsigned long long int sreclaimableMem = 0;
  47. host->totalMem = host->usedMem = host->cachedMem = 0;
  48. host->usedSwap = host->totalSwap = host->sharedMem = 0;
  49. pmAtomValue value;
  50. if (Metric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL)
  51. host->totalMem = value.ull;
  52. if (Metric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL)
  53. freeMem = value.ull;
  54. if (Metric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL)
  55. host->buffersMem = value.ull;
  56. if (Metric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL)
  57. sreclaimableMem = value.ull;
  58. if (Metric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL)
  59. host->sharedMem = value.ull;
  60. if (Metric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL)
  61. host->cachedMem = value.ull + sreclaimableMem - host->sharedMem;
  62. const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem;
  63. host->usedMem = (host->totalMem >= usedDiff) ?
  64. host->totalMem - usedDiff : host->totalMem - freeMem;
  65. if (Metric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL)
  66. host->availableMem = MINIMUM(value.ull, host->totalMem);
  67. else
  68. host->availableMem = freeMem;
  69. if (Metric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL)
  70. swapFreeMem = value.ull;
  71. if (Metric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL)
  72. host->totalSwap = value.ull;
  73. if (Metric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL)
  74. host->cachedSwap = value.ull;
  75. host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap;
  76. }
  77. /* make copies of previously sampled values to avoid overwrite */
  78. static inline void PCPMachine_backupCPUTime(pmAtomValue* values) {
  79. /* the PERIOD fields (must) mirror the TIME fields */
  80. for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) {
  81. values[metric + CPU_TOTAL_PERIOD] = values[metric];
  82. }
  83. }
  84. static inline void PCPMachine_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) {
  85. pmAtomValue* value;
  86. /* new value for period */
  87. value = &values[previous];
  88. if (latest->ull > value->ull)
  89. value->ull = latest->ull - value->ull;
  90. else
  91. value->ull = 0;
  92. /* new value for time */
  93. value = &values[previous - CPU_TOTAL_PERIOD];
  94. value->ull = latest->ull;
  95. }
  96. /* using copied sampled values and new values, calculate derivations */
  97. static void PCPMachine_deriveCPUTime(pmAtomValue* values) {
  98. pmAtomValue* usertime = &values[CPU_USER_TIME];
  99. pmAtomValue* guesttime = &values[CPU_GUEST_TIME];
  100. usertime->ull -= guesttime->ull;
  101. pmAtomValue* nicetime = &values[CPU_NICE_TIME];
  102. pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME];
  103. nicetime->ull -= guestnicetime->ull;
  104. pmAtomValue* idletime = &values[CPU_IDLE_TIME];
  105. pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME];
  106. pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME];
  107. idlealltime->ull = idletime->ull + iowaittime->ull;
  108. pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME];
  109. pmAtomValue* irqtime = &values[CPU_IRQ_TIME];
  110. pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME];
  111. pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME];
  112. systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull;
  113. pmAtomValue* virtalltime = &values[CPU_GUEST_TIME];
  114. virtalltime->ull = guesttime->ull + guestnicetime->ull;
  115. pmAtomValue* stealtime = &values[CPU_STEAL_TIME];
  116. pmAtomValue* totaltime = &values[CPU_TOTAL_TIME];
  117. totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull +
  118. idlealltime->ull + stealtime->ull + virtalltime->ull;
  119. PCPMachine_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime);
  120. PCPMachine_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime);
  121. PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime);
  122. PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime);
  123. PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime);
  124. PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime);
  125. PCPMachine_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime);
  126. PCPMachine_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime);
  127. PCPMachine_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime);
  128. PCPMachine_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime);
  129. PCPMachine_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime);
  130. PCPMachine_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime);
  131. }
  132. static void PCPMachine_updateAllCPUTime(PCPMachine* this, Metric metric, CPUMetric cpumetric)
  133. {
  134. pmAtomValue* value = &this->cpu[cpumetric];
  135. if (Metric_values(metric, value, 1, PM_TYPE_U64) == NULL)
  136. memset(value, 0, sizeof(pmAtomValue));
  137. }
  138. static void PCPMachine_updatePerCPUTime(PCPMachine* this, Metric metric, CPUMetric cpumetric)
  139. {
  140. int cpus = this->super.existingCPUs;
  141. if (Metric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL)
  142. memset(this->values, 0, cpus * sizeof(pmAtomValue));
  143. for (int i = 0; i < cpus; i++)
  144. this->percpu[i][cpumetric].ull = this->values[i].ull;
  145. }
  146. static void PCPMachine_updatePerCPUReal(PCPMachine* this, Metric metric, CPUMetric cpumetric)
  147. {
  148. int cpus = this->super.existingCPUs;
  149. if (Metric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL)
  150. memset(this->values, 0, cpus * sizeof(pmAtomValue));
  151. for (int i = 0; i < cpus; i++)
  152. this->percpu[i][cpumetric].d = this->values[i].d;
  153. }
  154. static inline void PCPMachine_scanZswapInfo(PCPMachine* this) {
  155. pmAtomValue value;
  156. memset(&this->zswap, 0, sizeof(ZswapStats));
  157. if (Metric_values(PCP_MEM_ZSWAP, &value, 1, PM_TYPE_U64))
  158. this->zswap.usedZswapComp = value.ull;
  159. if (Metric_values(PCP_MEM_ZSWAPPED, &value, 1, PM_TYPE_U64))
  160. this->zswap.usedZswapOrig = value.ull;
  161. }
  162. static inline void PCPMachine_scanZfsArcstats(PCPMachine* this) {
  163. unsigned long long int dbufSize = 0;
  164. unsigned long long int dnodeSize = 0;
  165. unsigned long long int bonusSize = 0;
  166. pmAtomValue value;
  167. memset(&this->zfs, 0, sizeof(ZfsArcStats));
  168. if (Metric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
  169. this->zfs.anon = value.ull / ONE_K;
  170. if (Metric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64))
  171. this->zfs.min = value.ull / ONE_K;
  172. if (Metric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
  173. this->zfs.max = value.ull / ONE_K;
  174. if (Metric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
  175. bonusSize = value.ull / ONE_K;
  176. if (Metric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64))
  177. dbufSize = value.ull / ONE_K;
  178. if (Metric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64))
  179. dnodeSize = value.ull / ONE_K;
  180. if (Metric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
  181. this->zfs.compressed = value.ull / ONE_K;
  182. if (Metric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
  183. this->zfs.uncompressed = value.ull / ONE_K;
  184. if (Metric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64))
  185. this->zfs.header = value.ull / ONE_K;
  186. if (Metric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64))
  187. this->zfs.MFU = value.ull / ONE_K;
  188. if (Metric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64))
  189. this->zfs.MRU = value.ull / ONE_K;
  190. if (Metric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64))
  191. this->zfs.size = value.ull / ONE_K;
  192. this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K;
  193. this->zfs.enabled = (this->zfs.size > 0);
  194. this->zfs.isCompressed = (this->zfs.compressed > 0);
  195. }
  196. static void PCPMachine_scan(PCPMachine* this) {
  197. Machine* super = &this->super;
  198. PCPMachine_updateMemoryInfo(super);
  199. PCPMachine_updateCPUcount(this);
  200. PCPMachine_backupCPUTime(this->cpu);
  201. PCPMachine_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME);
  202. PCPMachine_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME);
  203. PCPMachine_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME);
  204. PCPMachine_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME);
  205. PCPMachine_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME);
  206. PCPMachine_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME);
  207. PCPMachine_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
  208. PCPMachine_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME);
  209. PCPMachine_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME);
  210. PCPMachine_deriveCPUTime(this->cpu);
  211. for (unsigned int i = 0; i < super->existingCPUs; i++)
  212. PCPMachine_backupCPUTime(this->percpu[i]);
  213. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME);
  214. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME);
  215. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME);
  216. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME);
  217. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME);
  218. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME);
  219. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
  220. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME);
  221. PCPMachine_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME);
  222. for (unsigned int i = 0; i < super->existingCPUs; i++)
  223. PCPMachine_deriveCPUTime(this->percpu[i]);
  224. if (super->settings->showCPUFrequency)
  225. PCPMachine_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY);
  226. PCPMachine_scanZfsArcstats(this);
  227. PCPMachine_scanZswapInfo(this);
  228. }
  229. void Machine_scan(Machine* super) {
  230. PCPMachine* host = (PCPMachine*) super;
  231. const Settings* settings = super->settings;
  232. uint32_t flags = settings->ss->flags;
  233. bool flagged;
  234. for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++)
  235. Metric_enable(metric, true);
  236. flagged = settings->showCPUFrequency;
  237. Metric_enable(PCP_HINV_CPUCLOCK, flagged);
  238. flagged = flags & PROCESS_FLAG_LINUX_CGROUP;
  239. Metric_enable(PCP_PROC_CGROUPS, flagged);
  240. flagged = flags & PROCESS_FLAG_LINUX_OOM;
  241. Metric_enable(PCP_PROC_OOMSCORE, flagged);
  242. flagged = flags & PROCESS_FLAG_LINUX_CTXT;
  243. Metric_enable(PCP_PROC_VCTXSW, flagged);
  244. Metric_enable(PCP_PROC_NVCTXSW, flagged);
  245. flagged = flags & PROCESS_FLAG_LINUX_SECATTR;
  246. Metric_enable(PCP_PROC_LABELS, flagged);
  247. flagged = flags & PROCESS_FLAG_LINUX_AUTOGROUP;
  248. Metric_enable(PCP_PROC_AUTOGROUP_ID, flagged);
  249. Metric_enable(PCP_PROC_AUTOGROUP_NICE, flagged);
  250. /* Sample smaps metrics on every second pass to improve performance */
  251. host->smaps_flag = !!host->smaps_flag;
  252. Metric_enable(PCP_PROC_SMAPS_PSS, host->smaps_flag);
  253. Metric_enable(PCP_PROC_SMAPS_SWAP, host->smaps_flag);
  254. Metric_enable(PCP_PROC_SMAPS_SWAPPSS, host->smaps_flag);
  255. struct timeval timestamp;
  256. if (Metric_fetch(&timestamp) != true)
  257. return;
  258. double sample = host->timestamp;
  259. host->timestamp = pmtimevalToReal(&timestamp);
  260. host->period = (host->timestamp - sample) * 100;
  261. PCPMachine_scan(host);
  262. }
  263. Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
  264. PCPMachine* this = xCalloc(1, sizeof(PCPMachine));
  265. Machine* super = &this->super;
  266. Machine_init(super, usersTable, userId);
  267. struct timeval timestamp;
  268. gettimeofday(&timestamp, NULL);
  269. this->timestamp = pmtimevalToReal(&timestamp);
  270. this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
  271. PCPMachine_updateCPUcount(this);
  272. Platform_updateTables(super);
  273. return super;
  274. }
  275. void Machine_delete(Machine* super) {
  276. PCPMachine* this = (PCPMachine*) super;
  277. Machine_done(super);
  278. free(this->values);
  279. for (unsigned int i = 0; i < super->existingCPUs; i++)
  280. free(this->percpu[i]);
  281. free(this->percpu);
  282. free(this->cpu);
  283. free(this);
  284. }
  285. bool Machine_isCPUonline(const Machine* host, unsigned int id) {
  286. assert(id < host->existingCPUs);
  287. (void) host;
  288. pmAtomValue value;
  289. if (Metric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32))
  290. return true;
  291. return false;
  292. }