xref: /netbsd/external/mit/libuv/dist/src/unix/darwin.c (revision b29f2fbf)
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 <stdint.h>
26 #include <errno.h>
27 
28 #include <dlfcn.h>
29 #include <mach/mach.h>
30 #include <mach/mach_time.h>
31 #include <mach-o/dyld.h> /* _NSGetExecutablePath */
32 #include <sys/resource.h>
33 #include <sys/sysctl.h>
34 #include <unistd.h>  /* sysconf */
35 
36 #include "darwin-stub.h"
37 
38 static uv_once_t once = UV_ONCE_INIT;
39 static uint64_t (*time_func)(void);
40 static mach_timebase_info_data_t timebase;
41 
42 typedef unsigned char UInt8;
43 
uv__platform_loop_init(uv_loop_t * loop)44 int uv__platform_loop_init(uv_loop_t* loop) {
45   loop->cf_state = NULL;
46 
47   if (uv__kqueue_init(loop))
48     return UV__ERR(errno);
49 
50   return 0;
51 }
52 
53 
uv__platform_loop_delete(uv_loop_t * loop)54 void uv__platform_loop_delete(uv_loop_t* loop) {
55   uv__fsevents_loop_delete(loop);
56 }
57 
58 
uv__hrtime_init_once(void)59 static void uv__hrtime_init_once(void) {
60   if (KERN_SUCCESS != mach_timebase_info(&timebase))
61     abort();
62 
63   time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");
64   if (time_func == NULL)
65     time_func = mach_absolute_time;
66 }
67 
68 
uv__hrtime(uv_clocktype_t type)69 uint64_t uv__hrtime(uv_clocktype_t type) {
70   uv_once(&once, uv__hrtime_init_once);
71   return time_func() * timebase.numer / timebase.denom;
72 }
73 
74 
uv_exepath(char * buffer,size_t * size)75 int uv_exepath(char* buffer, size_t* size) {
76   /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
77   char abspath[PATH_MAX * 2 + 1];
78   char exepath[PATH_MAX + 1];
79   uint32_t exepath_size;
80   size_t abspath_size;
81 
82   if (buffer == NULL || size == NULL || *size == 0)
83     return UV_EINVAL;
84 
85   exepath_size = sizeof(exepath);
86   if (_NSGetExecutablePath(exepath, &exepath_size))
87     return UV_EIO;
88 
89   if (realpath(exepath, abspath) != abspath)
90     return UV__ERR(errno);
91 
92   abspath_size = strlen(abspath);
93   if (abspath_size == 0)
94     return UV_EIO;
95 
96   *size -= 1;
97   if (*size > abspath_size)
98     *size = abspath_size;
99 
100   memcpy(buffer, abspath, *size);
101   buffer[*size] = '\0';
102 
103   return 0;
104 }
105 
106 
uv_get_free_memory(void)107 uint64_t uv_get_free_memory(void) {
108   vm_statistics_data_t info;
109   mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
110 
111   if (host_statistics(mach_host_self(), HOST_VM_INFO,
112                       (host_info_t)&info, &count) != KERN_SUCCESS) {
113     return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
114   }
115 
116   return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE);
117 }
118 
119 
uv_get_total_memory(void)120 uint64_t uv_get_total_memory(void) {
121   uint64_t info;
122   int which[] = {CTL_HW, HW_MEMSIZE};
123   size_t size = sizeof(info);
124 
125   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
126     return UV__ERR(errno);
127 
128   return (uint64_t) info;
129 }
130 
131 
uv_get_constrained_memory(void)132 uint64_t uv_get_constrained_memory(void) {
133   return 0;  /* Memory constraints are unknown. */
134 }
135 
136 
uv_loadavg(double avg[3])137 void uv_loadavg(double avg[3]) {
138   struct loadavg info;
139   size_t size = sizeof(info);
140   int which[] = {CTL_VM, VM_LOADAVG};
141 
142   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return;
143 
144   avg[0] = (double) info.ldavg[0] / info.fscale;
145   avg[1] = (double) info.ldavg[1] / info.fscale;
146   avg[2] = (double) info.ldavg[2] / info.fscale;
147 }
148 
149 
uv_resident_set_memory(size_t * rss)150 int uv_resident_set_memory(size_t* rss) {
151   mach_msg_type_number_t count;
152   task_basic_info_data_t info;
153   kern_return_t err;
154 
155   count = TASK_BASIC_INFO_COUNT;
156   err = task_info(mach_task_self(),
157                   TASK_BASIC_INFO,
158                   (task_info_t) &info,
159                   &count);
160   (void) &err;
161   /* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than
162    * KERN_SUCCESS implies a libuv bug.
163    */
164   assert(err == KERN_SUCCESS);
165   *rss = info.resident_size;
166 
167   return 0;
168 }
169 
170 
uv_uptime(double * uptime)171 int uv_uptime(double* uptime) {
172   time_t now;
173   struct timeval info;
174   size_t size = sizeof(info);
175   static int which[] = {CTL_KERN, KERN_BOOTTIME};
176 
177   if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0))
178     return UV__ERR(errno);
179 
180   now = time(NULL);
181   *uptime = now - info.tv_sec;
182 
183   return 0;
184 }
185 
uv__get_cpu_speed(uint64_t * speed)186 static int uv__get_cpu_speed(uint64_t* speed) {
187   /* IOKit */
188   void (*pIOObjectRelease)(io_object_t);
189   kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*);
190   CFMutableDictionaryRef (*pIOServiceMatching)(const char*);
191   kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t,
192                                                  CFMutableDictionaryRef,
193                                                  io_iterator_t*);
194   io_service_t (*pIOIteratorNext)(io_iterator_t);
195   CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t,
196                                                 CFStringRef,
197                                                 CFAllocatorRef,
198                                                 IOOptionBits);
199 
200   /* CoreFoundation */
201   CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
202                                             const char*,
203                                             CFStringEncoding);
204   CFStringEncoding (*pCFStringGetSystemEncoding)(void);
205   UInt8 *(*pCFDataGetBytePtr)(CFDataRef);
206   CFIndex (*pCFDataGetLength)(CFDataRef);
207   void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*);
208   void (*pCFRelease)(CFTypeRef);
209 
210   void* core_foundation_handle;
211   void* iokit_handle;
212   int err;
213 
214   kern_return_t kr;
215   mach_port_t mach_port;
216   io_iterator_t it;
217   io_object_t service;
218 
219   mach_port = 0;
220 
221   err = UV_ENOENT;
222   core_foundation_handle = dlopen("/System/Library/Frameworks/"
223                                   "CoreFoundation.framework/"
224                                   "CoreFoundation",
225                                   RTLD_LAZY | RTLD_LOCAL);
226   iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/"
227                         "IOKit",
228                         RTLD_LAZY | RTLD_LOCAL);
229 
230   if (core_foundation_handle == NULL || iokit_handle == NULL)
231     goto out;
232 
233 #define V(handle, symbol)                                                     \
234   do {                                                                        \
235     *(void **)(&p ## symbol) = dlsym((handle), #symbol);                      \
236     if (p ## symbol == NULL)                                                  \
237       goto out;                                                               \
238   }                                                                           \
239   while (0)
240   V(iokit_handle, IOMasterPort);
241   V(iokit_handle, IOServiceMatching);
242   V(iokit_handle, IOServiceGetMatchingServices);
243   V(iokit_handle, IOIteratorNext);
244   V(iokit_handle, IOObjectRelease);
245   V(iokit_handle, IORegistryEntryCreateCFProperty);
246   V(core_foundation_handle, CFStringCreateWithCString);
247   V(core_foundation_handle, CFStringGetSystemEncoding);
248   V(core_foundation_handle, CFDataGetBytePtr);
249   V(core_foundation_handle, CFDataGetLength);
250   V(core_foundation_handle, CFDataGetBytes);
251   V(core_foundation_handle, CFRelease);
252 #undef V
253 
254 #define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
255 
256   kr = pIOMasterPort(MACH_PORT_NULL, &mach_port);
257   assert(kr == KERN_SUCCESS);
258   CFMutableDictionaryRef classes_to_match
259       = pIOServiceMatching("IOPlatformDevice");
260   kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it);
261   assert(kr == KERN_SUCCESS);
262   service = pIOIteratorNext(it);
263 
264   CFStringRef device_type_str = S("device_type");
265   CFStringRef clock_frequency_str = S("clock-frequency");
266 
267   while (service != 0) {
268     CFDataRef data;
269     data = pIORegistryEntryCreateCFProperty(service,
270                                             device_type_str,
271                                             NULL,
272                                             0);
273     if (data) {
274       const UInt8* raw = pCFDataGetBytePtr(data);
275       if (strncmp((char*)raw, "cpu", 3) == 0 ||
276           strncmp((char*)raw, "processor", 9) == 0) {
277         CFDataRef freq_ref;
278         freq_ref = pIORegistryEntryCreateCFProperty(service,
279                                                     clock_frequency_str,
280                                                     NULL,
281                                                     0);
282         if (freq_ref) {
283           const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref);
284           CFIndex len = pCFDataGetLength(freq_ref);
285           if (len == 8)
286             memcpy(speed, freq_ref_ptr, 8);
287           else if (len == 4) {
288             uint32_t v;
289             memcpy(&v, freq_ref_ptr, 4);
290             *speed = v;
291           } else {
292             *speed = 0;
293           }
294 
295           pCFRelease(freq_ref);
296           pCFRelease(data);
297           break;
298         }
299       }
300       pCFRelease(data);
301     }
302 
303     service = pIOIteratorNext(it);
304   }
305 
306   pIOObjectRelease(it);
307 
308   err = 0;
309 
310   if (device_type_str != NULL)
311     pCFRelease(device_type_str);
312   if (clock_frequency_str != NULL)
313     pCFRelease(clock_frequency_str);
314 
315 out:
316   if (core_foundation_handle != NULL)
317     dlclose(core_foundation_handle);
318 
319   if (iokit_handle != NULL)
320     dlclose(iokit_handle);
321 
322   mach_port_deallocate(mach_task_self(), mach_port);
323 
324   return err;
325 }
326 
uv_cpu_info(uv_cpu_info_t ** cpu_infos,int * count)327 int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
328   unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
329                multiplier = ((uint64_t)1000L / ticks);
330   char model[512];
331   size_t size;
332   unsigned int i;
333   natural_t numcpus;
334   mach_msg_type_number_t msg_type;
335   processor_cpu_load_info_data_t *info;
336   uv_cpu_info_t* cpu_info;
337   uint64_t cpuspeed;
338   int err;
339 
340   size = sizeof(model);
341   if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&
342       sysctlbyname("hw.model", &model, &size, NULL, 0)) {
343     return UV__ERR(errno);
344   }
345 
346   err = uv__get_cpu_speed(&cpuspeed);
347   if (err < 0)
348     return err;
349 
350   if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
351                           (processor_info_array_t*)&info,
352                           &msg_type) != KERN_SUCCESS) {
353     return UV_EINVAL;  /* FIXME(bnoordhuis) Translate error. */
354   }
355 
356   *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
357   if (!(*cpu_infos)) {
358     vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
359     return UV_ENOMEM;
360   }
361 
362   *count = numcpus;
363 
364   for (i = 0; i < numcpus; i++) {
365     cpu_info = &(*cpu_infos)[i];
366 
367     cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier;
368     cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier;
369     cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier;
370     cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier;
371     cpu_info->cpu_times.irq = 0;
372 
373     cpu_info->model = uv__strdup(model);
374     cpu_info->speed = cpuspeed/1000000;
375   }
376   vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
377 
378   return 0;
379 }
380