LinuxMachine.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  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. #ifdef HAVE_SENSORS_SENSORS_H
  529. static void LinuxMachine_fetchCPUTopologyFromCPUinfo(LinuxMachine* this) {
  530. const Machine* super = &this->super;
  531. FILE* file = fopen(PROCCPUINFOFILE, "r");
  532. if (file == NULL)
  533. return;
  534. int cpuid = -1;
  535. int coreid = -1;
  536. int physicalid = -1;
  537. int max_physicalid = -1;
  538. int max_coreid = -1;
  539. while (!feof(file)) {
  540. char *buffer = String_readLine(file);
  541. if (!buffer)
  542. break;
  543. if (buffer[0] == '\0') { /* empty line after each cpu */
  544. if (cpuid >= 0 && (unsigned int)cpuid < super->existingCPUs) {
  545. CPUData* cpuData = &(this->cpuData[cpuid + 1]);
  546. cpuData->coreID = coreid;
  547. cpuData->physicalID = physicalid;
  548. if (coreid > max_coreid)
  549. max_coreid = coreid;
  550. if (physicalid > max_physicalid)
  551. max_physicalid = physicalid;
  552. cpuid = -1;
  553. coreid = -1;
  554. physicalid = -1;
  555. }
  556. } else if (String_startsWith(buffer, "processor")) {
  557. sscanf(buffer, "processor : %d", &cpuid);
  558. } else if (String_startsWith(buffer, "physical id")) {
  559. sscanf(buffer, "physical id : %d", &physicalid);
  560. } else if (String_startsWith(buffer, "core id")) {
  561. sscanf(buffer, "core id : %d", &coreid);
  562. }
  563. free(buffer);
  564. }
  565. this->maxPhysicalID = max_physicalid;
  566. this->maxCoreID = max_coreid;
  567. fclose(file);
  568. }
  569. static void LinuxMachine_assignCCDs(LinuxMachine* this, int ccds) {
  570. /* For AMD k10temp/zenpower, temperatures are provided for CCDs only,
  571. which is an aggregate of multiple cores.
  572. There's no obvious mapping between hwmon sensors and sockets and CCDs.
  573. Assume both are iterated in order.
  574. Hypothesis: Each CCD has same size N = #Cores/#CCD
  575. and is assigned N coreID in sequence.
  576. Also assume all CPUs have same number of CCDs. */
  577. const Machine* super = &this->super;
  578. CPUData *cpus = this->cpuData;
  579. if (ccds == 0) {
  580. for (size_t i = 0; i < super->existingCPUs + 1; i++) {
  581. cpus[i].ccdID = -1;
  582. }
  583. return;
  584. }
  585. int coresPerCCD = super->existingCPUs / ccds;
  586. int ccd = 0;
  587. int nc = coresPerCCD;
  588. for (int p = 0; p <= (int)this->maxPhysicalID; p++) {
  589. for (int c = 0; c <= (int)this->maxCoreID; c++) {
  590. for (size_t i = 1; i <= super->existingCPUs; i++) {
  591. if (cpus[i].physicalID != p || cpus[i].coreID != c)
  592. continue;
  593. cpus[i].ccdID = ccd;
  594. if (--nc <= 0) {
  595. nc = coresPerCCD;
  596. ccd++;
  597. }
  598. }
  599. }
  600. }
  601. }
  602. #endif
  603. static void LinuxMachine_scanCPUFrequency(LinuxMachine* this) {
  604. const Machine* super = &this->super;
  605. for (unsigned int i = 0; i <= super->existingCPUs; i++)
  606. this->cpuData[i].frequency = NAN;
  607. if (scanCPUFrequencyFromSysCPUFreq(this) == 0)
  608. return;
  609. scanCPUFrequencyFromCPUinfo(this);
  610. }
  611. void Machine_scan(Machine* super) {
  612. LinuxMachine* this = (LinuxMachine*) super;
  613. LinuxMachine_scanMemoryInfo(this);
  614. LinuxMachine_scanHugePages(this);
  615. LinuxMachine_scanZfsArcstats(this);
  616. LinuxMachine_scanZramInfo(this);
  617. LinuxMachine_scanCPUTime(this);
  618. const Settings* settings = super->settings;
  619. if (settings->showCPUFrequency
  620. #ifdef HAVE_SENSORS_SENSORS_H
  621. || settings->showCPUTemperature
  622. #endif
  623. )
  624. LinuxMachine_scanCPUFrequency(this);
  625. #ifdef HAVE_SENSORS_SENSORS_H
  626. if (settings->showCPUTemperature)
  627. LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs);
  628. #endif
  629. }
  630. Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
  631. LinuxMachine* this = xCalloc(1, sizeof(LinuxMachine));
  632. Machine* super = &this->super;
  633. Machine_init(super, usersTable, userId);
  634. // Initialize page size
  635. if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1)
  636. CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
  637. this->pageSizeKB = this->pageSize / ONE_K;
  638. // Initialize clock ticks
  639. if ((this->jiffies = sysconf(_SC_CLK_TCK)) == -1)
  640. CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
  641. // Read btime (the kernel boot time, as number of seconds since the epoch)
  642. FILE* statfile = fopen(PROCSTATFILE, "r");
  643. if (statfile == NULL)
  644. CRT_fatalError("Cannot open " PROCSTATFILE);
  645. this->boottime = -1;
  646. while (true) {
  647. char buffer[PROC_LINE_LENGTH + 1];
  648. if (fgets(buffer, sizeof(buffer), statfile) == NULL)
  649. break;
  650. if (String_startsWith(buffer, "btime ") == false)
  651. continue;
  652. if (sscanf(buffer, "btime %lld\n", &this->boottime) == 1)
  653. break;
  654. CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
  655. }
  656. fclose(statfile);
  657. if (this->boottime == -1)
  658. CRT_fatalError("No btime in " PROCSTATFILE);
  659. // Initialize CPU count
  660. LinuxMachine_updateCPUcount(this);
  661. #ifdef HAVE_SENSORS_SENSORS_H
  662. // Fetch CPU topology
  663. LinuxMachine_fetchCPUTopologyFromCPUinfo(this);
  664. int ccds = LibSensors_countCCDs();
  665. LinuxMachine_assignCCDs(this, ccds);
  666. #endif
  667. return super;
  668. }
  669. void Machine_delete(Machine* super) {
  670. LinuxMachine* this = (LinuxMachine*) super;
  671. GPUEngineData* gpuEngineData = this->gpuEngineData;
  672. Machine_done(super);
  673. while (gpuEngineData) {
  674. GPUEngineData* next = gpuEngineData->next;
  675. free(gpuEngineData->key);
  676. free(gpuEngineData);
  677. gpuEngineData = next;
  678. }
  679. free(this->cpuData);
  680. free(this);
  681. }
  682. bool Machine_isCPUonline(const Machine* super, unsigned int id) {
  683. const LinuxMachine* this = (const LinuxMachine*) super;
  684. assert(id < super->existingCPUs);
  685. return this->cpuData[id + 1].online;
  686. }