xref: /netbsd-src/external/mit/libuv/dist/src/unix/freebsd.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  * Permission is hereby granted, free of charge, to any person obtaining a copy
3  * of this software and associated documentation files (the "Software"), to
4  * deal in the Software without restriction, including without limitation the
5  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6  * sell copies of the Software, and to permit persons to whom the Software is
7  * furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18  * IN THE SOFTWARE.
19  */
20 
21 #include "uv.h"
22 #include "internal.h"
23 
24 #include <assert.h>
25 #include <string.h>
26 #include <errno.h>
27 
28 #include <paths.h>
29 #include <sys/user.h>
30 #include <sys/types.h>
31 #include <sys/resource.h>
32 #include <sys/sysctl.h>
33 #include <vm/vm_param.h> /* VM_LOADAVG */
34 #include <time.h>
35 #include <stdlib.h>
36 #include <unistd.h> /* sysconf */
37 #include <fcntl.h>
38 
39 #ifndef CPUSTATES
40 # define CPUSTATES 5U
41 #endif
42 #ifndef CP_USER
43 # define CP_USER 0
44 # define CP_NICE 1
45 # define CP_SYS 2
46 # define CP_IDLE 3
47 # define CP_INTR 4
48 #endif
49 
50 
51 int uv__platform_loop_init(uv_loop_t* loop) {
52   return uv__kqueue_init(loop);
53 }
54 
55 
56 void uv__platform_loop_delete(uv_loop_t* loop) {
57 }
58 
59 
60 #ifdef __DragonFly__
61 int uv_exepath(char* buffer, size_t* size) {
62   char abspath[PATH_MAX * 2 + 1];
63   ssize_t abspath_size;
64 
65   if (buffer == NULL || size == NULL || *size == 0)
66     return UV_EINVAL;
67 
68   abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath));
69   if (abspath_size < 0)
70     return UV__ERR(errno);
71 
72   assert(abspath_size > 0);
73   *size -= 1;
74 
75   if (*size > abspath_size)
76     *size = abspath_size;
77 
78   memcpy(buffer, abspath, *size);
79   buffer[*size] = '\0';
80 
81   return 0;
82 }
83 #else
84 int uv_exepath(char* buffer, size_t* size) {
85   char abspath[PATH_MAX * 2 + 1];
86   int mib[4];
87   size_t abspath_size;
88 
89   if (buffer == NULL || size == NULL || *size == 0)
90     return UV_EINVAL;
91 
92   mib[0] = CTL_KERN;
93   mib[1] = KERN_PROC;
94   mib[2] = KERN_PROC_PATHNAME;
95   mib[3] = -1;
96 
97   abspath_size = sizeof abspath;
98   if (sysctl(mib, ARRAY_SIZE(mib), abspath, &abspath_size, NULL, 0))
99     return UV__ERR(errno);
100 
101   assert(abspath_size > 0);
102   abspath_size -= 1;
103   *size -= 1;
104 
105   if (*size > abspath_size)
106     *size = abspath_size;
107 
108   memcpy(buffer, abspath, *size);
109   buffer[*size] = '\0';
110 
111   return 0;
112 }
113 #endif
114 
115 uint64_t uv_get_free_memory(void) {
116   int freecount;
117   size_t size = sizeof(freecount);
118 
119   if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0))
120     return UV__ERR(errno);
121 
122   return (uint64_t) freecount * sysconf(_SC_PAGESIZE);
123 
124 }
125 
126 
127 uint64_t uv_get_total_memory(void) {
128   unsigned long info;
129   int which[] = {CTL_HW, HW_PHYSMEM};
130 
131   size_t size = sizeof(info);
132 
133   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
134     return UV__ERR(errno);
135 
136   return (uint64_t) info;
137 }
138 
139 
140 uint64_t uv_get_constrained_memory(void) {
141   return 0;  /* Memory constraints are unknown. */
142 }
143 
144 
145 void uv_loadavg(double avg[3]) {
146   struct loadavg info;
147   size_t size = sizeof(info);
148   int which[] = {CTL_VM, VM_LOADAVG};
149 
150   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return;
151 
152   avg[0] = (double) info.ldavg[0] / info.fscale;
153   avg[1] = (double) info.ldavg[1] / info.fscale;
154   avg[2] = (double) info.ldavg[2] / info.fscale;
155 }
156 
157 
158 int uv_resident_set_memory(size_t* rss) {
159   struct kinfo_proc kinfo;
160   size_t page_size;
161   size_t kinfo_size;
162   int mib[4];
163 
164   mib[0] = CTL_KERN;
165   mib[1] = KERN_PROC;
166   mib[2] = KERN_PROC_PID;
167   mib[3] = getpid();
168 
169   kinfo_size = sizeof(kinfo);
170 
171   if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &kinfo_size, NULL, 0))
172     return UV__ERR(errno);
173 
174   page_size = getpagesize();
175 
176 #ifdef __DragonFly__
177   *rss = kinfo.kp_vm_rssize * page_size;
178 #else
179   *rss = kinfo.ki_rssize * page_size;
180 #endif
181 
182   return 0;
183 }
184 
185 
186 int uv_uptime(double* uptime) {
187   int r;
188   struct timespec sp;
189   r = clock_gettime(CLOCK_MONOTONIC, &sp);
190   if (r)
191     return UV__ERR(errno);
192 
193   *uptime = sp.tv_sec;
194   return 0;
195 }
196 
197 
198 int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
199   unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
200                multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus,
201                cur = 0;
202   uv_cpu_info_t* cpu_info;
203   const char* maxcpus_key;
204   const char* cptimes_key;
205   const char* model_key;
206   char model[512];
207   long* cp_times;
208   int numcpus;
209   size_t size;
210   int i;
211 
212 #if defined(__DragonFly__)
213   /* This is not quite correct but DragonFlyBSD doesn't seem to have anything
214    * comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total,
215    * not per CPU). At least this stops uv_cpu_info() from failing completely.
216    */
217   maxcpus_key = "hw.ncpu";
218   cptimes_key = "kern.cp_time";
219 #else
220   maxcpus_key = "kern.smp.maxcpus";
221   cptimes_key = "kern.cp_times";
222 #endif
223 
224 #if defined(__arm__) || defined(__aarch64__)
225   /* The key hw.model and hw.clockrate are not available on FreeBSD ARM. */
226   model_key = "hw.machine";
227   cpuspeed = 0;
228 #else
229   model_key = "hw.model";
230 
231   size = sizeof(cpuspeed);
232   if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0))
233     return -errno;
234 #endif
235 
236   size = sizeof(model);
237   if (sysctlbyname(model_key, &model, &size, NULL, 0))
238     return UV__ERR(errno);
239 
240   size = sizeof(numcpus);
241   if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
242     return UV__ERR(errno);
243 
244   *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
245   if (!(*cpu_infos))
246     return UV_ENOMEM;
247 
248   *count = numcpus;
249 
250   /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of
251    * ncpu.
252    */
253   size = sizeof(maxcpus);
254   if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) {
255     uv__free(*cpu_infos);
256     return UV__ERR(errno);
257   }
258 
259   size = maxcpus * CPUSTATES * sizeof(long);
260 
261   cp_times = uv__malloc(size);
262   if (cp_times == NULL) {
263     uv__free(*cpu_infos);
264     return UV_ENOMEM;
265   }
266 
267   if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) {
268     uv__free(cp_times);
269     uv__free(*cpu_infos);
270     return UV__ERR(errno);
271   }
272 
273   for (i = 0; i < numcpus; i++) {
274     cpu_info = &(*cpu_infos)[i];
275 
276     cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
277     cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
278     cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
279     cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
280     cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
281 
282     cpu_info->model = uv__strdup(model);
283     cpu_info->speed = cpuspeed;
284 
285     cur+=CPUSTATES;
286   }
287 
288   uv__free(cp_times);
289   return 0;
290 }
291 
292 
293 int uv__sendmmsg(int fd,
294                  struct uv__mmsghdr* mmsg,
295                  unsigned int vlen,
296                  unsigned int flags) {
297 #if __FreeBSD__ >= 11
298   return sendmmsg(fd, mmsg, vlen, flags);
299 #else
300   return errno = ENOSYS, -1;
301 #endif
302 }
303 
304 
305 int uv__recvmmsg(int fd,
306                  struct uv__mmsghdr* mmsg,
307                  unsigned int vlen,
308                  unsigned int flags,
309                  struct timespec* timeout) {
310 #if __FreeBSD__ >= 11
311   return recvmmsg(fd, mmsg, vlen, flags, timeout);
312 #else
313   return errno = ENOSYS, -1;
314 #endif
315 }
316