10e552da7Schristos /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
20e552da7Schristos * Permission is hereby granted, free of charge, to any person obtaining a copy
30e552da7Schristos * of this software and associated documentation files (the "Software"), to
40e552da7Schristos * deal in the Software without restriction, including without limitation the
50e552da7Schristos * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
60e552da7Schristos * sell copies of the Software, and to permit persons to whom the Software is
70e552da7Schristos * furnished to do so, subject to the following conditions:
80e552da7Schristos *
90e552da7Schristos * The above copyright notice and this permission notice shall be included in
100e552da7Schristos * all copies or substantial portions of the Software.
110e552da7Schristos *
120e552da7Schristos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
130e552da7Schristos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
140e552da7Schristos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
150e552da7Schristos * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
160e552da7Schristos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
170e552da7Schristos * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
180e552da7Schristos * IN THE SOFTWARE.
190e552da7Schristos */
200e552da7Schristos
210e552da7Schristos /* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
220e552da7Schristos * EPOLL* counterparts. We use the POLL* variants in this file because that
230e552da7Schristos * is what libuv uses elsewhere.
240e552da7Schristos */
250e552da7Schristos
260e552da7Schristos #include "uv.h"
270e552da7Schristos #include "internal.h"
280e552da7Schristos
290e552da7Schristos #include <inttypes.h>
300e552da7Schristos #include <stdint.h>
310e552da7Schristos #include <stdio.h>
320e552da7Schristos #include <stdlib.h>
330e552da7Schristos #include <string.h>
340e552da7Schristos #include <assert.h>
350e552da7Schristos #include <errno.h>
360e552da7Schristos
370e552da7Schristos #include <net/if.h>
380e552da7Schristos #include <sys/epoll.h>
390e552da7Schristos #include <sys/param.h>
400e552da7Schristos #include <sys/prctl.h>
410e552da7Schristos #include <sys/sysinfo.h>
420e552da7Schristos #include <unistd.h>
430e552da7Schristos #include <fcntl.h>
440e552da7Schristos #include <time.h>
450e552da7Schristos
460e552da7Schristos #define HAVE_IFADDRS_H 1
470e552da7Schristos
48*5f2f4271Schristos # if defined(__ANDROID_API__) && __ANDROID_API__ < 24
49*5f2f4271Schristos # undef HAVE_IFADDRS_H
50*5f2f4271Schristos #endif
51*5f2f4271Schristos
520e552da7Schristos #ifdef __UCLIBC__
530e552da7Schristos # if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
540e552da7Schristos # undef HAVE_IFADDRS_H
550e552da7Schristos # endif
560e552da7Schristos #endif
570e552da7Schristos
580e552da7Schristos #ifdef HAVE_IFADDRS_H
590e552da7Schristos # include <ifaddrs.h>
600e552da7Schristos # include <sys/socket.h>
610e552da7Schristos # include <net/ethernet.h>
620e552da7Schristos # include <netpacket/packet.h>
630e552da7Schristos #endif /* HAVE_IFADDRS_H */
640e552da7Schristos
650e552da7Schristos /* Available from 2.6.32 onwards. */
660e552da7Schristos #ifndef CLOCK_MONOTONIC_COARSE
670e552da7Schristos # define CLOCK_MONOTONIC_COARSE 6
680e552da7Schristos #endif
690e552da7Schristos
700e552da7Schristos /* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
710e552da7Schristos * include that file because it conflicts with <time.h>. We'll just have to
720e552da7Schristos * define it ourselves.
730e552da7Schristos */
740e552da7Schristos #ifndef CLOCK_BOOTTIME
750e552da7Schristos # define CLOCK_BOOTTIME 7
760e552da7Schristos #endif
770e552da7Schristos
780e552da7Schristos static int read_models(unsigned int numcpus, uv_cpu_info_t* ci);
790e552da7Schristos static int read_times(FILE* statfile_fp,
800e552da7Schristos unsigned int numcpus,
810e552da7Schristos uv_cpu_info_t* ci);
820e552da7Schristos static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
830e552da7Schristos static uint64_t read_cpufreq(unsigned int cpunum);
840e552da7Schristos
uv__platform_loop_init(uv_loop_t * loop)850e552da7Schristos int uv__platform_loop_init(uv_loop_t* loop) {
860e552da7Schristos
870e552da7Schristos loop->inotify_fd = -1;
880e552da7Schristos loop->inotify_watchers = NULL;
890e552da7Schristos
90*5f2f4271Schristos return uv__epoll_init(loop);
910e552da7Schristos }
920e552da7Schristos
930e552da7Schristos
uv__io_fork(uv_loop_t * loop)940e552da7Schristos int uv__io_fork(uv_loop_t* loop) {
950e552da7Schristos int err;
960e552da7Schristos void* old_watchers;
970e552da7Schristos
980e552da7Schristos old_watchers = loop->inotify_watchers;
990e552da7Schristos
1000e552da7Schristos uv__close(loop->backend_fd);
1010e552da7Schristos loop->backend_fd = -1;
1020e552da7Schristos uv__platform_loop_delete(loop);
1030e552da7Schristos
1040e552da7Schristos err = uv__platform_loop_init(loop);
1050e552da7Schristos if (err)
1060e552da7Schristos return err;
1070e552da7Schristos
1080e552da7Schristos return uv__inotify_fork(loop, old_watchers);
1090e552da7Schristos }
1100e552da7Schristos
1110e552da7Schristos
uv__platform_loop_delete(uv_loop_t * loop)1120e552da7Schristos void uv__platform_loop_delete(uv_loop_t* loop) {
1130e552da7Schristos if (loop->inotify_fd == -1) return;
1140e552da7Schristos uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
1150e552da7Schristos uv__close(loop->inotify_fd);
1160e552da7Schristos loop->inotify_fd = -1;
1170e552da7Schristos }
1180e552da7Schristos
1190e552da7Schristos
1200e552da7Schristos
uv__hrtime(uv_clocktype_t type)1210e552da7Schristos uint64_t uv__hrtime(uv_clocktype_t type) {
1220e552da7Schristos static clock_t fast_clock_id = -1;
1230e552da7Schristos struct timespec t;
1240e552da7Schristos clock_t clock_id;
1250e552da7Schristos
1260e552da7Schristos /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
1270e552da7Schristos * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is
1280e552da7Schristos * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
1290e552da7Schristos * decide to make a costly system call.
1300e552da7Schristos */
1310e552da7Schristos /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
1320e552da7Schristos * when it has microsecond granularity or better (unlikely).
1330e552da7Schristos */
134*5f2f4271Schristos clock_id = CLOCK_MONOTONIC;
135*5f2f4271Schristos if (type != UV_CLOCK_FAST)
136*5f2f4271Schristos goto done;
137*5f2f4271Schristos
138*5f2f4271Schristos clock_id = uv__load_relaxed(&fast_clock_id);
139*5f2f4271Schristos if (clock_id != -1)
140*5f2f4271Schristos goto done;
1410e552da7Schristos
1420e552da7Schristos clock_id = CLOCK_MONOTONIC;
143*5f2f4271Schristos if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t))
144*5f2f4271Schristos if (t.tv_nsec <= 1 * 1000 * 1000)
145*5f2f4271Schristos clock_id = CLOCK_MONOTONIC_COARSE;
146*5f2f4271Schristos
147*5f2f4271Schristos uv__store_relaxed(&fast_clock_id, clock_id);
148*5f2f4271Schristos
149*5f2f4271Schristos done:
1500e552da7Schristos
1510e552da7Schristos if (clock_gettime(clock_id, &t))
1520e552da7Schristos return 0; /* Not really possible. */
1530e552da7Schristos
1540e552da7Schristos return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
1550e552da7Schristos }
1560e552da7Schristos
1570e552da7Schristos
uv_resident_set_memory(size_t * rss)1580e552da7Schristos int uv_resident_set_memory(size_t* rss) {
1590e552da7Schristos char buf[1024];
1600e552da7Schristos const char* s;
1610e552da7Schristos ssize_t n;
1620e552da7Schristos long val;
1630e552da7Schristos int fd;
1640e552da7Schristos int i;
1650e552da7Schristos
1660e552da7Schristos do
1670e552da7Schristos fd = open("/proc/self/stat", O_RDONLY);
1680e552da7Schristos while (fd == -1 && errno == EINTR);
1690e552da7Schristos
1700e552da7Schristos if (fd == -1)
1710e552da7Schristos return UV__ERR(errno);
1720e552da7Schristos
1730e552da7Schristos do
1740e552da7Schristos n = read(fd, buf, sizeof(buf) - 1);
1750e552da7Schristos while (n == -1 && errno == EINTR);
1760e552da7Schristos
1770e552da7Schristos uv__close(fd);
1780e552da7Schristos if (n == -1)
1790e552da7Schristos return UV__ERR(errno);
1800e552da7Schristos buf[n] = '\0';
1810e552da7Schristos
1820e552da7Schristos s = strchr(buf, ' ');
1830e552da7Schristos if (s == NULL)
1840e552da7Schristos goto err;
1850e552da7Schristos
1860e552da7Schristos s += 1;
1870e552da7Schristos if (*s != '(')
1880e552da7Schristos goto err;
1890e552da7Schristos
1900e552da7Schristos s = strchr(s, ')');
1910e552da7Schristos if (s == NULL)
1920e552da7Schristos goto err;
1930e552da7Schristos
1940e552da7Schristos for (i = 1; i <= 22; i++) {
1950e552da7Schristos s = strchr(s + 1, ' ');
1960e552da7Schristos if (s == NULL)
1970e552da7Schristos goto err;
1980e552da7Schristos }
1990e552da7Schristos
2000e552da7Schristos errno = 0;
2010e552da7Schristos val = strtol(s, NULL, 10);
2020e552da7Schristos if (errno != 0)
2030e552da7Schristos goto err;
2040e552da7Schristos if (val < 0)
2050e552da7Schristos goto err;
2060e552da7Schristos
2070e552da7Schristos *rss = val * getpagesize();
2080e552da7Schristos return 0;
2090e552da7Schristos
2100e552da7Schristos err:
2110e552da7Schristos return UV_EINVAL;
2120e552da7Schristos }
2130e552da7Schristos
uv_uptime(double * uptime)2140e552da7Schristos int uv_uptime(double* uptime) {
2150e552da7Schristos static volatile int no_clock_boottime;
216*5f2f4271Schristos char buf[128];
2170e552da7Schristos struct timespec now;
2180e552da7Schristos int r;
2190e552da7Schristos
220*5f2f4271Schristos /* Try /proc/uptime first, then fallback to clock_gettime(). */
221*5f2f4271Schristos
222*5f2f4271Schristos if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf)))
223*5f2f4271Schristos if (1 == sscanf(buf, "%lf", uptime))
224*5f2f4271Schristos return 0;
225*5f2f4271Schristos
2260e552da7Schristos /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available
2270e552da7Schristos * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system
2280e552da7Schristos * is suspended.
2290e552da7Schristos */
2300e552da7Schristos if (no_clock_boottime) {
231*5f2f4271Schristos retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now);
2320e552da7Schristos }
2330e552da7Schristos else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) {
2340e552da7Schristos no_clock_boottime = 1;
235*5f2f4271Schristos goto retry_clock_gettime;
2360e552da7Schristos }
2370e552da7Schristos
2380e552da7Schristos if (r)
2390e552da7Schristos return UV__ERR(errno);
2400e552da7Schristos
2410e552da7Schristos *uptime = now.tv_sec;
2420e552da7Schristos return 0;
2430e552da7Schristos }
2440e552da7Schristos
2450e552da7Schristos
uv__cpu_num(FILE * statfile_fp,unsigned int * numcpus)2460e552da7Schristos static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) {
2470e552da7Schristos unsigned int num;
2480e552da7Schristos char buf[1024];
2490e552da7Schristos
2500e552da7Schristos if (!fgets(buf, sizeof(buf), statfile_fp))
2510e552da7Schristos return UV_EIO;
2520e552da7Schristos
2530e552da7Schristos num = 0;
2540e552da7Schristos while (fgets(buf, sizeof(buf), statfile_fp)) {
2550e552da7Schristos if (strncmp(buf, "cpu", 3))
2560e552da7Schristos break;
2570e552da7Schristos num++;
2580e552da7Schristos }
2590e552da7Schristos
2600e552da7Schristos if (num == 0)
2610e552da7Schristos return UV_EIO;
2620e552da7Schristos
2630e552da7Schristos *numcpus = num;
2640e552da7Schristos return 0;
2650e552da7Schristos }
2660e552da7Schristos
2670e552da7Schristos
uv_cpu_info(uv_cpu_info_t ** cpu_infos,int * count)2680e552da7Schristos int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
2690e552da7Schristos unsigned int numcpus;
2700e552da7Schristos uv_cpu_info_t* ci;
2710e552da7Schristos int err;
2720e552da7Schristos FILE* statfile_fp;
2730e552da7Schristos
2740e552da7Schristos *cpu_infos = NULL;
2750e552da7Schristos *count = 0;
2760e552da7Schristos
2770e552da7Schristos statfile_fp = uv__open_file("/proc/stat");
2780e552da7Schristos if (statfile_fp == NULL)
2790e552da7Schristos return UV__ERR(errno);
2800e552da7Schristos
2810e552da7Schristos err = uv__cpu_num(statfile_fp, &numcpus);
2820e552da7Schristos if (err < 0)
2830e552da7Schristos goto out;
2840e552da7Schristos
2850e552da7Schristos err = UV_ENOMEM;
2860e552da7Schristos ci = uv__calloc(numcpus, sizeof(*ci));
2870e552da7Schristos if (ci == NULL)
2880e552da7Schristos goto out;
2890e552da7Schristos
2900e552da7Schristos err = read_models(numcpus, ci);
2910e552da7Schristos if (err == 0)
2920e552da7Schristos err = read_times(statfile_fp, numcpus, ci);
2930e552da7Schristos
2940e552da7Schristos if (err) {
2950e552da7Schristos uv_free_cpu_info(ci, numcpus);
2960e552da7Schristos goto out;
2970e552da7Schristos }
2980e552da7Schristos
2990e552da7Schristos /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo.
3000e552da7Schristos * We don't check for errors here. Worst case, the field is left zero.
3010e552da7Schristos */
3020e552da7Schristos if (ci[0].speed == 0)
3030e552da7Schristos read_speeds(numcpus, ci);
3040e552da7Schristos
3050e552da7Schristos *cpu_infos = ci;
3060e552da7Schristos *count = numcpus;
3070e552da7Schristos err = 0;
3080e552da7Schristos
3090e552da7Schristos out:
3100e552da7Schristos
3110e552da7Schristos if (fclose(statfile_fp))
3120e552da7Schristos if (errno != EINTR && errno != EINPROGRESS)
3130e552da7Schristos abort();
3140e552da7Schristos
3150e552da7Schristos return err;
3160e552da7Schristos }
3170e552da7Schristos
3180e552da7Schristos
read_speeds(unsigned int numcpus,uv_cpu_info_t * ci)3190e552da7Schristos static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
3200e552da7Schristos unsigned int num;
3210e552da7Schristos
3220e552da7Schristos for (num = 0; num < numcpus; num++)
3230e552da7Schristos ci[num].speed = read_cpufreq(num) / 1000;
3240e552da7Schristos }
3250e552da7Schristos
3260e552da7Schristos
327*5f2f4271Schristos /* Also reads the CPU frequency on ppc and x86. The other architectures only
328*5f2f4271Schristos * have a BogoMIPS field, which may not be very accurate.
3290e552da7Schristos *
3300e552da7Schristos * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup.
3310e552da7Schristos */
read_models(unsigned int numcpus,uv_cpu_info_t * ci)3320e552da7Schristos static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
333*5f2f4271Schristos #if defined(__PPC__)
334*5f2f4271Schristos static const char model_marker[] = "cpu\t\t: ";
335*5f2f4271Schristos static const char speed_marker[] = "clock\t\t: ";
336*5f2f4271Schristos #else
3370e552da7Schristos static const char model_marker[] = "model name\t: ";
3380e552da7Schristos static const char speed_marker[] = "cpu MHz\t\t: ";
339*5f2f4271Schristos #endif
3400e552da7Schristos const char* inferred_model;
3410e552da7Schristos unsigned int model_idx;
3420e552da7Schristos unsigned int speed_idx;
343*5f2f4271Schristos unsigned int part_idx;
3440e552da7Schristos char buf[1024];
3450e552da7Schristos char* model;
3460e552da7Schristos FILE* fp;
347*5f2f4271Schristos int model_id;
3480e552da7Schristos
3490e552da7Schristos /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */
3500e552da7Schristos (void) &model_marker;
3510e552da7Schristos (void) &speed_marker;
3520e552da7Schristos (void) &speed_idx;
353*5f2f4271Schristos (void) &part_idx;
3540e552da7Schristos (void) &model;
3550e552da7Schristos (void) &buf;
3560e552da7Schristos (void) &fp;
357*5f2f4271Schristos (void) &model_id;
3580e552da7Schristos
3590e552da7Schristos model_idx = 0;
3600e552da7Schristos speed_idx = 0;
361*5f2f4271Schristos part_idx = 0;
3620e552da7Schristos
3630e552da7Schristos #if defined(__arm__) || \
3640e552da7Schristos defined(__i386__) || \
3650e552da7Schristos defined(__mips__) || \
366*5f2f4271Schristos defined(__aarch64__) || \
367*5f2f4271Schristos defined(__PPC__) || \
3680e552da7Schristos defined(__x86_64__)
3690e552da7Schristos fp = uv__open_file("/proc/cpuinfo");
3700e552da7Schristos if (fp == NULL)
3710e552da7Schristos return UV__ERR(errno);
3720e552da7Schristos
3730e552da7Schristos while (fgets(buf, sizeof(buf), fp)) {
3740e552da7Schristos if (model_idx < numcpus) {
3750e552da7Schristos if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
3760e552da7Schristos model = buf + sizeof(model_marker) - 1;
3770e552da7Schristos model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */
3780e552da7Schristos if (model == NULL) {
3790e552da7Schristos fclose(fp);
3800e552da7Schristos return UV_ENOMEM;
3810e552da7Schristos }
3820e552da7Schristos ci[model_idx++].model = model;
3830e552da7Schristos continue;
3840e552da7Schristos }
3850e552da7Schristos }
386*5f2f4271Schristos #if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
3870e552da7Schristos if (model_idx < numcpus) {
3880e552da7Schristos #if defined(__arm__)
3890e552da7Schristos /* Fallback for pre-3.8 kernels. */
3900e552da7Schristos static const char model_marker[] = "Processor\t: ";
391*5f2f4271Schristos #elif defined(__aarch64__)
392*5f2f4271Schristos static const char part_marker[] = "CPU part\t: ";
393*5f2f4271Schristos
394*5f2f4271Schristos /* Adapted from: https://github.com/karelzak/util-linux */
395*5f2f4271Schristos struct vendor_part {
396*5f2f4271Schristos const int id;
397*5f2f4271Schristos const char* name;
398*5f2f4271Schristos };
399*5f2f4271Schristos
400*5f2f4271Schristos static const struct vendor_part arm_chips[] = {
401*5f2f4271Schristos { 0x811, "ARM810" },
402*5f2f4271Schristos { 0x920, "ARM920" },
403*5f2f4271Schristos { 0x922, "ARM922" },
404*5f2f4271Schristos { 0x926, "ARM926" },
405*5f2f4271Schristos { 0x940, "ARM940" },
406*5f2f4271Schristos { 0x946, "ARM946" },
407*5f2f4271Schristos { 0x966, "ARM966" },
408*5f2f4271Schristos { 0xa20, "ARM1020" },
409*5f2f4271Schristos { 0xa22, "ARM1022" },
410*5f2f4271Schristos { 0xa26, "ARM1026" },
411*5f2f4271Schristos { 0xb02, "ARM11 MPCore" },
412*5f2f4271Schristos { 0xb36, "ARM1136" },
413*5f2f4271Schristos { 0xb56, "ARM1156" },
414*5f2f4271Schristos { 0xb76, "ARM1176" },
415*5f2f4271Schristos { 0xc05, "Cortex-A5" },
416*5f2f4271Schristos { 0xc07, "Cortex-A7" },
417*5f2f4271Schristos { 0xc08, "Cortex-A8" },
418*5f2f4271Schristos { 0xc09, "Cortex-A9" },
419*5f2f4271Schristos { 0xc0d, "Cortex-A17" }, /* Originally A12 */
420*5f2f4271Schristos { 0xc0f, "Cortex-A15" },
421*5f2f4271Schristos { 0xc0e, "Cortex-A17" },
422*5f2f4271Schristos { 0xc14, "Cortex-R4" },
423*5f2f4271Schristos { 0xc15, "Cortex-R5" },
424*5f2f4271Schristos { 0xc17, "Cortex-R7" },
425*5f2f4271Schristos { 0xc18, "Cortex-R8" },
426*5f2f4271Schristos { 0xc20, "Cortex-M0" },
427*5f2f4271Schristos { 0xc21, "Cortex-M1" },
428*5f2f4271Schristos { 0xc23, "Cortex-M3" },
429*5f2f4271Schristos { 0xc24, "Cortex-M4" },
430*5f2f4271Schristos { 0xc27, "Cortex-M7" },
431*5f2f4271Schristos { 0xc60, "Cortex-M0+" },
432*5f2f4271Schristos { 0xd01, "Cortex-A32" },
433*5f2f4271Schristos { 0xd03, "Cortex-A53" },
434*5f2f4271Schristos { 0xd04, "Cortex-A35" },
435*5f2f4271Schristos { 0xd05, "Cortex-A55" },
436*5f2f4271Schristos { 0xd06, "Cortex-A65" },
437*5f2f4271Schristos { 0xd07, "Cortex-A57" },
438*5f2f4271Schristos { 0xd08, "Cortex-A72" },
439*5f2f4271Schristos { 0xd09, "Cortex-A73" },
440*5f2f4271Schristos { 0xd0a, "Cortex-A75" },
441*5f2f4271Schristos { 0xd0b, "Cortex-A76" },
442*5f2f4271Schristos { 0xd0c, "Neoverse-N1" },
443*5f2f4271Schristos { 0xd0d, "Cortex-A77" },
444*5f2f4271Schristos { 0xd0e, "Cortex-A76AE" },
445*5f2f4271Schristos { 0xd13, "Cortex-R52" },
446*5f2f4271Schristos { 0xd20, "Cortex-M23" },
447*5f2f4271Schristos { 0xd21, "Cortex-M33" },
448*5f2f4271Schristos { 0xd41, "Cortex-A78" },
449*5f2f4271Schristos { 0xd42, "Cortex-A78AE" },
450*5f2f4271Schristos { 0xd4a, "Neoverse-E1" },
451*5f2f4271Schristos { 0xd4b, "Cortex-A78C" },
452*5f2f4271Schristos };
453*5f2f4271Schristos
454*5f2f4271Schristos if (strncmp(buf, part_marker, sizeof(part_marker) - 1) == 0) {
455*5f2f4271Schristos model = buf + sizeof(part_marker) - 1;
456*5f2f4271Schristos
457*5f2f4271Schristos errno = 0;
458*5f2f4271Schristos model_id = strtol(model, NULL, 16);
459*5f2f4271Schristos if ((errno != 0) || model_id < 0) {
460*5f2f4271Schristos fclose(fp);
461*5f2f4271Schristos return UV_EINVAL;
462*5f2f4271Schristos }
463*5f2f4271Schristos
464*5f2f4271Schristos for (part_idx = 0; part_idx < ARRAY_SIZE(arm_chips); part_idx++) {
465*5f2f4271Schristos if (model_id == arm_chips[part_idx].id) {
466*5f2f4271Schristos model = uv__strdup(arm_chips[part_idx].name);
467*5f2f4271Schristos if (model == NULL) {
468*5f2f4271Schristos fclose(fp);
469*5f2f4271Schristos return UV_ENOMEM;
470*5f2f4271Schristos }
471*5f2f4271Schristos ci[model_idx++].model = model;
472*5f2f4271Schristos break;
473*5f2f4271Schristos }
474*5f2f4271Schristos }
475*5f2f4271Schristos }
4760e552da7Schristos #else /* defined(__mips__) */
4770e552da7Schristos static const char model_marker[] = "cpu model\t\t: ";
4780e552da7Schristos #endif
4790e552da7Schristos if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
4800e552da7Schristos model = buf + sizeof(model_marker) - 1;
4810e552da7Schristos model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */
4820e552da7Schristos if (model == NULL) {
4830e552da7Schristos fclose(fp);
4840e552da7Schristos return UV_ENOMEM;
4850e552da7Schristos }
4860e552da7Schristos ci[model_idx++].model = model;
4870e552da7Schristos continue;
4880e552da7Schristos }
4890e552da7Schristos }
490*5f2f4271Schristos #else /* !__arm__ && !__mips__ && !__aarch64__ */
4910e552da7Schristos if (speed_idx < numcpus) {
4920e552da7Schristos if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) {
4930e552da7Schristos ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1);
4940e552da7Schristos continue;
4950e552da7Schristos }
4960e552da7Schristos }
497*5f2f4271Schristos #endif /* __arm__ || __mips__ || __aarch64__ */
4980e552da7Schristos }
4990e552da7Schristos
5000e552da7Schristos fclose(fp);
501*5f2f4271Schristos #endif /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ || __aarch__ */
5020e552da7Schristos
5030e552da7Schristos /* Now we want to make sure that all the models contain *something* because
5040e552da7Schristos * it's not safe to leave them as null. Copy the last entry unless there
5050e552da7Schristos * isn't one, in that case we simply put "unknown" into everything.
5060e552da7Schristos */
5070e552da7Schristos inferred_model = "unknown";
5080e552da7Schristos if (model_idx > 0)
5090e552da7Schristos inferred_model = ci[model_idx - 1].model;
5100e552da7Schristos
5110e552da7Schristos while (model_idx < numcpus) {
5120e552da7Schristos model = uv__strndup(inferred_model, strlen(inferred_model));
5130e552da7Schristos if (model == NULL)
5140e552da7Schristos return UV_ENOMEM;
5150e552da7Schristos ci[model_idx++].model = model;
5160e552da7Schristos }
5170e552da7Schristos
5180e552da7Schristos return 0;
5190e552da7Schristos }
5200e552da7Schristos
5210e552da7Schristos
read_times(FILE * statfile_fp,unsigned int numcpus,uv_cpu_info_t * ci)5220e552da7Schristos static int read_times(FILE* statfile_fp,
5230e552da7Schristos unsigned int numcpus,
5240e552da7Schristos uv_cpu_info_t* ci) {
5250e552da7Schristos struct uv_cpu_times_s ts;
5260e552da7Schristos unsigned int ticks;
5270e552da7Schristos unsigned int multiplier;
5280e552da7Schristos uint64_t user;
5290e552da7Schristos uint64_t nice;
5300e552da7Schristos uint64_t sys;
5310e552da7Schristos uint64_t idle;
5320e552da7Schristos uint64_t dummy;
5330e552da7Schristos uint64_t irq;
5340e552da7Schristos uint64_t num;
5350e552da7Schristos uint64_t len;
5360e552da7Schristos char buf[1024];
5370e552da7Schristos
5380e552da7Schristos ticks = (unsigned int)sysconf(_SC_CLK_TCK);
5390e552da7Schristos assert(ticks != (unsigned int) -1);
5400e552da7Schristos assert(ticks != 0);
541*5f2f4271Schristos multiplier = ((uint64_t)1000L / ticks);
5420e552da7Schristos
5430e552da7Schristos rewind(statfile_fp);
5440e552da7Schristos
5450e552da7Schristos if (!fgets(buf, sizeof(buf), statfile_fp))
5460e552da7Schristos abort();
5470e552da7Schristos
5480e552da7Schristos num = 0;
5490e552da7Schristos
5500e552da7Schristos while (fgets(buf, sizeof(buf), statfile_fp)) {
5510e552da7Schristos if (num >= numcpus)
5520e552da7Schristos break;
5530e552da7Schristos
5540e552da7Schristos if (strncmp(buf, "cpu", 3))
5550e552da7Schristos break;
5560e552da7Schristos
5570e552da7Schristos /* skip "cpu<num> " marker */
5580e552da7Schristos {
5590e552da7Schristos unsigned int n;
5600e552da7Schristos int r = sscanf(buf, "cpu%u ", &n);
5610e552da7Schristos assert(r == 1);
5620e552da7Schristos (void) r; /* silence build warning */
5630e552da7Schristos for (len = sizeof("cpu0"); n /= 10; len++);
5640e552da7Schristos }
5650e552da7Schristos
5660e552da7Schristos /* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
5670e552da7Schristos * guest, guest_nice but we're only interested in the first four + irq.
5680e552da7Schristos *
5690e552da7Schristos * Don't use %*s to skip fields or %ll to read straight into the uint64_t
5700e552da7Schristos * fields, they're not allowed in C89 mode.
5710e552da7Schristos */
5720e552da7Schristos if (6 != sscanf(buf + len,
5730e552da7Schristos "%" PRIu64 " %" PRIu64 " %" PRIu64
5740e552da7Schristos "%" PRIu64 " %" PRIu64 " %" PRIu64,
5750e552da7Schristos &user,
5760e552da7Schristos &nice,
5770e552da7Schristos &sys,
5780e552da7Schristos &idle,
5790e552da7Schristos &dummy,
5800e552da7Schristos &irq))
5810e552da7Schristos abort();
5820e552da7Schristos
5830e552da7Schristos ts.user = user * multiplier;
5840e552da7Schristos ts.nice = nice * multiplier;
5850e552da7Schristos ts.sys = sys * multiplier;
5860e552da7Schristos ts.idle = idle * multiplier;
5870e552da7Schristos ts.irq = irq * multiplier;
5880e552da7Schristos ci[num++].cpu_times = ts;
5890e552da7Schristos }
5900e552da7Schristos assert(num == numcpus);
5910e552da7Schristos
5920e552da7Schristos return 0;
5930e552da7Schristos }
5940e552da7Schristos
5950e552da7Schristos
read_cpufreq(unsigned int cpunum)5960e552da7Schristos static uint64_t read_cpufreq(unsigned int cpunum) {
5970e552da7Schristos uint64_t val;
5980e552da7Schristos char buf[1024];
5990e552da7Schristos FILE* fp;
6000e552da7Schristos
6010e552da7Schristos snprintf(buf,
6020e552da7Schristos sizeof(buf),
6030e552da7Schristos "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
6040e552da7Schristos cpunum);
6050e552da7Schristos
6060e552da7Schristos fp = uv__open_file(buf);
6070e552da7Schristos if (fp == NULL)
6080e552da7Schristos return 0;
6090e552da7Schristos
6100e552da7Schristos if (fscanf(fp, "%" PRIu64, &val) != 1)
6110e552da7Schristos val = 0;
6120e552da7Schristos
6130e552da7Schristos fclose(fp);
6140e552da7Schristos
6150e552da7Schristos return val;
6160e552da7Schristos }
6170e552da7Schristos
6180e552da7Schristos
619*5f2f4271Schristos #ifdef HAVE_IFADDRS_H
uv__ifaddr_exclude(struct ifaddrs * ent,int exclude_type)6200e552da7Schristos static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
6210e552da7Schristos if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
6220e552da7Schristos return 1;
6230e552da7Schristos if (ent->ifa_addr == NULL)
6240e552da7Schristos return 1;
6250e552da7Schristos /*
6260e552da7Schristos * On Linux getifaddrs returns information related to the raw underlying
6270e552da7Schristos * devices. We're not interested in this information yet.
6280e552da7Schristos */
6290e552da7Schristos if (ent->ifa_addr->sa_family == PF_PACKET)
6300e552da7Schristos return exclude_type;
6310e552da7Schristos return !exclude_type;
6320e552da7Schristos }
633*5f2f4271Schristos #endif
6340e552da7Schristos
uv_interface_addresses(uv_interface_address_t ** addresses,int * count)6350e552da7Schristos int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
6360e552da7Schristos #ifndef HAVE_IFADDRS_H
6370e552da7Schristos *count = 0;
6380e552da7Schristos *addresses = NULL;
6390e552da7Schristos return UV_ENOSYS;
6400e552da7Schristos #else
6410e552da7Schristos struct ifaddrs *addrs, *ent;
6420e552da7Schristos uv_interface_address_t* address;
6430e552da7Schristos int i;
6440e552da7Schristos struct sockaddr_ll *sll;
6450e552da7Schristos
6460e552da7Schristos *count = 0;
6470e552da7Schristos *addresses = NULL;
6480e552da7Schristos
6490e552da7Schristos if (getifaddrs(&addrs))
6500e552da7Schristos return UV__ERR(errno);
6510e552da7Schristos
6520e552da7Schristos /* Count the number of interfaces */
6530e552da7Schristos for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
6540e552da7Schristos if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
6550e552da7Schristos continue;
6560e552da7Schristos
6570e552da7Schristos (*count)++;
6580e552da7Schristos }
6590e552da7Schristos
6600e552da7Schristos if (*count == 0) {
6610e552da7Schristos freeifaddrs(addrs);
6620e552da7Schristos return 0;
6630e552da7Schristos }
6640e552da7Schristos
6650e552da7Schristos /* Make sure the memory is initiallized to zero using calloc() */
6660e552da7Schristos *addresses = uv__calloc(*count, sizeof(**addresses));
6670e552da7Schristos if (!(*addresses)) {
6680e552da7Schristos freeifaddrs(addrs);
6690e552da7Schristos return UV_ENOMEM;
6700e552da7Schristos }
6710e552da7Schristos
6720e552da7Schristos address = *addresses;
6730e552da7Schristos
6740e552da7Schristos for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
6750e552da7Schristos if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
6760e552da7Schristos continue;
6770e552da7Schristos
6780e552da7Schristos address->name = uv__strdup(ent->ifa_name);
6790e552da7Schristos
6800e552da7Schristos if (ent->ifa_addr->sa_family == AF_INET6) {
6810e552da7Schristos address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
6820e552da7Schristos } else {
6830e552da7Schristos address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
6840e552da7Schristos }
6850e552da7Schristos
6860e552da7Schristos if (ent->ifa_netmask->sa_family == AF_INET6) {
6870e552da7Schristos address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
6880e552da7Schristos } else {
6890e552da7Schristos address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
6900e552da7Schristos }
6910e552da7Schristos
6920e552da7Schristos address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
6930e552da7Schristos
6940e552da7Schristos address++;
6950e552da7Schristos }
6960e552da7Schristos
6970e552da7Schristos /* Fill in physical addresses for each interface */
6980e552da7Schristos for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
6990e552da7Schristos if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
7000e552da7Schristos continue;
7010e552da7Schristos
7020e552da7Schristos address = *addresses;
7030e552da7Schristos
7040e552da7Schristos for (i = 0; i < (*count); i++) {
7050e552da7Schristos size_t namelen = strlen(ent->ifa_name);
7060e552da7Schristos /* Alias interface share the same physical address */
7070e552da7Schristos if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
7080e552da7Schristos (address->name[namelen] == 0 || address->name[namelen] == ':')) {
7090e552da7Schristos sll = (struct sockaddr_ll*)ent->ifa_addr;
7100e552da7Schristos memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
7110e552da7Schristos }
7120e552da7Schristos address++;
7130e552da7Schristos }
7140e552da7Schristos }
7150e552da7Schristos
7160e552da7Schristos freeifaddrs(addrs);
7170e552da7Schristos
7180e552da7Schristos return 0;
7190e552da7Schristos #endif
7200e552da7Schristos }
7210e552da7Schristos
7220e552da7Schristos
uv_free_interface_addresses(uv_interface_address_t * addresses,int count)7230e552da7Schristos void uv_free_interface_addresses(uv_interface_address_t* addresses,
7240e552da7Schristos int count) {
7250e552da7Schristos int i;
7260e552da7Schristos
7270e552da7Schristos for (i = 0; i < count; i++) {
7280e552da7Schristos uv__free(addresses[i].name);
7290e552da7Schristos }
7300e552da7Schristos
7310e552da7Schristos uv__free(addresses);
7320e552da7Schristos }
7330e552da7Schristos
7340e552da7Schristos
uv__set_process_title(const char * title)7350e552da7Schristos void uv__set_process_title(const char* title) {
7360e552da7Schristos #if defined(PR_SET_NAME)
7370e552da7Schristos prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */
7380e552da7Schristos #endif
7390e552da7Schristos }
7400e552da7Schristos
7410e552da7Schristos
uv__read_proc_meminfo(const char * what)7420e552da7Schristos static uint64_t uv__read_proc_meminfo(const char* what) {
7430e552da7Schristos uint64_t rc;
7440e552da7Schristos char* p;
7450e552da7Schristos char buf[4096]; /* Large enough to hold all of /proc/meminfo. */
7460e552da7Schristos
747*5f2f4271Schristos if (uv__slurp("/proc/meminfo", buf, sizeof(buf)))
7480e552da7Schristos return 0;
7490e552da7Schristos
7500e552da7Schristos p = strstr(buf, what);
7510e552da7Schristos
7520e552da7Schristos if (p == NULL)
753*5f2f4271Schristos return 0;
7540e552da7Schristos
7550e552da7Schristos p += strlen(what);
7560e552da7Schristos
757*5f2f4271Schristos rc = 0;
758*5f2f4271Schristos sscanf(p, "%" PRIu64 " kB", &rc);
7590e552da7Schristos
760*5f2f4271Schristos return rc * 1024;
7610e552da7Schristos }
7620e552da7Schristos
7630e552da7Schristos
uv_get_free_memory(void)7640e552da7Schristos uint64_t uv_get_free_memory(void) {
7650e552da7Schristos struct sysinfo info;
7660e552da7Schristos uint64_t rc;
7670e552da7Schristos
768*5f2f4271Schristos rc = uv__read_proc_meminfo("MemAvailable:");
7690e552da7Schristos
7700e552da7Schristos if (rc != 0)
7710e552da7Schristos return rc;
7720e552da7Schristos
7730e552da7Schristos if (0 == sysinfo(&info))
7740e552da7Schristos return (uint64_t) info.freeram * info.mem_unit;
7750e552da7Schristos
7760e552da7Schristos return 0;
7770e552da7Schristos }
7780e552da7Schristos
7790e552da7Schristos
uv_get_total_memory(void)7800e552da7Schristos uint64_t uv_get_total_memory(void) {
7810e552da7Schristos struct sysinfo info;
7820e552da7Schristos uint64_t rc;
7830e552da7Schristos
7840e552da7Schristos rc = uv__read_proc_meminfo("MemTotal:");
7850e552da7Schristos
7860e552da7Schristos if (rc != 0)
7870e552da7Schristos return rc;
7880e552da7Schristos
7890e552da7Schristos if (0 == sysinfo(&info))
7900e552da7Schristos return (uint64_t) info.totalram * info.mem_unit;
7910e552da7Schristos
7920e552da7Schristos return 0;
7930e552da7Schristos }
7940e552da7Schristos
7950e552da7Schristos
uv__read_cgroups_uint64(const char * cgroup,const char * param)7960e552da7Schristos static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) {
7970e552da7Schristos char filename[256];
7980e552da7Schristos char buf[32]; /* Large enough to hold an encoded uint64_t. */
799*5f2f4271Schristos uint64_t rc;
8000e552da7Schristos
8010e552da7Schristos rc = 0;
802*5f2f4271Schristos snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param);
803*5f2f4271Schristos if (0 == uv__slurp(filename, buf, sizeof(buf)))
8040e552da7Schristos sscanf(buf, "%" PRIu64, &rc);
8050e552da7Schristos
8060e552da7Schristos return rc;
8070e552da7Schristos }
8080e552da7Schristos
8090e552da7Schristos
uv_get_constrained_memory(void)8100e552da7Schristos uint64_t uv_get_constrained_memory(void) {
8110e552da7Schristos /*
8120e552da7Schristos * This might return 0 if there was a problem getting the memory limit from
8130e552da7Schristos * cgroups. This is OK because a return value of 0 signifies that the memory
8140e552da7Schristos * limit is unknown.
8150e552da7Schristos */
8160e552da7Schristos return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes");
8170e552da7Schristos }
818*5f2f4271Schristos
819*5f2f4271Schristos
uv_loadavg(double avg[3])820*5f2f4271Schristos void uv_loadavg(double avg[3]) {
821*5f2f4271Schristos struct sysinfo info;
822*5f2f4271Schristos char buf[128]; /* Large enough to hold all of /proc/loadavg. */
823*5f2f4271Schristos
824*5f2f4271Schristos if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf)))
825*5f2f4271Schristos if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]))
826*5f2f4271Schristos return;
827*5f2f4271Schristos
828*5f2f4271Schristos if (sysinfo(&info) < 0)
829*5f2f4271Schristos return;
830*5f2f4271Schristos
831*5f2f4271Schristos avg[0] = (double) info.loads[0] / 65536.0;
832*5f2f4271Schristos avg[1] = (double) info.loads[1] / 65536.0;
833*5f2f4271Schristos avg[2] = (double) info.loads[2] / 65536.0;
834*5f2f4271Schristos }
835