LinuxMachine.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. /*
  2. htop - LinuxMachine.c
  3. (C) 2014 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 "config.h" // IWYU pragma: keep
  8. #include "linux/LinuxMachine.h"
  9. #include <assert.h>
  10. #include <dirent.h>
  11. #include <errno.h>
  12. #include <fcntl.h>
  13. #include <limits.h>
  14. #include <math.h>
  15. #include <stdbool.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <strings.h>
  20. #include <unistd.h>
  21. #include <time.h>
  22. #include "Compat.h"
  23. #include "CRT.h"
  24. #include "Macros.h"
  25. #include "ProcessTable.h"
  26. #include "Row.h"
  27. #include "Settings.h"
  28. #include "UsersTable.h"
  29. #include "XUtils.h"
  30. #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
  31. #ifdef HAVE_SENSORS_SENSORS_H
  32. #include "LibSensors.h"
  33. #endif
  34. #ifndef O_PATH
  35. #define O_PATH 010000000 // declare for ancient glibc versions
  36. #endif
  37. /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF
  38. * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
  39. */
  40. static void LinuxMachine_updateCPUcount(LinuxMachine* this) {
  41. unsigned int existing = 0, active = 0;
  42. Machine* super = &this->super;
  43. // Initialize the cpuData array before anything else.
  44. if (!this->cpuData) {
  45. this->cpuData = xCalloc(2, sizeof(CPUData));
  46. this->cpuData[0].online = true; /* average is always "online" */
  47. this->cpuData[1].online = true;
  48. super->activeCPUs = 1;
  49. super->existingCPUs = 1;
  50. }
  51. DIR* dir = opendir("/sys/devices/system/cpu");
  52. if (!dir)
  53. return;
  54. unsigned int currExisting = super->existingCPUs;
  55. const struct dirent* entry;
  56. while ((entry = readdir(dir)) != NULL) {
  57. if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
  58. continue;
  59. if (!String_startsWith(entry->d_name, "cpu"))
  60. continue;
  61. char* endp;
  62. unsigned long int id = strtoul(entry->d_name + 3, &endp, 10);
  63. if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0')
  64. continue;
  65. #ifdef HAVE_OPENAT
  66. int cpuDirFd = openat(xDirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW);
  67. if (cpuDirFd < 0)
  68. continue;
  69. #else
  70. char cpuDirFd[4096];
  71. xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name);
  72. #endif
  73. existing++;
  74. /* readdir() iterates with no specific order */
  75. unsigned int max = MAXIMUM(existing, id + 1);
  76. if (max > currExisting) {
  77. this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData));
  78. this->cpuData[0].online = true; /* average is always "online" */
  79. currExisting = max;
  80. }
  81. char buffer[8];
  82. ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer));
  83. /* If the file "online" does not exist or on failure count as active */
  84. if (res < 1 || buffer[0] != '0') {
  85. active++;
  86. this->cpuData[id + 1].online = true;
  87. } else {
  88. this->cpuData[id + 1].online = false;
  89. }
  90. Compat_openatArgClose(cpuDirFd);
  91. }
  92. closedir(dir);
  93. // return if no CPU is found
  94. if (existing < 1)
  95. return;
  96. #ifdef HAVE_SENSORS_SENSORS_H
  97. /* When started with offline CPUs, libsensors does not monitor those,
  98. * even when they become online. */
  99. if (super->existingCPUs != 0 && (active > super->activeCPUs || currExisting > super->existingCPUs))
  100. LibSensors_reload();
  101. #endif
  102. super->activeCPUs = active;
  103. assert(existing == currExisting);
  104. super->existingCPUs = currExisting;
  105. }
  106. static void LinuxMachine_scanMemoryInfo(LinuxMachine* this) {
  107. Machine* host = &this->super;
  108. memory_t availableMem = 0;
  109. memory_t freeMem = 0;
  110. memory_t totalMem = 0;
  111. memory_t buffersMem = 0;
  112. memory_t cachedMem = 0;
  113. memory_t sharedMem = 0;
  114. memory_t swapTotalMem = 0;
  115. memory_t swapCacheMem = 0;
  116. memory_t swapFreeMem = 0;
  117. memory_t sreclaimableMem = 0;
  118. memory_t zswapCompMem = 0;
  119. memory_t zswapOrigMem = 0;
  120. FILE* file = fopen(PROCMEMINFOFILE, "r");
  121. if (!file)
  122. CRT_fatalError("Cannot open " PROCMEMINFOFILE);
  123. char buffer[128];
  124. while (fgets(buffer, sizeof(buffer), file)) {
  125. #define tryRead(label, variable) \
  126. if (String_startsWith(buffer, label)) { \
  127. memory_t parsed_; \
  128. if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \
  129. (variable) = parsed_; \
  130. } \
  131. break; \
  132. } else (void) 0 /* Require a ";" after the macro use. */
  133. switch (buffer[0]) {
  134. case 'M':
  135. tryRead("MemAvailable:", availableMem);
  136. tryRead("MemFree:", freeMem);
  137. tryRead("MemTotal:", totalMem);
  138. break;
  139. case 'B':
  140. tryRead("Buffers:", buffersMem);
  141. break;
  142. case 'C':
  143. tryRead("Cached:", cachedMem);
  144. break;
  145. case 'S':
  146. switch (buffer[1]) {
  147. case 'h':
  148. tryRead("Shmem:", sharedMem);
  149. break;
  150. case 'w':
  151. tryRead("SwapTotal:", swapTotalMem);
  152. tryRead("SwapCached:", swapCacheMem);
  153. tryRead("SwapFree:", swapFreeMem);
  154. break;
  155. case 'R':
  156. tryRead("SReclaimable:", sreclaimableMem);
  157. break;
  158. }
  159. break;
  160. case 'Z':
  161. tryRead("Zswap:", zswapCompMem);
  162. tryRead("Zswapped:", zswapOrigMem);
  163. break;
  164. }
  165. #undef tryRead
  166. }
  167. fclose(file);
  168. /*
  169. * Compute memory partition like procps(free)
  170. * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c
  171. *
  172. * Adjustments:
  173. * - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/),
  174. * do not show twice by subtracting from Cached and do not subtract twice from used.
  175. */
  176. host->totalMem = totalMem;
  177. host->cachedMem = cachedMem + sreclaimableMem - sharedMem;
  178. host->sharedMem = sharedMem;
  179. const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem;
  180. host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem;
  181. host->buffersMem = buffersMem;
  182. host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem;
  183. host->totalSwap = swapTotalMem;
  184. host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem;
  185. host->cachedSwap = swapCacheMem;
  186. this->zswap.usedZswapComp = zswapCompMem;
  187. this->zswap.usedZswapOrig = zswapOrigMem;
  188. }
  189. static void LinuxMachine_scanHugePages(LinuxMachine* this) {
  190. this->totalHugePageMem = 0;
  191. for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
  192. this->usedHugePageMem[i] = MEMORY_MAX;
  193. }
  194. DIR* dir = opendir("/sys/kernel/mm/hugepages");
  195. if (!dir)
  196. return;
  197. const struct dirent* entry;
  198. while ((entry = readdir(dir)) != NULL) {
  199. const char* name = entry->d_name;
  200. /* Ignore all non-directories */
  201. if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
  202. continue;
  203. if (!String_startsWith(name, "hugepages-"))
  204. continue;
  205. char* endptr;
  206. unsigned long int hugePageSize = strtoul(name + strlen("hugepages-"), &endptr, 10);
  207. if (!endptr || *endptr != 'k')
  208. continue;
  209. char content[64];
  210. char hugePagePath[128];
  211. ssize_t r;
  212. xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/nr_hugepages", name);
  213. r = xReadfile(hugePagePath, content, sizeof(content));
  214. if (r <= 0)
  215. continue;
  216. memory_t total = strtoull(content, NULL, 10);
  217. if (total == 0)
  218. continue;
  219. xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/free_hugepages", name);
  220. r = xReadfile(hugePagePath, content, sizeof(content));
  221. if (r <= 0)
  222. continue;
  223. memory_t free = strtoull(content, NULL, 10);
  224. int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10);
  225. assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT);
  226. this->totalHugePageMem += total * hugePageSize;
  227. this->usedHugePageMem[shift] = (total - free) * hugePageSize;
  228. }
  229. closedir(dir);
  230. }
  231. static void LinuxMachine_scanZramInfo(LinuxMachine* this) {
  232. memory_t totalZram = 0;
  233. memory_t usedZramComp = 0;
  234. memory_t usedZramOrig = 0;
  235. char mm_stat[34];
  236. char disksize[34];
  237. unsigned int i = 0;
  238. for (;;) {
  239. xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i);
  240. xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i);
  241. i++;
  242. FILE* disksize_file = fopen(disksize, "r");
  243. FILE* mm_stat_file = fopen(mm_stat, "r");
  244. if (disksize_file == NULL || mm_stat_file == NULL) {
  245. if (disksize_file) {
  246. fclose(disksize_file);
  247. }
  248. if (mm_stat_file) {
  249. fclose(mm_stat_file);
  250. }
  251. break;
  252. }
  253. memory_t size = 0;
  254. memory_t orig_data_size = 0;
  255. memory_t compr_data_size = 0;
  256. if (1 != fscanf(disksize_file, "%llu\n", &size) ||
  257. 2 != fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) {
  258. fclose(disksize_file);
  259. fclose(mm_stat_file);
  260. break;
  261. }
  262. totalZram += size;
  263. usedZramComp += compr_data_size;
  264. usedZramOrig += orig_data_size;
  265. fclose(disksize_file);
  266. fclose(mm_stat_file);
  267. }
  268. this->zram.totalZram = totalZram / 1024;
  269. this->zram.usedZramComp = usedZramComp / 1024;
  270. this->zram.usedZramOrig = usedZramOrig / 1024;
  271. if (this->zram.usedZramComp > this->zram.usedZramOrig) {
  272. this->zram.usedZramComp = this->zram.usedZramOrig;
  273. }
  274. }
  275. static void LinuxMachine_scanZfsArcstats(LinuxMachine* this) {
  276. memory_t dbufSize = 0;
  277. memory_t dnodeSize = 0;
  278. memory_t bonusSize = 0;
  279. FILE* file = fopen(PROCARCSTATSFILE, "r");
  280. if (file == NULL) {
  281. this->zfs.enabled = 0;
  282. return;
  283. }
  284. char buffer[128];
  285. while (fgets(buffer, 128, file)) {
  286. #define tryRead(label, variable) \
  287. if (String_startsWith(buffer, label)) { \
  288. sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
  289. break; \
  290. } else (void) 0 /* Require a ";" after the macro use. */
  291. #define tryReadFlag(label, variable, flag) \
  292. if (String_startsWith(buffer, label)) { \
  293. (flag) = (1 == sscanf(buffer + strlen(label), " %*2u %32llu", variable)); \
  294. break; \
  295. } else (void) 0 /* Require a ";" after the macro use. */
  296. switch (buffer[0]) {
  297. case 'c':
  298. tryRead("c_min", &this->zfs.min);
  299. tryRead("c_max", &this->zfs.max);
  300. tryReadFlag("compressed_size", &this->zfs.compressed, this->zfs.isCompressed);
  301. break;
  302. case 'u':
  303. tryRead("uncompressed_size", &this->zfs.uncompressed);
  304. break;
  305. case 's':
  306. tryRead("size", &this->zfs.size);
  307. break;
  308. case 'h':
  309. tryRead("hdr_size", &this->zfs.header);
  310. break;
  311. case 'd':
  312. tryRead("dbuf_size", &dbufSize);
  313. tryRead("dnode_size", &dnodeSize);
  314. break;
  315. case 'b':
  316. tryRead("bonus_size", &bonusSize);
  317. break;
  318. case 'a':
  319. tryRead("anon_size", &this->zfs.anon);
  320. break;
  321. case 'm':
  322. tryRead("mfu_size", &this->zfs.MFU);
  323. tryRead("mru_size", &this->zfs.MRU);
  324. break;
  325. }
  326. #undef tryRead
  327. #undef tryReadFlag
  328. }
  329. fclose(file);
  330. this->zfs.enabled = (this->zfs.size > 0 ? 1 : 0);
  331. this->zfs.size /= 1024;
  332. this->zfs.min /= 1024;
  333. this->zfs.max /= 1024;
  334. this->zfs.MFU /= 1024;
  335. this->zfs.MRU /= 1024;
  336. this->zfs.anon /= 1024;
  337. this->zfs.header /= 1024;
  338. this->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024;
  339. if ( this->zfs.isCompressed ) {
  340. this->zfs.compressed /= 1024;
  341. this->zfs.uncompressed /= 1024;
  342. }
  343. }
  344. static void LinuxMachine_scanCPUTime(LinuxMachine* this) {
  345. const Machine* super = &this->super;
  346. LinuxMachine_updateCPUcount(this);
  347. FILE* file = fopen(PROCSTATFILE, "r");
  348. if (!file)
  349. CRT_fatalError("Cannot open " PROCSTATFILE);
  350. // Add an extra phantom thread for a later loop
  351. bool adjCpuIdProcessed[super->existingCPUs+2];
  352. memset(adjCpuIdProcessed, 0, sizeof(adjCpuIdProcessed));
  353. for (unsigned int i = 0; i <= super->existingCPUs; i++) {
  354. char buffer[PROC_LINE_LENGTH + 1];
  355. unsigned long long int usertime, nicetime, systemtime, idletime;
  356. unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0;
  357. const char* ok = fgets(buffer, sizeof(buffer), file);
  358. if (!ok)
  359. break;
  360. // cpu fields are sorted first
  361. if (!String_startsWith(buffer, "cpu"))
  362. break;
  363. // Depending on your kernel version,
  364. // 5, 7, 8 or 9 of these fields will be set.
  365. // The rest will remain at zero.
  366. unsigned int adjCpuId;
  367. if (i == 0) {
  368. (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
  369. adjCpuId = 0;
  370. } else {
  371. unsigned int cpuid;
  372. (void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
  373. adjCpuId = cpuid + 1;
  374. }
  375. if (adjCpuId > super->existingCPUs)
  376. break;
  377. // Guest time is already accounted in usertime
  378. usertime -= guest;
  379. nicetime -= guestnice;
  380. // Fields existing on kernels >= 2.6
  381. // (and RHEL's patched kernel 2.4...)
  382. unsigned long long int idlealltime = idletime + ioWait;
  383. unsigned long long int systemalltime = systemtime + irq + softIrq;
  384. unsigned long long int virtalltime = guest + guestnice;
  385. unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
  386. CPUData* cpuData = &(this->cpuData[adjCpuId]);
  387. // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t()
  388. // used in /proc/stat rounds down numbers, it can lead to a case where the
  389. // integer overflow.
  390. cpuData->userPeriod = saturatingSub(usertime, cpuData->userTime);
  391. cpuData->nicePeriod = saturatingSub(nicetime, cpuData->niceTime);
  392. cpuData->systemPeriod = saturatingSub(systemtime, cpuData->systemTime);
  393. cpuData->systemAllPeriod = saturatingSub(systemalltime, cpuData->systemAllTime);
  394. cpuData->idleAllPeriod = saturatingSub(idlealltime, cpuData->idleAllTime);
  395. cpuData->idlePeriod = saturatingSub(idletime, cpuData->idleTime);
  396. cpuData->ioWaitPeriod = saturatingSub(ioWait, cpuData->ioWaitTime);
  397. cpuData->irqPeriod = saturatingSub(irq, cpuData->irqTime);
  398. cpuData->softIrqPeriod = saturatingSub(softIrq, cpuData->softIrqTime);
  399. cpuData->stealPeriod = saturatingSub(steal, cpuData->stealTime);
  400. cpuData->guestPeriod = saturatingSub(virtalltime, cpuData->guestTime);
  401. cpuData->totalPeriod = saturatingSub(totaltime, cpuData->totalTime);
  402. cpuData->userTime = usertime;
  403. cpuData->niceTime = nicetime;
  404. cpuData->systemTime = systemtime;
  405. cpuData->systemAllTime = systemalltime;
  406. cpuData->idleAllTime = idlealltime;
  407. cpuData->idleTime = idletime;
  408. cpuData->ioWaitTime = ioWait;
  409. cpuData->irqTime = irq;
  410. cpuData->softIrqTime = softIrq;
  411. cpuData->stealTime = steal;
  412. cpuData->guestTime = virtalltime;
  413. cpuData->totalTime = totaltime;
  414. adjCpuIdProcessed[adjCpuId] = true;
  415. }
  416. // Set the extra phantom thread as checked to make sure to mark trailing offline threads correctly in the loop
  417. adjCpuIdProcessed[super->existingCPUs+1] = true;
  418. unsigned int lastAdjCpuIdProcessed = 0;
  419. for (unsigned int i = 0; i <= super->existingCPUs+1; i++) {
  420. if (adjCpuIdProcessed[i]) {
  421. for (unsigned int j = lastAdjCpuIdProcessed+1; j < i; j++) {
  422. // Skipped an ID, but /proc/stat is ordered => threads in between are offline
  423. memset(&(this->cpuData[j]), '\0', sizeof(CPUData));
  424. }
  425. lastAdjCpuIdProcessed = i;
  426. }
  427. }
  428. this->period = (double)this->cpuData[0].totalPeriod / super->activeCPUs;
  429. if (!ferror(file) && !feof(file)) {
  430. char buffer[PROC_LINE_LENGTH + 1];
  431. while (fgets(buffer, sizeof(buffer), file)) {
  432. if (String_startsWith(buffer, "procs_running")) {
  433. this->runningTasks = (unsigned int) strtoul(buffer + strlen("procs_running"), NULL, 10);
  434. break;
  435. }
  436. }
  437. }
  438. fclose(file);
  439. }
  440. static int scanCPUFrequencyFromSysCPUFreq(LinuxMachine* this) {
  441. const Machine* super = &this->super;
  442. int numCPUsWithFrequency = 0;
  443. unsigned long totalFrequency = 0;
  444. /*
  445. * On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow (> 1ms). This delay
  446. * accumulates for every core. For details see issue#471.
  447. * If the read on CPU 0 takes longer than 500us bail out and fall back to reading the
  448. * frequencies from /proc/cpuinfo.
  449. * Once the condition has been met, bail out early for the next couple of scans.
  450. */
  451. static int timeout = 0;
  452. if (timeout > 0) {
  453. timeout--;
  454. return -1;
  455. }
  456. for (unsigned int i = 0; i < super->existingCPUs; ++i) {
  457. if (!Machine_isCPUonline(super, i))
  458. continue;
  459. char pathBuffer[64];
  460. xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
  461. struct timespec start;
  462. if (i == 0)
  463. clock_gettime(CLOCK_MONOTONIC, &start);
  464. FILE* file = fopen(pathBuffer, "r");
  465. if (!file)
  466. return -errno;
  467. unsigned long frequency;
  468. if (fscanf(file, "%lu", &frequency) == 1) {
  469. /* convert kHz to MHz */
  470. frequency = frequency / 1000;
  471. this->cpuData[i + 1].frequency = frequency;
  472. numCPUsWithFrequency++;
  473. totalFrequency += frequency;
  474. }
  475. fclose(file);
  476. if (i == 0) {
  477. struct timespec end;
  478. clock_gettime(CLOCK_MONOTONIC, &end);
  479. const time_t timeTakenUs = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
  480. if (timeTakenUs > 500) {
  481. timeout = 30;
  482. return -1;
  483. }
  484. }
  485. }
  486. if (numCPUsWithFrequency > 0)
  487. this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
  488. return 0;
  489. }
  490. static void scanCPUFrequencyFromCPUinfo(LinuxMachine* this) {
  491. const Machine* super = &this->super;
  492. FILE* file = fopen(PROCCPUINFOFILE, "r");
  493. if (file == NULL)
  494. return;
  495. int numCPUsWithFrequency = 0;
  496. double totalFrequency = 0;
  497. int cpuid = -1;
  498. while (!feof(file)) {
  499. double frequency;
  500. char buffer[PROC_LINE_LENGTH];
  501. if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
  502. break;
  503. if (sscanf(buffer, "processor : %d", &cpuid) == 1) {
  504. continue;
  505. } else if (
  506. (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
  507. (sscanf(buffer, "clock : %lfMHz", &frequency) == 1)
  508. ) {
  509. if (cpuid < 0 || (unsigned int)cpuid > (super->existingCPUs - 1)) {
  510. continue;
  511. }
  512. CPUData* cpuData = &(this->cpuData[cpuid + 1]);
  513. /* do not override sysfs data */
  514. if (!isNonnegative(cpuData->frequency)) {
  515. cpuData->frequency = frequency;
  516. }
  517. numCPUsWithFrequency++;
  518. totalFrequency += frequency;
  519. } else if (buffer[0] == '\n') {
  520. cpuid = -1;
  521. }
  522. }
  523. fclose(file);
  524. if (numCPUsWithFrequency > 0) {
  525. this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency;
  526. }
  527. }
  528. static void LinuxMachine_scanCPUFrequency(LinuxMachine* this) {
  529. const Machine* super = &this->super;
  530. for (unsigned int i = 0; i <= super->existingCPUs; i++)
  531. this->cpuData[i].frequency = NAN;
  532. if (scanCPUFrequencyFromSysCPUFreq(this) == 0)
  533. return;
  534. scanCPUFrequencyFromCPUinfo(this);
  535. }
  536. void Machine_scan(Machine* super) {
  537. LinuxMachine* this = (LinuxMachine*) super;
  538. LinuxMachine_scanMemoryInfo(this);
  539. LinuxMachine_scanHugePages(this);
  540. LinuxMachine_scanZfsArcstats(this);
  541. LinuxMachine_scanZramInfo(this);
  542. LinuxMachine_scanCPUTime(this);
  543. const Settings* settings = super->settings;
  544. if (settings->showCPUFrequency)
  545. LinuxMachine_scanCPUFrequency(this);
  546. #ifdef HAVE_SENSORS_SENSORS_H
  547. if (settings->showCPUTemperature)
  548. LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs);
  549. #endif
  550. }
  551. Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
  552. LinuxMachine* this = xCalloc(1, sizeof(LinuxMachine));
  553. Machine* super = &this->super;
  554. Machine_init(super, usersTable, userId);
  555. // Initialize page size
  556. if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1)
  557. CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
  558. this->pageSizeKB = this->pageSize / ONE_K;
  559. // Initialize clock ticks
  560. if ((this->jiffies = sysconf(_SC_CLK_TCK)) == -1)
  561. CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
  562. // Read btime (the kernel boot time, as number of seconds since the epoch)
  563. FILE* statfile = fopen(PROCSTATFILE, "r");
  564. if (statfile == NULL)
  565. CRT_fatalError("Cannot open " PROCSTATFILE);
  566. this->boottime = -1;
  567. while (true) {
  568. char buffer[PROC_LINE_LENGTH + 1];
  569. if (fgets(buffer, sizeof(buffer), statfile) == NULL)
  570. break;
  571. if (String_startsWith(buffer, "btime ") == false)
  572. continue;
  573. if (sscanf(buffer, "btime %lld\n", &this->boottime) == 1)
  574. break;
  575. CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
  576. }
  577. fclose(statfile);
  578. if (this->boottime == -1)
  579. CRT_fatalError("No btime in " PROCSTATFILE);
  580. // Initialize CPU count
  581. LinuxMachine_updateCPUcount(this);
  582. return super;
  583. }
  584. void Machine_delete(Machine* super) {
  585. LinuxMachine* this = (LinuxMachine*) super;
  586. GPUEngineData* gpuEngineData = this->gpuEngineData;
  587. Machine_done(super);
  588. while (gpuEngineData) {
  589. GPUEngineData* next = gpuEngineData->next;
  590. free(gpuEngineData->key);
  591. free(gpuEngineData);
  592. gpuEngineData = next;
  593. }
  594. free(this->cpuData);
  595. free(this);
  596. }
  597. bool Machine_isCPUonline(const Machine* super, unsigned int id) {
  598. const LinuxMachine* this = (const LinuxMachine*) super;
  599. assert(id < super->existingCPUs);
  600. return this->cpuData[id + 1].online;
  601. }